Skip to content

Commit d05978f

Browse files
ejohnstownphilljj
authored andcommitted
Report the disconnect with no channel to drop
wolfSSH_shutdown() set ssh->error to WS_DISCONNECT only inside the channel branch, so a flush that emptied the buffer with the channel already retired left behind the WS_WANT_WRITE that queued it. echoserver and sftpclient read that error and burn ten wolfSSH_worker() calls on a write that is already done. TestShutdownFlushesWithNoChannel asserts it. Issue: F-8837
1 parent a7fda8f commit d05978f

2 files changed

Lines changed: 10 additions & 4 deletions

File tree

src/ssh.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,13 +1161,16 @@ int wolfSSH_shutdown(WOLFSSH* ssh)
11611161
* and the wait for a close that will not come. RFC 4253 section 11.1. */
11621162
if (channel != NULL && ssh->disconnected) {
11631163
WLOG(WS_LOG_DEBUG, "Session already disconnected, nothing to send");
1164-
/* An unfinished flush owns ssh->error. Callers gate their retry on
1165-
* WS_WANT_WRITE, so overwriting it strands the queued disconnect. */
1166-
if (flushRet == WS_SUCCESS)
1167-
ssh->error = WS_DISCONNECT;
11681164
channel = NULL;
11691165
}
11701166

1167+
/* Report the dead session with or without a channel to drop: callers
1168+
* gate their retry on ssh->error, and the flush above may have just
1169+
* emptied the output buffer they would be retrying for. An unfinished
1170+
* flush owns the error instead, since that retry is still owed. */
1171+
if (ssh != NULL && ssh->disconnected && flushRet == WS_SUCCESS)
1172+
ssh->error = WS_DISCONNECT;
1173+
11711174
/* if channel close was not already sent then send it */
11721175
if (channel != NULL && !channel->closeTxd) {
11731176
if (ret == WS_SUCCESS) {

tests/regress.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3583,6 +3583,9 @@ static void TestShutdownFlushesWithNoChannel(void)
35833583
AssertIntEQ(ret, WS_CHANNEL_CLOSED);
35843584
AssertFalse(wolfSSH_OutputPending(ssh));
35853585
AssertIntEQ(out[LENGTH_SZ + 1], MSGID_DISCONNECT);
3586+
/* The flush finished, so the WS_WANT_WRITE that queued it is stale.
3587+
* Leaving it sends the caller back for a write that is already done. */
3588+
AssertIntEQ(wolfSSH_get_error(ssh), WS_DISCONNECT);
35863589

35873590
wolfSSH_free(ssh);
35883591
wolfSSH_CTX_free(ctx);

0 commit comments

Comments
 (0)