From dbcaa75f49ab533bdff5c441dd18f456adea7567 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Fri, 9 Oct 2026 14:56:17 -0700 Subject: [PATCH] apps, examples: join readInput() on Windows The Windows path of apps/wolfssh and examples/client sets the volatile quit with InterlockedOr8() once readPeer() returns, repeats CancelSynchronousIo() on the readInput() thread until it has exited, and only then closes the handles and frees the session. readInput() checks quit around its console read and inside its send retries, and returns quietly when it is set. Issue: F-11662 --- apps/wolfssh/wolfssh.c | 28 ++++++++++++++++++++++------ examples/client/client.c | 26 +++++++++++++++++++++----- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 5ebbdf8d..b6b8c249 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -268,7 +268,7 @@ typedef struct thread_args { WOLFSSH* ssh; wolfSSL_Mutex lock; byte rawMode; - byte quit; + volatile byte quit; /* set by the main thread, read by the workers */ int readError; } thread_args; @@ -538,7 +538,7 @@ static void readInputLoop(thread_args* args, byte* buf, int bufSz) HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); #endif - while (ret >= 0) { + while (ret >= 0 && !args->quit) { WMEMSET(buf, 0, bufSz); #ifdef USE_WINDOWS_API /* Using A version to avoid potential 2 byte chars */ @@ -549,6 +549,11 @@ static void readInputLoop(thread_args* args, byte* buf, int bufSz) ret = (int)read(STDIN_FILENO, buf, bufSz -1); sz = (word32)ret; #endif + if (args->quit) { + /* The session is over and the read was broken to end this + * thread. */ + break; + } if (ret <= 0) { fprintf(stderr, "Error reading stdin\n"); break; @@ -590,9 +595,11 @@ static void readInputLoop(thread_args* args, byte* buf, int bufSz) else { break; } - } while (off < sz); + } while (off < sz && !args->quit); if (ret <= 0) { - fprintf(stderr, "Couldn't send data\n"); + if (!args->quit) { + fprintf(stderr, "Couldn't send data\n"); + } break; } } @@ -1396,9 +1403,9 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) wc_InitMutex(&arg.lock); arg.ssh = ssh; + arg.quit = 0; arg.readError = 0; #ifdef WOLFSSH_TERM - arg.quit = 0; if (!wolfSSH_SEMAPHORE_Init(&windowSem, 0)) { err_sys("Couldn't initialize window semaphore."); } @@ -1421,9 +1428,9 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) pthread_create(&thread[1], NULL, readInput, (void*)&arg); pthread_create(&thread[2], NULL, readPeer, (void*)&arg); pthread_join(thread[2], NULL); + arg.quit = 1; #ifdef WOLFSSH_TERM /* Wake the windowMonitor thread so it can exit. */ - arg.quit = 1; signal(SIGWINCH, SIG_DFL); wolfSSH_SEMAPHORE_Post(&windowSem); pthread_join(thread[0], NULL); @@ -1440,6 +1447,7 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) arg.ssh = ssh; arg.rawMode = rawMode; + arg.quit = 0; arg.readError = 0; wc_InitMutex(&arg.lock); @@ -1458,6 +1466,14 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) thread[0] = CreateThread(NULL, 0, readInput, (void*)&arg, 0, 0); thread[1] = CreateThread(NULL, 0, readPeer, (void*)&arg, 0, 0); WaitForSingleObject(thread[1], INFINITE); + /* readInput() is blocked in ReadConsoleA() and would use the session + * after it is freed. Break the read and wait for the thread to see + * quit. The cancel finds nothing while the read is not yet pending, + * so repeat it until the thread is gone. */ + InterlockedOr8((volatile CHAR*)&arg.quit, 1); + do { + CancelSynchronousIo(thread[0]); + } while (WaitForSingleObject(thread[0], 100) == WAIT_TIMEOUT); CloseHandle(thread[0]); CloseHandle(thread[1]); ioErr = arg.readError; diff --git a/examples/client/client.c b/examples/client/client.c index 5763c0b2..37463645 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -177,7 +177,7 @@ typedef struct thread_args { WOLFSSH* ssh; wolfSSL_Mutex lock; byte rawMode; - byte quit; + volatile byte quit; /* set by the main thread, read by the workers */ } thread_args; #ifdef _POSIX_THREADS @@ -372,7 +372,7 @@ static void readInputLoop(thread_args* args, byte* buf, int bufSz) HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); #endif - while (ret >= 0) { + while (ret >= 0 && !args->quit) { WMEMSET(buf, 0, bufSz); #ifdef USE_WINDOWS_API /* Using A version to avoid potential 2 byte chars */ @@ -383,6 +383,11 @@ static void readInputLoop(thread_args* args, byte* buf, int bufSz) ret = (int)read(STDIN_FILENO, buf, bufSz -1); sz = (word32)ret; #endif + if (args->quit) { + /* The session is over and the read was broken to end this + * thread. */ + break; + } if (ret <= 0) { fprintf(stderr, "Error reading stdin\n"); break; @@ -424,10 +429,12 @@ static void readInputLoop(thread_args* args, byte* buf, int bufSz) usleep(1000); #endif } - } while (off < sz && + } while (off < sz && !args->quit && (err != WS_WANT_WRITE || WTIME(NULL) < deadline)); if (ret <= 0) { - fprintf(stderr, "Couldn't send data\n"); + if (!args->quit) { + fprintf(stderr, "Couldn't send data\n"); + } break; } } @@ -1259,9 +1266,9 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) pthread_create(&thread[1], NULL, readInput, (void*)&arg); pthread_create(&thread[2], NULL, readPeer, (void*)&arg); pthread_join(thread[2], NULL); + arg.quit = 1; #ifdef WOLFSSH_TERM /* Wake the windowMonitor thread so it can exit. */ - arg.quit = 1; signal(SIGWINCH, SIG_DFL); wolfSSH_SEMAPHORE_Post(&windowSem); pthread_join(thread[0], NULL); @@ -1277,6 +1284,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) arg.ssh = ssh; arg.rawMode = rawMode; + arg.quit = 0; wc_InitMutex(&arg.lock); if (cmd) { @@ -1293,6 +1301,14 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) thread[0] = CreateThread(NULL, 0, readInput, (void*)&arg, 0, 0); thread[1] = CreateThread(NULL, 0, readPeer, (void*)&arg, 0, 0); WaitForSingleObject(thread[1], INFINITE); + /* readInput() is blocked in ReadConsoleA() and would use the session + * after it is freed. Break the read and wait for the thread to see + * quit. The cancel finds nothing while the read is not yet pending, + * so repeat it until the thread is gone. */ + InterlockedOr8((volatile CHAR*)&arg.quit, 1); + do { + CancelSynchronousIo(thread[0]); + } while (WaitForSingleObject(thread[0], 100) == WAIT_TIMEOUT); CloseHandle(thread[0]); CloseHandle(thread[1]); #else