Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions apps/wolfssh/wolfssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,21 +325,26 @@ static int FlushQueuedSend(WOLFSSH* ssh, wolfSSL_Mutex* lock)
wc_LockMutex(lock);
}
ret = wolfSSH_worker(ssh, NULL);
if (ret == WS_FATAL_ERROR) {
/* the session holds the detail behind a fatal error */
ret = wolfSSH_get_error(ssh);
}
if (lock != NULL) {
wc_UnLockMutex(lock);
}
Comment thread
yosuke-wolfssl marked this conversation as resolved.
} while (ret == WS_WANT_WRITE && WTIME(NULL) < deadline);

/* The queue is out. Whatever the worker made of the peer's end of the
* conversation is for the reader to sort out. A rekey started on the way
* through is the reader's as well, the send itself went out. */
if (ret == WS_WANT_READ || ret == WS_CHAN_RXD || ret == WS_EXTDATA
|| ret == WS_REKEYING) {
ret = WS_SUCCESS;

/* Anything outside the receive's own statuses is a failure. */
if (ret != WS_SUCCESS && ret != WS_WANT_READ && ret != WS_CHAN_RXD
&& ret != WS_EXTDATA && ret != WS_REKEYING) {
break;
}
} while (wolfSSH_get_error(ssh) == WS_WANT_WRITE
&& WTIME(NULL) < deadline);

/* Report only whether the queue went out. The deadline can run out
* with the packet still queued. */
if (ret == WS_SUCCESS || ret == WS_WANT_READ || ret == WS_CHAN_RXD
|| ret == WS_EXTDATA || ret == WS_REKEYING) {
if (wolfSSH_get_error(ssh) == WS_WANT_WRITE)
ret = WS_WANT_WRITE;
else
ret = WS_SUCCESS;
}

return ret;
Expand Down Expand Up @@ -1353,9 +1358,6 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)

if (ret == WS_SUCCESS) {
ret = wolfSSH_worker(ssh, NULL);
if (ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
if (ret == WS_WANT_WRITE) {
/* The close messages are already out, whatever the drain
* still wants to send is a reply to the peer. */
Expand Down
17 changes: 8 additions & 9 deletions apps/wolfsshd/wolfsshd.c
Original file line number Diff line number Diff line change
Expand Up @@ -2618,20 +2618,17 @@ static void* HandleConnection(void* arg)
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Attempting to close down connection");
ret = wolfSSH_shutdown(ssh);

/* peer hung up, stop shutdown */
if (ret == WS_SOCKET_ERROR_E) {
/* peer hung up or the channel is already gone, stop shutdown */
if (ret == WS_SOCKET_ERROR_E || ret == WS_CHANNEL_CLOSED) {
ret = 0;
}

error = wolfSSH_get_error(ssh);
if (error != WS_SOCKET_ERROR_E &&
(error == WS_WANT_READ || error == WS_WANT_WRITE)) {
if (ret == WS_WANT_READ || ret == WS_WANT_WRITE) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shutdown drain is skipped when the owed flush lives only in ssh-error · Logic errors

wolfSSH_worker() no longer returns WS_WANT_WRITE for an owed flush, so wolfSSH_shutdown() returns WS_SUCCESS (via its WS_CHAN_RXD remap at ssh.c:1200) while teardown output is still queued. The ret-only gate then skips the drain and the socket closes with those packets unsent. Same at echoserver.c:1647 and sftpclient.c:1794.

Fix: Also enter the drain when wolfSSH_get_error(ssh) == WS_WANT_WRITE, at all three shutdown sites.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graceful-shutdown drain loop is skipped when only a flush is owed · Channel handling errors

The drain gate now reads wolfSSH_shutdown()'s return, but the PR's contract puts an owed flush in ssh->error, not the return. When the peer's CHANNEL_CLOSE arrives during shutdown (return WS_CHANNEL_CLOSED, mapped to 0) with a blocked write, the loop is skipped and wolfSSH_free() discards the queued EOF/CLOSE/window-adjust bytes; the previous wolfSSH_get_error() gate drained them.

Fix: Also enter the drain loop when wolfSSH_get_error(ssh) == WS_WANT_WRITE, and apply the same at echoserver.c:1647 and sftpclient.c:1794.

int maxAttempt = 10; /* make 10 attempts max before giving up */
int attempt;

for (attempt = 0; attempt < maxAttempt; attempt++) {
ret = wolfSSH_worker(ssh, NULL);
error = wolfSSH_get_error(ssh);

/* peer successfully closed down gracefully */
if (ret == WS_CHANNEL_CLOSED) {
Expand All @@ -2645,9 +2642,11 @@ static void* HandleConnection(void* arg)
break;
}

if (ret == WS_FATAL_ERROR &&
(error != WS_WANT_READ &&
error != WS_WANT_WRITE)) {
/* Keep draining while the socket blocks or the peer is still
* talking. Anything else is a failure worth giving up on. */
if (ret != WS_SUCCESS && ret != WS_WANT_READ &&
ret != WS_WANT_WRITE && ret != WS_CHAN_RXD &&
ret != WS_EXTDATA && ret != WS_REKEYING) {
break;
}
#ifdef _WIN32
Expand Down
6 changes: 4 additions & 2 deletions examples/client/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
}
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E &&
ret != WS_CHANNEL_CLOSED) {
ret != WS_CHANNEL_CLOSED &&
ret != WS_WANT_READ && ret != WS_WANT_WRITE) {
ClientFreeBuffers(pubKeyName, privKeyName, NULL);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
Expand All @@ -1226,7 +1227,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E &&
ret != WS_CHANNEL_CLOSED) {
ret != WS_CHANNEL_CLOSED &&
ret != WS_WANT_READ && ret != WS_WANT_WRITE) {
err_sys("Closing client stream failed");
}

Expand Down
14 changes: 8 additions & 6 deletions examples/echoserver/echoserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1383,7 +1383,7 @@ static int sftp_worker(thread_ctx_t* threadCtx)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sftp_worker pre-select flush loop becomes an unbounded busy-wait · Channel handling errors

wolfSSH_worker() now leaves ssh->error == WS_WANT_WRITE for as long as the flush is owed, so this loop — which has no select(), sleep, or iteration bound — spins at 100% CPU until the peer's socket drains, and ignores any channel data received meanwhile. Previously the worker left ssh->error at WS_WANT_READ, so the loop exited after one pass.

Related known finding #10544 (similar but distinct): Both findings affect echoserver worker-side SSH I/O handling, but #10544 is ssh_worker’s loss of unsent tails after partial writes, whereas this is sftp_worker’s unbounded retry loop on an owed flush; the faulting operations, root causes, and required patches differ.

Fix: Bound the loop with a tcp_select() on writability (or an attempt cap) before each wolfSSH_worker() call.


do {
if (ret == WS_WANT_WRITE || ret == WS_CHAN_RXD ||
if (ret == WS_CHAN_RXD || error == WS_WANT_WRITE ||
wolfSSH_SFTP_PendingSend(ssh)) {
/* Yes, process the SFTP data. */
ret = wolfSSH_SFTP_read(ssh);
Expand Down Expand Up @@ -1639,14 +1639,12 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
if (error != WS_SOCKET_ERROR_E && error != WS_FATAL_ERROR) {
ret = wolfSSH_shutdown(threadCtx->ssh);

/* peer hung up, stop shutdown */
if (ret == WS_SOCKET_ERROR_E) {
/* peer hung up or the channel is already gone, stop shutdown */
if (ret == WS_SOCKET_ERROR_E || ret == WS_CHANNEL_CLOSED) {
ret = 0;
}

error = wolfSSH_get_error(threadCtx->ssh);
if (error != WS_SOCKET_ERROR_E &&
(error == WS_WANT_READ || error == WS_WANT_WRITE)) {
if (ret == WS_WANT_READ || ret == WS_WANT_WRITE) {
int maxAttempt = 10; /* make 10 attempts max before giving up */
int attempt;

Expand Down Expand Up @@ -1681,6 +1679,10 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
}
}

/* The report below names how the connection ended, and the shutdown
* drain refreshes error only on the paths that enter it. */
error = wolfSSH_get_error(threadCtx->ssh);

if (threadCtx->fd != -1) {
WCLOSESOCKET(threadCtx->fd);
threadCtx->fd = -1;
Expand Down
3 changes: 2 additions & 1 deletion examples/scpclient/scpclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
}
else {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret != WS_CHANNEL_CLOSED) {
if (ret != WS_SUCCESS && ret != WS_CHANNEL_CLOSED &&
ret != WS_WANT_READ && ret != WS_WANT_WRITE) {
WLOG(WS_LOG_DEBUG,
"Failed to listen for close messages from the peer.");
}
Expand Down
34 changes: 1 addition & 33 deletions examples/sftpclient/sftpclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -661,9 +661,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}

ret = wolfSSH_SFTP_Get(ssh, pt, to, resume, &myStatusCb);
Expand Down Expand Up @@ -772,9 +769,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}

ret = wolfSSH_SFTP_Put(ssh, pt, to, resume, &myStatusCb);
Expand Down Expand Up @@ -866,9 +860,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}

ret = wolfSSH_SFTP_STAT(ssh, pt, &atrb);
Expand Down Expand Up @@ -920,9 +911,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}

ret = wolfSSH_SFTP_CHMOD(ssh, path, mode);
Expand Down Expand Up @@ -987,9 +975,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}
ret = wolfSSH_SFTP_Open(ssh, path,
WOLFSSH_FXF_WRITE | WOLFSSH_FXF_CREAT |
Expand All @@ -1003,9 +988,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}
ret = wolfSSH_SFTP_Close(ssh, handle, handleSz);
err = wolfSSH_get_error(ssh);
Expand Down Expand Up @@ -1060,9 +1042,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}

ret = wolfSSH_SFTP_RMDIR(ssh, pt);
Expand Down Expand Up @@ -1117,9 +1096,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}

ret = wolfSSH_SFTP_Remove(ssh, pt);
Expand Down Expand Up @@ -1210,9 +1186,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}

ret = wolfSSH_SFTP_Rename(ssh, pt, to);
Expand Down Expand Up @@ -1348,9 +1321,6 @@ static int doCmds(func_args* args)
do {
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
}

current = wolfSSH_SFTP_LS(ssh, workingDir);
Expand Down Expand Up @@ -1821,9 +1791,7 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
ret = 0;
}

err = wolfSSH_get_error(ssh);
if (err != WS_SOCKET_ERROR_E &&
(err == WS_WANT_READ || err == WS_WANT_WRITE)) {
if (ret == WS_WANT_READ || ret == WS_WANT_WRITE) {
int maxAttempt = 10; /* make 10 attempts max before giving up */
int attempt;

Expand Down
Loading
Loading