Drop closeTxd on a discarded close

SendChannelClose() latches closeTxd on the same terms as the EOF beside it: on
anything but the send failure that discards the output buffer. It latched
unconditionally, so a closeTxd claiming a close that never left made
wolfSSH_shutdown()'s gate skip the teardown altogether.

- Split from the EOF latch on purpose: that one widens, from success-only to
  bundled, and this one narrows. Read as one change they read wrong.
pull/1218/head
John Safranek 2026-08-27 13:08:01 -07:00 committed by philljj
parent 147b787798
commit 5911c51ef3
2 changed files with 10 additions and 1 deletions

View File

@ -20492,7 +20492,10 @@ int SendChannelClose(WOLFSSH* ssh, word32 peerChannelId)
if (ret == WS_SUCCESS) {
ret = wolfSSH_SendPacket(ssh);
channel->closeTxd = 1;
/* Same terms as SendChannelEof(). A closeTxd for a close that never
* left makes wolfSSH_shutdown() skip the teardown. */
if (ret != WS_SOCKET_ERROR_E || wolfSSH_OutputPending(ssh))
channel->closeTxd = 1;
}
WLOG(WS_LOG_DEBUG, "Leaving SendChannelClose(), ret = %d", ret);

View File

@ -7327,6 +7327,12 @@ static int test_SendChannelEofSendFails(void)
if (ch->eofTxd) { result = -1692; goto done; }
if (ssh->outputBuffer.length != 0) { result = -1693; goto done; }
/* SendChannelClose() has the same shape, and a closeTxd claiming a close
* that never left makes wolfSSH_shutdown() skip the teardown. */
ret = SendChannelClose(ssh, ch->peerChannel);
if (ret != WS_SOCKET_ERROR_E) { result = -1762; goto done; }
if (ch->closeTxd) { result = -1763; goto done; }
/* So the channel is not half-closed, and an EOF can still be built and
* sent once the socket takes bytes again. */
wolfSSH_SetIOSend(ctx, DiscardIoSend);