From bbf46b5f3ff9e8d196fe0ffacc48a9670362d4ae Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Tue, 15 Sep 2026 09:23:53 +0900 Subject: [PATCH 1/2] examples, ide, tests: classify the worker's status at the call site - ssh_worker() in both echoservers maps a WS_FATAL_ERROR return from wolfSSH_worker() to WS_WANT_READ or WS_WANT_WRITE at the call site, off a new local err, and keeps the result in rc. - The terminal arm tests rc in place of re-reading wolfSSH_get_error(): rc != WS_WANT_READ in examples/echoserver, and rc != WS_WANT_READ && rc != WS_WANT_WRITE in the Espressif copy, whose empty WS_WANT_WRITE arm is removed. - ssh_worker() in examples/echoserver sets wantWrite from wolfSSH_OutputPending() before it builds writeFds. - The comment above rc = cnt_r in both echoservers names cnt_r and ssh->error as the values that are reused. - scripts/sshclient.test and scripts/fwd-bulk.test name the client's missing non-blocking mode in the skip message where they echoed the macro name, and the comments above both skips are removed. --- examples/echoserver/echoserver.c | 17 ++++++++++++----- .../wolfssh_echoserver/main/echoserver.c | 17 +++++++++-------- scripts/fwd-bulk.test | 6 ++---- scripts/sshclient.test | 6 +----- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index cc63b7a07..caa2e08c2 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -1220,6 +1220,9 @@ static int ssh_worker(thread_ctx_t* threadCtx) } #endif + if (wolfSSH_OutputPending(ssh)) + wantWrite = 1; + FD_ZERO(&writeFds); if (wantWrite) FD_SET(sshFd, &writeFds); @@ -1295,9 +1298,15 @@ static int ssh_worker(thread_ctx_t* threadCtx) break; } #endif - /* The channel reads below overwrite cnt_r with a byte - * count, so keep the worker's status. */ + /* rc keeps the worker's status: cnt_r and ssh->error are + * both reused. */ rc = cnt_r; + if (rc == WS_FATAL_ERROR) { + int err = wolfSSH_get_error(ssh); + + if (err == WS_WANT_READ || err == WS_WANT_WRITE) + rc = err; + } /* The peer is done sending: hand back the backlog and answer * its EOF, since the library no longer answers for us. Off @@ -1513,9 +1522,7 @@ static int ssh_worker(thread_ctx_t* threadCtx) wantWrite = 1; continue; } - else if (rc != WS_FATAL_ERROR - || (wolfSSH_get_error(ssh) != WS_WANT_READ - && wolfSSH_get_error(ssh) != WS_WANT_WRITE)) { + else if (rc != WS_WANT_READ) { #ifdef SHELL_DEBUG printf("Break:read sshFd returns %d: errno =%x\n", cnt_r, errno); diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c index c8c1fdb74..3f6f708ac 100644 --- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c +++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c @@ -995,9 +995,15 @@ static int ssh_worker(thread_ctx_t* threadCtx) channel. The additional channel is only used with the agent. */ cnt_r = wolfSSH_worker(ssh, &lastChannel); - /* The channel reads below overwrite cnt_r with a byte - * count, so keep the worker's status. */ + /* rc keeps the worker's status: cnt_r and ssh->error are + * both reused. */ rc = cnt_r; + if (rc == WS_FATAL_ERROR) { + int err = wolfSSH_get_error(ssh); + + if (err == WS_WANT_READ || err == WS_WANT_WRITE) + rc = err; + } /* The peer is done sending: hand back the backlog and answer * its EOF, since the library no longer answers for us. Off @@ -1167,12 +1173,7 @@ static int ssh_worker(thread_ctx_t* threadCtx) * above, which has already run this pass. */ continue; } - else if (rc == WS_WANT_WRITE) { - /* Transient; the queue drives the write side. */ - } - else if (rc != WS_FATAL_ERROR - || (wolfSSH_get_error(ssh) != WS_WANT_READ - && wolfSSH_get_error(ssh) != WS_WANT_WRITE)) { + else if (rc != WS_WANT_READ && rc != WS_WANT_WRITE) { #ifdef SHELL_DEBUG printf("Break:read sshFd returns %d: errno =%x\n", cnt_r, errno); diff --git a/scripts/fwd-bulk.test b/scripts/fwd-bulk.test index 4004329c9..b327c38cd 100755 --- a/scripts/fwd-bulk.test +++ b/scripts/fwd-bulk.test @@ -80,15 +80,13 @@ stall_limit=15 ./examples/portfwd/portfwd '-?' 2>&1 | grep -q "does not exist" \ && { echo "forwarding not compiled in, skipping"; exit 77; } -# A WOLFSSH_TEST_BLOCK build fails writes at random, which stalls the -# echoserver regardless of what the peer does. The other echoserver scripts -# skip it for the same reason. WOLFSSH_OPTIONS=`./apps/wolfssh-options` || { echo "fail: could not run ./apps/wolfssh-options" exit 1 } echo "$WOLFSSH_OPTIONS" | grep -qx "TEST_BLOCK" \ - && { echo "macro WOLFSSH_TEST_BLOCK was used, skipping"; exit 77; } + && { echo "portfwd does not support non-blocking mode, skipping test" + exit 77; } do_cleanup() { for pid in $nc_client_pid $portfwd_pid $server_pid $nc_server_pid \ diff --git a/scripts/sshclient.test b/scripts/sshclient.test index 762f457fe..4e8c26843 100755 --- a/scripts/sshclient.test +++ b/scripts/sshclient.test @@ -30,14 +30,10 @@ client_limit=60 ./examples/echoserver/echoserver '-?' 2>&1 | grep -q "^echoserver " \ || { echo "echoserver doesn't run, skipping"; exit 77; } -# A WOLFSSH_TEST_BLOCK build fails writes at random. The echoserver leaves a -# failed write queued and then waits on the peer for a reply to the message -# it never sent, so a session stalls no matter what the client does. The -# other echoserver scripts skip this build for the same reason. if [ -x ./examples/client/client ] \ && ./examples/client/client -h 2>&1 | grep -q "WOLFSSH_TEST_BLOCK" then - echo "macro WOLFSSH_TEST_BLOCK was used, skipping" + echo "wolfssh client does not support non-blocking mode, skipping test" exit 77 fi From fab606684562cbca7a062db7dce6e1d92b504a5b Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 15 Sep 2026 11:12:04 -0700 Subject: [PATCH 2/2] tests: run app-driven sftp in a block build The non-blocking app-driven case runs under WOLFSSH_TEST_BLOCK again, reverting ba899603. It deadlocks there until ssh_worker() waits on the write side for a send wolfSSH_worker() reported through ssh->error, so this wants PR 1251 landed with the wantWrite arm kept. --- scripts/sftp.test | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/scripts/sftp.test b/scripts/sftp.test index 90fcdd199..9e551df15 100755 --- a/scripts/sftp.test +++ b/scripts/sftp.test @@ -6,7 +6,6 @@ no_pid=-1 server_pid="$no_pid" ready_file="$PWD/wolfssh_sftp_ready$$" nonblockingOnly=0 -blockBuild=0 [ ! -x ./examples/sftpclient/wolfsftp ] && echo && echo "wolfSFTP client doesn't exist" && exit 1 @@ -28,7 +27,6 @@ if echo "$WOLFSSH_OPTIONS" | grep -qx "TEST_BLOCK" then echo "macro WOLFSSH_TEST_BLOCK was used" nonblockingOnly=1 - blockBuild=1 fi #echo "ready file $ready_file" @@ -134,24 +132,19 @@ if [ "$nonblockingOnly" = 0 ]; then fi # The same handoff on a non-blocking server, which reaches the accept call's -# want-read and want-write retries. Held out of a forced-blocking build: the -# two ends deadlock there, the server parked in DoReceive with output it owes -# still queued, which is a defect of its own rather than this case failing. -if [ "$blockBuild" = 0 ]; then - echo "Test non blocking connection to an app-driven server" - ./examples/echoserver/echoserver -A -N -1 -R "$ready_file" & - server_pid=$! - create_port - echo "exit" | ./examples/sftpclient/wolfsftp -N -u jill -P upthehill \ - -p "$port" - RESULT=$? - remove_ready_file - if [ $RESULT -ne 0 ]; then - echo - echo "failed to connect to non blocking app-driven server" - do_cleanup - exit 1 - fi +# want-read and want-write retries. +echo "Test non blocking connection to an app-driven server" +./examples/echoserver/echoserver -A -N -1 -R "$ready_file" & +server_pid=$! +create_port +echo "exit" | ./examples/sftpclient/wolfsftp -N -u jill -P upthehill -p "$port" +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo + echo "failed to connect to non blocking app-driven server" + do_cleanup + exit 1 fi # Test want write return from highwater callback