From 3ee43e047b6507db9e9bf7689f9f896edbebf8e2 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Thu, 23 Jul 2026 11:39:04 -0700 Subject: [PATCH] fix: free scpBasePathDynamic before realloc in ParseScpCommand A peer command that repeats the direction option (scp -t a -t b, scp -f a -f b) re-enters the case and overwrites scpBasePathDynamic, leaking the earlier buffer for the connection lifetime. Free and clear the old buffer before each reallocation, and keep scpBasePathSz in lockstep with the pointer the way wolfSSH_free() already does. scpBasePath aliases that buffer and is only reassigned when the option carries a path, so clear it alongside the free; otherwise a later pathless -t/-f leaves it dangling into freed memory that DoScpSource()/DoScpSink() hand to the scp callback. Clearing the alias exposes a pre-existing case: a direction option with no path parses to WS_SUCCESS with a NULL base path, which the callbacks then dereference. Reject that after the parse loop instead. Issue: F-6813 --- src/wolfscp.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/wolfscp.c b/src/wolfscp.c index 441549673..32c1fd7a9 100644 --- a/src/wolfscp.c +++ b/src/wolfscp.c @@ -1540,13 +1540,21 @@ int ParseScpCommand(WOLFSSH* ssh) case 't': ssh->scpDirection = WOLFSSH_SCP_TO; - ssh->scpBasePathSz = cmdSz + WOLFSSH_MAX_FILENAME; + if (ssh->scpBasePathDynamic != NULL) { + WFREE(ssh->scpBasePathDynamic, ssh->ctx->heap, + DYNTYPE_BUFFER); + ssh->scpBasePathDynamic = NULL; + /* scpBasePath aliases the buffer just freed */ + ssh->scpBasePath = NULL; + ssh->scpBasePathSz = 0; + } ssh->scpBasePathDynamic = (char*)WMALLOC( - ssh->scpBasePathSz, + cmdSz + WOLFSSH_MAX_FILENAME, ssh->ctx->heap, DYNTYPE_BUFFER); if (ssh->scpBasePathDynamic == NULL) { return WS_MEMORY_E; } + ssh->scpBasePathSz = cmdSz + WOLFSSH_MAX_FILENAME; WMEMSET(ssh->scpBasePathDynamic, 0, ssh->scpBasePathSz); if (idx + 2 < cmdSz) { /* skip space */ @@ -1567,13 +1575,21 @@ int ParseScpCommand(WOLFSSH* ssh) case 'f': ssh->scpDirection = WOLFSSH_SCP_FROM; - ssh->scpBasePathSz = cmdSz + WOLFSSH_MAX_FILENAME; + if (ssh->scpBasePathDynamic != NULL) { + WFREE(ssh->scpBasePathDynamic, ssh->ctx->heap, + DYNTYPE_BUFFER); + ssh->scpBasePathDynamic = NULL; + /* scpBasePath aliases the buffer just freed */ + ssh->scpBasePath = NULL; + ssh->scpBasePathSz = 0; + } ssh->scpBasePathDynamic = (char*)WMALLOC( - ssh->scpBasePathSz, + cmdSz + WOLFSSH_MAX_FILENAME, ssh->ctx->heap, DYNTYPE_BUFFER); if (ssh->scpBasePathDynamic == NULL) { return WS_MEMORY_E; } + ssh->scpBasePathSz = cmdSz + WOLFSSH_MAX_FILENAME; WMEMSET(ssh->scpBasePathDynamic, 0, ssh->scpBasePathSz); if (idx + 2 < cmdSz) { /* skip space */ @@ -1596,6 +1612,11 @@ int ParseScpCommand(WOLFSSH* ssh) ssh->scpDirection != WOLFSSH_SCP_FROM) { ret = WS_SCP_CMD_E; } + else if (ret == WS_SUCCESS && ssh->scpBasePath == NULL) { + /* direction set but no path parsed; don't hand a NULL base + * path to the scp callbacks */ + ret = WS_SCP_CMD_E; + } } return ret;