Skip to content

Commit fb4ddba

Browse files
committed
Fix double-free crash and socket-close spin
1 parent 70914a9 commit fb4ddba

File tree

5 files changed

+137
-29
lines changed

5 files changed

+137
-29
lines changed

apps/wolfssh/common.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ static byte* userPublicKey = userPublicKeyBuf;
4545
static const byte* userPublicKeyType = NULL;
4646
static byte userPassword[256];
4747
static const byte* userPrivateKeyType = NULL;
48+
static byte userPublicKeyAlloc = 0;
4849
static word32 userPublicKeySz = 0;
4950
static byte pubKeyLoaded = 0; /* was a public key loaded */
5051
static byte userPrivateKeyBuf[1191];
5152
static byte* userPrivateKey = userPrivateKeyBuf;
53+
static byte userPrivateKeyAlloc = 0;
5254
static word32 userPublicKeyTypeSz = 0;
5355
static word32 userPrivateKeySz = sizeof(userPrivateKeyBuf);
5456
static word32 userPrivateKeyTypeSz = 0;
@@ -670,6 +672,13 @@ int ClientUseCert(const char* certName)
670672
userPublicKeyType = publicKeyType;
671673
userPublicKeyTypeSz = (word32)WSTRLEN((const char*)publicKeyType);
672674
pubKeyLoaded = 1;
675+
userPublicKeyAlloc = 1;
676+
}
677+
else {
678+
userPublicKey = userPublicKeyBuf;
679+
userPublicKeySz = 0;
680+
userPublicKeyType = NULL;
681+
userPublicKeyAlloc = 0;
673682
}
674683
#else
675684
fprintf(stderr, "Certificate support not compiled in");
@@ -687,12 +696,22 @@ int ClientSetPrivateKey(const char* privKeyName)
687696
{
688697
int ret;
689698

699+
userPrivateKeyAlloc = 0;
690700
userPrivateKey = NULL; /* create new buffer based on parsed input */
691701
ret = wolfSSH_ReadKey_file(privKeyName,
692702
(byte**)&userPrivateKey, &userPrivateKeySz,
693703
(const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz,
694704
&isPrivate, NULL);
695705

706+
if (ret == 0) {
707+
userPrivateKeyAlloc = 1;
708+
}
709+
else {
710+
userPrivateKey = userPrivateKeyBuf;
711+
userPrivateKeySz = sizeof(userPrivateKeyBuf);
712+
userPrivateKeyType = NULL;
713+
}
714+
696715
return ret;
697716
}
698717

@@ -703,6 +722,7 @@ int ClientUsePubKey(const char* pubKeyName)
703722
{
704723
int ret;
705724

725+
userPublicKeyAlloc = 0;
706726
userPublicKey = NULL; /* create new buffer based on parsed input */
707727
ret = wolfSSH_ReadKey_file(pubKeyName,
708728
&userPublicKey, &userPublicKeySz,
@@ -711,6 +731,11 @@ int ClientUsePubKey(const char* pubKeyName)
711731

712732
if (ret == 0) {
713733
pubKeyLoaded = 1;
734+
userPublicKeyAlloc = 1;
735+
}
736+
else {
737+
userPublicKey = userPublicKeyBuf;
738+
userPublicKeySz = 0;
714739
}
715740

716741
return ret;
@@ -747,11 +772,17 @@ int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert)
747772

748773
void ClientFreeBuffers(void)
749774
{
750-
if (userPublicKey != userPublicKeyBuf) {
775+
if (userPublicKeyAlloc && userPublicKey != NULL) {
751776
WFREE(userPublicKey, NULL, DYNTYPE_PRIVKEY);
777+
userPublicKey = userPublicKeyBuf;
778+
userPublicKeySz = 0;
779+
userPublicKeyAlloc = 0;
752780
}
753781

754-
if (userPrivateKey != userPrivateKeyBuf) {
782+
if (userPrivateKeyAlloc && userPrivateKey != NULL) {
755783
WFREE(userPrivateKey, NULL, DYNTYPE_PRIVKEY);
784+
userPrivateKey = userPrivateKeyBuf;
785+
userPrivateKeySz = sizeof(userPrivateKeyBuf);
786+
userPrivateKeyAlloc = 0;
756787
}
757788
}

apps/wolfssh/wolfssh.c

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ typedef struct thread_args {
220220
wolfSSL_Mutex lock;
221221
byte rawMode;
222222
byte quit;
223+
int readError;
223224
} thread_args;
224225

225226
#ifdef _POSIX_THREADS
@@ -390,6 +391,7 @@ static THREAD_RET readPeer(void* in)
390391
int bufSz = sizeof(buf);
391392
thread_args* args = (thread_args*)in;
392393
int ret = 0;
394+
int stop = 0;
393395
int fd = wolfSSH_get_fd(args->ssh);
394396
word32 bytes;
395397
#ifdef USE_WINDOWS_API
@@ -398,11 +400,6 @@ static THREAD_RET readPeer(void* in)
398400
fd_set readSet;
399401
fd_set errSet;
400402

401-
FD_ZERO(&readSet);
402-
FD_ZERO(&errSet);
403-
FD_SET(fd, &readSet);
404-
FD_SET(fd, &errSet);
405-
406403
#ifdef USE_WINDOWS_API
407404
if (args->rawMode == 0) {
408405
DWORD wrd;
@@ -431,9 +428,13 @@ static THREAD_RET readPeer(void* in)
431428
#endif
432429

433430
while (ret >= 0) {
434-
#if defined(WOLFSSH_TERM) && defined(USE_WINDOWS_API)
431+
#if defined(WOLFSSH_TERM) && defined(USE_WINDOWS_API)
435432
(void)windowMonitor(args);
436-
#endif
433+
#endif
434+
FD_ZERO(&readSet);
435+
FD_ZERO(&errSet);
436+
FD_SET(fd, &readSet);
437+
FD_SET(fd, &errSet);
437438

438439
bytes = select(fd + 1, &readSet, NULL, &errSet, NULL);
439440
wc_LockMutex(&args->lock);
@@ -458,18 +459,18 @@ static THREAD_RET readPeer(void* in)
458459
} while (ret > 0);
459460
}
460461
else if (ret <= 0) {
461-
if (ret == WS_FATAL_ERROR) {
462-
ret = wolfSSH_get_error(args->ssh);
463-
if (ret == WS_WANT_READ) {
464-
continue;
465-
}
466-
#ifdef WOLFSSH_AGENT
467-
else if (ret == WS_CHAN_RXD) {
468-
byte agentBuf[512];
469-
int rxd, txd;
470-
word32 channel = 0;
462+
int err = (ret == WS_FATAL_ERROR) ?
463+
wolfSSH_get_error(args->ssh) : ret;
464+
if (err == WS_WANT_READ) {
465+
bytes = 0;
466+
}
467+
#ifdef WOLFSSH_AGENT
468+
else if (err == WS_CHAN_RXD) {
469+
byte agentBuf[512];
470+
int rxd, txd;
471+
word32 channel = 0;
471472

472-
wolfSSH_GetLastRxId(args->ssh, &channel);
473+
wolfSSH_GetLastRxId(args->ssh, &channel);
473474
rxd = wolfSSH_ChannelIdRead(args->ssh, channel,
474475
agentBuf, sizeof(agentBuf));
475476
if (rxd > 4) {
@@ -495,9 +496,17 @@ static THREAD_RET readPeer(void* in)
495496
WMEMSET(agentBuf, 0, sizeof(agentBuf));
496497
continue;
497498
}
498-
#endif /* WOLFSSH_AGENT */
499+
#endif /* WOLFSSH_AGENT */
500+
else if (err == WS_CBIO_ERR_CONN_CLOSE ||
501+
err == WS_SOCKET_ERROR_E ||
502+
err == WS_MSGID_NOT_ALLOWED_E) {
503+
args->readError = err;
504+
ret = err;
505+
stop = 1;
506+
bytes = 0;
499507
}
500-
else if (ret != WS_EOF) {
508+
else if (err != WS_EOF) {
509+
wc_UnLockMutex(&args->lock);
501510
err_sys("Stream read failed.");
502511
}
503512
}
@@ -517,12 +526,16 @@ static THREAD_RET readPeer(void* in)
517526
}
518527
#endif
519528
}
520-
ret = wolfSSH_stream_peek(args->ssh, buf, bufSz);
521-
if (ret <= 0) {
522-
bytes = 0; /* read it all */
529+
if (!stop) {
530+
ret = wolfSSH_stream_peek(args->ssh, buf, bufSz);
531+
if (ret <= 0) {
532+
bytes = 0; /* read it all */
533+
}
523534
}
524535
}
525536
wc_UnLockMutex(&args->lock);
537+
if (stop)
538+
break;
526539
}
527540
#if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS)
528541
wc_ecc_fp_free(); /* free per thread cache */
@@ -791,7 +804,8 @@ static int config_parse_command_line(struct config* config,
791804
if (found != NULL) {
792805
*found = '\0';
793806
if (config->user) {
794-
free(config->user);
807+
WFREE(config->user, NULL, 0);
808+
config->user = NULL;
795809
}
796810
sz = WSTRLEN(cursor);
797811
config->user = (char*)WMALLOC(sz + 1, NULL, 0);
@@ -818,7 +832,7 @@ static int config_parse_command_line(struct config* config,
818832
strcpy(config->hostname, cursor);
819833
}
820834

821-
free(dest);
835+
WFREE(dest, NULL, 0);
822836
myoptind++;
823837
}
824838

@@ -874,18 +888,23 @@ static int config_cleanup(struct config* config)
874888
{
875889
if (config->user) {
876890
WFREE(config->user, NULL, 0);
891+
config->user = NULL;
877892
}
878893
if (config->hostname) {
879894
WFREE(config->hostname, NULL, 0);
895+
config->hostname = NULL;
880896
}
881897
if (config->keyFile) {
882898
WFREE(config->keyFile, NULL, 0);
899+
config->keyFile = NULL;
883900
}
884901
if (config->pubKeyFile) {
885902
WFREE(config->pubKeyFile, NULL, 0);
903+
config->pubKeyFile = NULL;
886904
}
887905
if (config->command) {
888906
WFREE(config->command, NULL, 0);
907+
config->command = NULL;
889908
}
890909

891910
return 0;
@@ -900,6 +919,7 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
900919
SOCKADDR_IN_T clientAddr;
901920
socklen_t clientAddrSz = sizeof(clientAddr);
902921
int ret = 0;
922+
int ioErr = 0;
903923
byte keepOpen = 1;
904924
#ifdef USE_WINDOWS_API
905925
byte rawMode = 0;
@@ -1037,6 +1057,7 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
10371057

10381058
wc_InitMutex(&arg.lock);
10391059
arg.ssh = ssh;
1060+
arg.readError = 0;
10401061
#ifdef WOLFSSH_TERM
10411062
arg.quit = 0;
10421063
#if (defined(__OSX__) || defined(__APPLE__))
@@ -1082,12 +1103,14 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
10821103
sem_destroy(&windowSem);
10831104
#endif
10841105
#endif /* WOLFSSH_TERM */
1106+
ioErr = arg.readError;
10851107
#elif defined(_MSC_VER)
10861108
thread_args arg;
10871109
HANDLE thread[2];
10881110

10891111
arg.ssh = ssh;
10901112
arg.rawMode = rawMode;
1113+
arg.readError = 0;
10911114
wc_InitMutex(&arg.lock);
10921115

10931116
if (config.command) {
@@ -1107,6 +1130,7 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
11071130
WaitForSingleObject(thread[1], INFINITE);
11081131
CloseHandle(thread[0]);
11091132
CloseHandle(thread[1]);
1133+
ioErr = arg.readError;
11101134
#else
11111135
err_sys("No threading to use");
11121136
#endif
@@ -1139,10 +1163,13 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
11391163
#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL)
11401164
((func_args*)args)->return_code = wolfSSH_GetExitStatus(ssh);
11411165
#endif
1166+
if (ioErr != 0 && ((func_args*)args)->return_code == 0) {
1167+
((func_args*)args)->return_code = 1;
1168+
}
11421169

11431170
wolfSSH_free(ssh);
11441171
wolfSSH_CTX_free(ctx);
1145-
if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) {
1172+
if ((ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) || ioErr != 0) {
11461173
WLOG(WS_LOG_DEBUG, "Closing client stream failed");
11471174
#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL)
11481175
/* override return value, do not want to return success if connection

src/internal.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,8 @@ INLINE static int IsMessageAllowedClient(WOLFSSH *ssh, byte msg)
691691

692692
/* Error if expecting a specific message and didn't receive. */
693693
if (ssh->handshake && ssh->handshake->expectMsgId != MSGID_NONE) {
694+
/* The explicit expectMsgId check supersedes the old
695+
* IsMessageAllowedKeying() stub for rekey filtering. */
694696
if (msg != ssh->handshake->expectMsgId) {
695697
WLOG(WS_LOG_DEBUG,
696698
"Message ID %u not the expected message %u",

tests/include.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ tests_kex_test_CPPFLAGS = -DNO_MAIN_DRIVER $(AM_CPPFLAGS)
4545
tests_kex_test_LDADD = src/libwolfssh.la
4646
tests_kex_test_DEPENDENCIES = src/libwolfssh.la
4747

48-
tests_regress_test_SOURCES = tests/regress.c
48+
tests_regress_test_SOURCES = tests/regress.c apps/wolfssh/common.c \
49+
apps/wolfssh/common.h
4950
tests_regress_test_CPPFLAGS = -DNO_MAIN_DRIVER -DWOLFSSH_TEST_INTERNAL $(AM_CPPFLAGS)
5051
tests_regress_test_LDADD = src/libwolfssh_test.la
5152
tests_regress_test_DEPENDENCIES = src/libwolfssh_test.la

tests/regress.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
#include <stdlib.h>
2020
#include <arpa/inet.h>
2121
#include <string.h>
22+
#include <unistd.h>
23+
#include <fcntl.h>
2224

2325
#include <wolfssh/port.h>
2426
#include <wolfssh/ssh.h>
2527
#include <wolfssh/internal.h>
28+
#include "apps/wolfssh/common.h"
2629

2730
#ifndef WOLFSSH_NO_ABORT
2831
#define WABORT() abort()
@@ -362,6 +365,44 @@ static void TestKexInitRejectedWhenKeying(WOLFSSH* ssh)
362365
AssertFalse(allowed);
363366
}
364367

368+
/* Ensure client buffer cleanup tolerates multiple invocations after allocs. */
369+
static void TestClientBuffersIdempotent(void)
370+
{
371+
int ret;
372+
373+
ret = ClientUsePubKey("keys/gretel-key-rsa.pub");
374+
AssertIntEQ(ret, 0);
375+
ret = ClientSetPrivateKey("keys/gretel-key-rsa.pem");
376+
AssertIntEQ(ret, 0);
377+
378+
ClientFreeBuffers();
379+
/* Should be safe to call again without double free. */
380+
ClientFreeBuffers();
381+
}
382+
383+
/* Simulate Ctrl+D (stdin EOF) during password prompt; expect failure but no crash. */
384+
static void TestPasswordEofNoCrash(void)
385+
{
386+
WS_UserAuthData auth;
387+
int savedStdin, devNull, ret;
388+
389+
WMEMSET(&auth, 0, sizeof(auth));
390+
391+
savedStdin = dup(STDIN_FILENO);
392+
devNull = open("/dev/null", O_RDONLY);
393+
AssertTrue(devNull >= 0);
394+
AssertTrue(dup2(devNull, STDIN_FILENO) >= 0);
395+
396+
ret = ClientUserAuth(WOLFSSH_USERAUTH_PASSWORD, &auth, NULL);
397+
AssertIntEQ(ret, WOLFSSH_USERAUTH_FAILURE);
398+
399+
close(devNull);
400+
dup2(savedStdin, STDIN_FILENO);
401+
close(savedStdin);
402+
403+
ClientFreeBuffers();
404+
}
405+
365406

366407
int main(int argc, char** argv)
367408
{
@@ -390,6 +431,12 @@ int main(int argc, char** argv)
390431
TestChannelBlockedBeforeAuth(ssh);
391432
TestChannelAllowedAfterAuth(ssh);
392433
TestKexInitRejectedWhenKeying(ssh);
434+
TestClientBuffersIdempotent();
435+
TestPasswordEofNoCrash();
436+
437+
/* TODO: add app-level regressions that simulate stdin EOF/password
438+
* prompts and mid-session socket closes once the test harness can
439+
* drive the wolfssh client without real sockets/tty. */
393440

394441
ResetSession(ssh);
395442
wolfSSH_free(ssh);

0 commit comments

Comments
 (0)