Skip to content

Commit 78633a1

Browse files
ejohnstownphilljj
authored andcommitted
Clear disconnectTxd once the buffer drains
The flag was set in SendDisconnect() and never cleared, so it meant "a disconnect was sent" rather than "a flush is owed". Once ours had gone out, the next teardown call still pushed whatever the internal senders had queued behind it: measured, a CHANNEL_EOF from DoChannelEof() went on the wire after the disconnect. wolfSSH_SendPacket() now clears it. Issue: F-8837
1 parent d05978f commit 78633a1

3 files changed

Lines changed: 72 additions & 1 deletion

File tree

src/internal.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4327,6 +4327,11 @@ int wolfSSH_SendPacket(WOLFSSH* ssh)
43274327

43284328
ssh->outputBuffer.plainSz = 0;
43294329

4330+
/* The buffer is empty, so our disconnect, if one was in it, has gone
4331+
* out and no flush is owed. Leaving this set hands the next teardown
4332+
* call a licence to push whatever gets queued next. */
4333+
ssh->disconnectTxd = 0;
4334+
43304335
WLOG(WS_LOG_DEBUG, "SB: Shrinking output buffer");
43314336
ShrinkBuffer(&ssh->outputBuffer, 0);
43324337
return HighwaterCheck(ssh, WOLFSSH_HWSIDE_TRANSMIT);

tests/regress.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3811,6 +3811,70 @@ static void TestDisconnectOutranksRekey(void)
38113811
}
38123812

38133813

3814+
/* disconnectTxd means "a flush is owed", not "a disconnect was sent". Once
3815+
* ours has gone out, a teardown call must not push whatever the internal
3816+
* senders queued behind it. */
3817+
static void TestDisconnectTxdClearsOnFlush(void)
3818+
{
3819+
WOLFSSH_CTX* ctx;
3820+
WOLFSSH* ssh;
3821+
WOLFSSH_CHANNEL* channel;
3822+
MemIo io;
3823+
byte out[512];
3824+
int ret;
3825+
3826+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
3827+
AssertNotNull(ctx);
3828+
3829+
wolfSSH_SetIORecv(ctx, MemRecv);
3830+
wolfSSH_SetIOSend(ctx, MemSendWantWrite);
3831+
3832+
ssh = wolfSSH_new(ctx);
3833+
AssertNotNull(ssh);
3834+
AddSessionChannel(ssh);
3835+
channel = ssh->channelList;
3836+
ssh->connectState = CONNECT_SERVER_USERAUTH_ACCEPT_DONE;
3837+
3838+
MemIoInit(&io, NULL, 0, out, sizeof(out));
3839+
wolfSSH_SetIOReadCtx(ssh, &io);
3840+
wolfSSH_SetIOWriteCtx(ssh, &io);
3841+
3842+
/* Our disconnect short-sends, so a flush is owed. */
3843+
MemSendWantWriteCount = 1;
3844+
AssertIntEQ(wolfSSH_SendDisconnect(ssh, WOLFSSH_DISCONNECT_BY_APPLICATION),
3845+
WS_WANT_WRITE);
3846+
AssertTrue(ssh->disconnectTxd);
3847+
AssertTrue(wolfSSH_OutputPending(ssh));
3848+
AssertIntEQ(io.outSz, 0);
3849+
3850+
/* The caller's retry loop pumps it out in full, so nothing is owed. */
3851+
AssertIntEQ(wolfSSH_SendPacket(ssh), WS_SUCCESS);
3852+
AssertFalse(wolfSSH_OutputPending(ssh));
3853+
AssertFalse(ssh->disconnectTxd);
3854+
AssertIntEQ(out[LENGTH_SZ + 1], MSGID_DISCONNECT);
3855+
io.outSz = 0;
3856+
3857+
/* An in-flight CHANNEL_EOF draws a reply out of DoChannelEof(); the
3858+
* internal senders are not behind the disconnect gate. It short-sends,
3859+
* so it sits in the output buffer. */
3860+
MemSendWantWriteCount = 1;
3861+
AssertIntEQ(SendChannelEof(ssh, channel->peerChannel), WS_WANT_WRITE);
3862+
AssertTrue(wolfSSH_OutputPending(ssh));
3863+
AssertIntEQ(io.outSz, 0);
3864+
3865+
/* Teardown must leave it there: the disconnect is already gone, so this
3866+
* would be traffic after it. RFC 4253 section 11.1. */
3867+
ret = wolfSSH_shutdown(ssh);
3868+
AssertIntEQ(ret, WS_SUCCESS);
3869+
AssertIntEQ(wolfSSH_get_error(ssh), WS_DISCONNECT);
3870+
AssertIntEQ(io.outSz, 0);
3871+
AssertTrue(wolfSSH_OutputPending(ssh));
3872+
3873+
wolfSSH_free(ssh);
3874+
wolfSSH_CTX_free(ctx);
3875+
}
3876+
3877+
38143878
/* A flush that is itself short owns ssh->error. Callers gate their retry on
38153879
* WS_WANT_WRITE, so the disconnect gate must not overwrite it. */
38163880
static void TestShutdownKeepsFlushWantWrite(void)
@@ -7644,6 +7708,7 @@ int main(int argc, char** argv)
76447708
TestQueuedDisconnectFlushes();
76457709
TestShutdownFlushesQueuedDisconnect();
76467710
TestShutdownKeepsFlushWantWrite();
7711+
TestDisconnectTxdClearsOnFlush();
76477712
TestDisconnectOutranksRekey();
76487713
#if defined(WOLFSSH_TERM) && !defined(NO_FILESYSTEM)
76497714
TestTerminalResizeBlockedAfterDisconnect();

wolfssh/internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,8 @@ struct WOLFSSH {
11101110
* paths pump the worker. */
11111111
byte disconnected;
11121112
/* Set once SendDisconnect() has bundled our own DISCONNECT into the
1113-
* output buffer, so a short send can still be flushed. The flag above
1113+
* output buffer, and cleared once wolfSSH_SendPacket() drains it, so it
1114+
* means "a flush is owed" rather than "one was sent". The flag above
11141115
* cannot stand in for this: it does not say whose disconnect it was,
11151116
* and a peer's leaves only unrelated traffic queued. */
11161117
byte disconnectTxd;

0 commit comments

Comments
 (0)