Repository navigation
Count a blocked write's record when another call flushed it - #12026
yosuke-wolfssl wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
There are no active review issues for the nonblocking write-retry changes.
No open issues 🎉
What changed in this PR
Fixes nonblocking write retries so wolfSSL does not resend application data when another API call has already flushed the queued record.
Changes:
- Credits previously buffered bytes whenever the output buffer is empty.
- Clears pending write credit when reusing a connection or rejecting TLS 1.3 early data.
- Adds regression tests for read-triggered flushing, connection reuse, and early-data acceptance and rejection, including HelloRetryRequest.
| File | Description |
|---|---|
tests/api/test_tls13.h |
Registers the early-data write-retry test. |
tests/api/test_tls13.c |
Tests blocked-write retries across early-data acceptance and rejection. |
tests/api/test_ssl_rw.h |
Registers write-retry and connection-reuse tests. |
tests/api/test_ssl_rw.c |
Tests read-triggered flushing and pending-write cleanup on reuse. |
src/tls13.c |
Clears write credit when early data is rejected. |
src/ssl.c |
Resets write credit in wolfSSL_clear(). |
src/internal.c |
Applies write credit after another call flushes the queued record. |
Review effort: Balanced
|
|
@claude review |
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #12026
Scan targets checked: wolfssl-src, wolfssl-bugs
Not reviewed (no scan target covers them): tests/api/test_ssl_rw.h, tests/api/test_tls13.h
Coverage: 5 of 5 in-scope changed file(s) opened by the reviewer
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Review tier: Max
4de274e to
1c9a983
Compare
- SendData() counts the bytes of a record left by an earlier blocked write whenever outputBuffer is empty, whether this call or another call flushed it. - SendData() groups early data with the ClientHello only when no queued write credit is pending, so a retried write flushes its record and takes the credit. - wolfSSL_clear() resets prevSent and plainSz. - Add test_wolfSSL_write_retry_flushed_by_update_keys, test_wolfSSL_clear_write_pending and test_tls13_early_data_group_write_retry.
- DoTls13EncryptedExtensions() and SendTls13ClientHello() clear prevSent and plainSz where the client sets earlyData to no_early_data on a rejection, so a retried wolfSSL_write() sends all of its data. - Add test_tls13_early_data_write_retry.
…ends - SendData() moves the queued write credit after the deferred KeyUpdate send, RetrySendAlert() and the IsWriteClosed() check, so a WANT_WRITE from any of them returns with prevSent and plainSz still set. - Add test_wolfSSL_write_retry_keyupdate_blocked and test_wolfSSL_write_retry_alert_blocked.
1c9a983 to
677042d
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #12026
Scan targets checked: wolfssl-src, wolfssl-bugs
Not reviewed (no scan target covers them): tests/api/test_ssl_rw.h, tests/api/test_tls13.h
Coverage: 1 of 3 in-scope changed file(s) opened by the reviewer; not opened: tests/api/test_ssl_rw.c, tests/api/test_tls13.c
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Review tier: Lite
Problem
When
wolfSSL_write()returnsWANT_WRITE,SendData()stores the bytes already handled inprevSent/plainSzso the retry counts them instead of sending them again. Three paths sent the data twice:outputBuffer. If another call flushed the queued record first (wolfSSL_update_keys(),wolfSSL_negotiate(),wolfSSL_shutdown()), the retry found the buffer empty, skipped the credit, and encrypted the whole buffer again.WANT_WRITEwhile sending a deferred KeyUpdate response or a pending alert, so the next retry started from byte 0.WOLFSSL_EARLY_DATA_GROUP). The retry of a blocked 0-RTT write skipped its flush to group with the ClientHello again, and appended a second copy of the record.The unused credit also stayed behind.
wolfSSL_clear()did not reset it, so it could carry into the first write on the next connection.Fix (
src/internal.c)SendData()applies the credit wheneveroutputBufferis empty, whether this call or another one flushed it, and only after the deferred KeyUpdate response,RetrySendAlert()andIsWriteClosed()can no longer return.SendData()groups early data with the ClientHello only when no credit is pending, so a retry flushes its queued record.wolfSSL_clear()resetsprevSentandplainSz.DoTls13EncryptedExtensions()andSendTls13ClientHello()(on a HelloRetryRequest) clear the credit where the client marks early data rejected. A blocked write sent as 0-RTT and then dropped by the server is resent in full on retry, not counted as delivered.Tests
Adds 6 tests to pin the behavior.
Verification
macOS, ASan + UBSan,
--enable-all --disable-sharedplus:--enable-asynccrypt --enable-asynccrypt-swssl_rw25 andtls13192 passed, 0 failed--enable-earlydata=grouptls13192 passed, 0 failed-DWOLFSSL_RW_THREADEDssl_rw25 passed, 0 failedsrc/internal.c, theflushed_by_update_keys,keyupdate_blocked,alert_blockedandearly_data_group_write_retrytests fail, as does the existingtest_tls13_early_datain the grouped build.SendTls13ClientHello()reset failstest_tls13_early_data_write_retry.