ssh: report the owed write when the worker leaves output queued - #1252
Merged
ejohnstown merged 2 commits intoSep 17, 2026
Merged
Conversation
- wolfSSH_shutdown() sets ret to WS_WANT_WRITE whenever the wolfSSH_worker() call it makes ends with output pending. The sendErr local and the arm that returned wolfSSH_get_error() are gone. - tests/unit.c gains EofDisconnectCb(), which sends a disconnect from the channel EOF dispatch, and two tests driving it: test_ShutdownOwedWriteAfterEofDisconnect() asserts wolfSSH_shutdown() returns WS_WANT_WRITE with output pending and WS_SUCCESS with the buffer drained on the retry; test_ShutdownHardEofDisconnectSurfacesOnRetry() refuses the disconnect's send outright and asserts the retry returns WS_SOCKET_ERROR_E.
…rite - The wolfSSH_worker() send-failure sentence names WS_CHANNEL_CLOSED and WS_FATAL_ERROR as the statuses that keep the return. - Its channelId sentence narrows the rekey case to a WS_REKEYING that displaced WS_SUCCESS or WS_CHAN_RXD. - The wolfSSH_shutdown() block adds that the read for the peer's close reply can leave output queued, which reports WS_WANT_WRITE, and that a send which failed outright there takes the return on the next call.
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
A moderate shutdown path issue remains unresolved for queued disconnect output.
Pull request overview
Updates wolfSSH_shutdown() to report queued teardown writes correctly, with documentation updates and regression tests.
Changes:
- Simplifies pending-output handling.
- Clarifies worker and shutdown status documentation.
- Adds retry and transport-error tests.
File summaries
| File | Summary |
|---|---|
wolfssh/ssh.h |
Documents updated status and shutdown behavior. |
tests/unit.c |
Adds shutdown retry regression coverage. |
src/ssh.c |
Handles owed writes, but WS_CHANNEL_CLOSED teardown can still bypass pending-write handling. |
Review details
Suppressed comments (1)
src/ssh.c:1296
- This still treats queued output as a write want only when the worker returned
WS_SUCCESS. A publicchannelCloseCbcan callwolfSSH_SendDisconnect()while the socket is blocked;DoChannelClose()then removes the channel andwolfSSH_worker()returnsWS_CHANNEL_CLOSEDwith that disconnect still inoutputBuffer. The branch is skipped, and the later empty-channel handling preserves the event, sowolfSSH_shutdown()returnsWS_CHANNEL_CLOSEDand a caller can stop without flushing the owed disconnect. Apply the pending-write precedence to this teardown event too, while preventing the later channel-list assignment from overwritingWS_WANT_WRITE.
ret = WS_WANT_WRITE;
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1252
Scan targets checked: wolfssh-src, wolfssh-bugs
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
ejohnstown
approved these changes
Sep 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
wolfSSH_shutdown()reads for the peer's close reply, then reports what theread left queued:
The
elsecannot do what it says. A hard flush failure inwolfSSH_worker()puts its code in the return, so this block is not entered; a short flush leaves
WS_WANT_WRITE; a successful one drains the buffer and fails the guard. AndDoReceive()overwritesssh->errorwith the dispatch status, so what the armreads is the event, not a send result.
What it can do is leak a per-channel event out of a teardown. The worker's
flush is gated on
!ssh->disconnected, and that flag can be set mid-pass:DoChannelEof()discards its callback's return, so achannelEofCbthat callswolfSSH_SendDisconnect()on a blocked socket leaves the DISCONNECT queuedwith the flag up. The flush is skipped, the pass returns
WS_EOF, andwolfSSH_shutdown()returnsWS_EOF(-1031) for a session that owes a write.No in-tree consumer installs
channelEofCb, so this is a contract fix ratherthan a reported failure.
Fix (
src/ssh.c)ret = WS_WANT_WRITEunconditionally — output is pending by the guard.sendErrand its two vestigial tolerances go with it.WS_WANT_WRITEis already the answer for a disconnected session with our ownDISCONNECT queued:
wolfSSH_shutdown()top flushWS_WANT_WRITE(TestShutdownKeepsFlushWantWrite)wolfSSH_SendDisconnect()WS_WANT_WRITEwolfSSH_accept()WS_FATAL_ERROR/WS_DISCONNECTFlushQueuedOutput()bails only ondisconnected && !disconnectTxd, so onceour DISCONNECT is bundled the flush is still wanted and the retry puts it on
the wire. A genuine transport error is not lost — it takes the return on the
next call.
wolfssh/ssh.hgains three doc corrections:WS_FATAL_ERRORkeeps thewolfSSH_worker()return alongsideWS_CHANNEL_CLOSED; onlyWS_SUCCESSandWS_CHAN_RXDare displaced by a reportedWS_REKEYING; and thewolfSSH_shutdown()block notes the owed write above. The first two describebehaviour already covered by
test_WorkerHardRecvErrorOutranksFlush,test_WorkerReportsExtDataChannelKeyingandtest_WorkerReportsEofChannelKeying.Tests
test_ShutdownOwedWriteAfterEofDisconnectWS_WANT_WRITEwith output pending, thenWS_SUCCESSwith the buffer drained on the retrytest_ShutdownHardEofDisconnectSurfacesOnRetryWS_SOCKET_ERROR_Etakes the return on the retryBoth fail against the unmodified
src/ssh.c, the first returningWS_EOF.Verification
-Werrorconfigs clean.regressandtestsuiteclean;scp/sshclient/get-put/sftpscripts green.unitandregress.