Skip to content

Commit 8cdfc3e

Browse files
ejohnstownphilljj
authored andcommitted
sftp: publish an OPENDIR handle with its reply
RecvOpenDir() allocates the directory node, its name and the HANDLE reply before linking the node into dirList, so an allocation failure leaves no handle the peer was never told about. Both the POSIX and the Windows branch. Issue: F-10551
1 parent eba6020 commit 8cdfc3e

2 files changed

Lines changed: 113 additions & 96 deletions

File tree

‎src/wolfsftp.c‎

Lines changed: 57 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2919,9 +2919,12 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
29192919
word32 idx = 0;
29202920
int ret = WS_SUCCESS;
29212921
int rc;
2922+
WS_DIR_LIST* cur;
2923+
char* dirName;
2924+
word32 dirNameSz;
29222925

29232926
word32 outSz = WOLFSSH_HANDLE_ID_SZ + WOLFSSH_SFTP_HEADER + UINT32_SZ;
2924-
byte* out = NULL;
2927+
byte* out;
29252928
byte idFlat[WOLFSSH_HANDLE_ID_SZ];
29262929
char per[] = "Permission denied";
29272930
char tooMany[] = "Too Many Open Directory Handles";
@@ -2960,71 +2963,46 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
29602963

29612964
if (WOPENDIR(ssh->fs, ssh->ctx->heap, &ctx, dir) != 0) {
29622965
WLOG(WS_LOG_SFTP, "Error with opening directory: %s", dir);
2963-
if (wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_NOFILE, reqId,
2964-
"Unable To Open Directory", "English", NULL, &outSz)
2965-
!= WS_SIZE_ONLY) {
2966-
return WS_FATAL_ERROR;
2967-
}
2968-
ret = WS_BAD_FILE_E;
2966+
rc = SFTP_SendStatus(ssh, WOLFSSH_FTP_NOFILE, reqId,
2967+
"Unable To Open Directory");
2968+
return (rc == WS_SUCCESS) ? WS_BAD_FILE_E : rc;
29692969
}
29702970

2971-
WOLFSSH_UNUSED(reqId);
2972-
2973-
if (ret == WS_SUCCESS) {
2974-
WS_DIR_LIST* cur = NULL;
2975-
char* dirName = NULL;
2976-
word32 dirNameSz;
2971+
/* allocate everything, the reply included, before publishing the handle
2972+
* so a failure leaves no handle the peer was never told about */
2973+
dirNameSz = (word32)WSTRLEN(dir) + 1;
2974+
cur = (WS_DIR_LIST*)WMALLOC(sizeof(WS_DIR_LIST),
2975+
ssh->ctx->heap, DYNTYPE_SFTP);
2976+
dirName = (char*)WMALLOC(dirNameSz, ssh->ctx->heap, DYNTYPE_PATH);
2977+
out = (byte*)WMALLOC(outSz, ssh->ctx->heap, DYNTYPE_BUFFER);
2978+
if (cur == NULL || dirName == NULL || out == NULL) {
2979+
WCLOSEDIR(ssh->fs, &ctx);
2980+
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
2981+
WFREE(dirName, ssh->ctx->heap, DYNTYPE_PATH);
2982+
WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER);
2983+
return WS_MEMORY_E;
2984+
}
29772985

2978-
cur = (WS_DIR_LIST*)WMALLOC(sizeof(WS_DIR_LIST),
2979-
ssh->ctx->heap, DYNTYPE_SFTP);
2980-
if (cur == NULL) {
2981-
WCLOSEDIR(ssh->fs, &ctx);
2982-
return WS_MEMORY_E;
2983-
}
2984-
dirNameSz = (word32)WSTRLEN(dir) + 1;
2985-
dirName = (char*)WMALLOC(dirNameSz,
2986-
ssh->ctx->heap, DYNTYPE_PATH);
2987-
if (dirName == NULL) {
2988-
WCLOSEDIR(ssh->fs, &ctx);
2989-
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
2990-
return WS_MEMORY_E;
2991-
}
2992-
WMEMCPY(dirName, dir, dirNameSz);
2986+
WMEMCPY(dirName, dir, dirNameSz);
29932987
#ifdef WOLFSSL_NUCLEUS
2994-
WMEMCPY(&cur->dir, &ctx, sizeof(WDIR));
2988+
WMEMCPY(&cur->dir, &ctx, sizeof(WDIR));
29952989
#else
2996-
cur->dir = ctx;
2990+
cur->dir = ctx;
29972991
#endif
2998-
SFTP_HandleIdNext(ssh, cur->id);
2999-
SFTP_HandleIdEncode(cur->id, idFlat);
3000-
cur->isEof = 0;
3001-
cur->next = ssh->dirList;
3002-
ssh->dirList = cur;
3003-
ssh->dirList->dirName = dirName; /* take over ownership of buffer */
3004-
}
2992+
SFTP_HandleIdNext(ssh, cur->id);
2993+
SFTP_HandleIdEncode(cur->id, idFlat);
2994+
cur->isEof = 0;
2995+
cur->dirName = dirName; /* take over ownership of buffer */
2996+
cur->next = ssh->dirList;
2997+
ssh->dirList = cur;
30052998

3006-
out = (byte*)WMALLOC(outSz, ssh->ctx->heap, DYNTYPE_BUFFER);
3007-
if (out == NULL) {
3008-
return WS_MEMORY_E;
3009-
}
3010-
3011-
if (ret == WS_SUCCESS) {
3012-
SFTP_CreatePacket(ssh, WOLFSSH_FTP_HANDLE, out, outSz,
3013-
idFlat, sizeof(idFlat));
3014-
}
3015-
else {
3016-
if (wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_NOFILE, reqId,
3017-
"Unable To Open Directory", "English", out, &outSz)
3018-
!= WS_SUCCESS) {
3019-
WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER);
3020-
return WS_FATAL_ERROR;
3021-
}
3022-
}
2999+
SFTP_CreatePacket(ssh, WOLFSSH_FTP_HANDLE, out, outSz,
3000+
idFlat, sizeof(idFlat));
30233001

30243002
/* set send out buffer, "out" is taken by ssh */
30253003
wolfSSH_SFTP_RecvSetSend(ssh, out, outSz);
30263004

3027-
return ret;
3005+
return WS_SUCCESS;
30283006
}
30293007
#else /* USE_WINDOWS_API */
30303008
{
@@ -3037,9 +3015,10 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
30373015
int isDir = 0;
30383016
int ret = WS_SUCCESS;
30393017
int rc;
3018+
WS_DIR_LIST* cur;
30403019

30413020
word32 outSz = WOLFSSH_HANDLE_ID_SZ + WOLFSSH_SFTP_HEADER + UINT32_SZ;
3042-
byte* out = NULL;
3021+
byte* out;
30433022
byte idFlat[WOLFSSH_HANDLE_ID_SZ];
30443023
char name[MAX_PATH];
30453024
char clean[WOLFSSH_MAX_FILENAME];
@@ -3133,62 +3112,45 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
31333112
findHandle = (HANDLE)WS_FindFirstFileA(name,
31343113
realName, sizeof(realName), &isDir, ssh->ctx->heap);
31353114
if (findHandle == INVALID_HANDLE_VALUE || !isDir) {
3136-
31373115
WLOG(WS_LOG_SFTP, "Error with opening directory: %s", name);
3138-
WFREE(dirName, ssh->ctx->heap, DYNTYPE_BUFFER);
3139-
3140-
if (wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_NOFILE, reqId,
3141-
"Unable To Open Directory", "English", NULL, &outSz)
3142-
!= WS_SIZE_ONLY) {
3143-
return WS_FATAL_ERROR;
3144-
}
31453116
ret = WS_BAD_FILE_E;
31463117
}
31473118
if (findHandle != NULL && findHandle != INVALID_HANDLE_VALUE)
31483119
FindClose(findHandle);
3149-
}
3150-
3151-
WOLFSSH_UNUSED(reqId);
3152-
3153-
if (ret == WS_SUCCESS) {
3154-
WS_DIR_LIST* cur = (WS_DIR_LIST*)WMALLOC(sizeof(WS_DIR_LIST),
3155-
ssh->ctx->heap, DYNTYPE_SFTP);
3156-
if (cur == NULL) {
3120+
if (ret != WS_SUCCESS) {
31573121
WFREE(dirName, ssh->ctx->heap, DYNTYPE_BUFFER);
3158-
return WS_MEMORY_E;
3122+
rc = SFTP_SendStatus(ssh, WOLFSSH_FTP_NOFILE, reqId,
3123+
"Unable To Open Directory");
3124+
return (rc == WS_SUCCESS) ? ret : rc;
31593125
}
3160-
cur->dir = INVALID_HANDLE_VALUE;
3161-
SFTP_HandleIdNext(ssh, cur->id);
3162-
SFTP_HandleIdEncode(cur->id, idFlat);
3163-
cur->isEof = 0;
3164-
cur->dirName = dirName; /* take over ownership of buffer */
3165-
cur->next = ssh->dirList;
3166-
ssh->dirList = cur;
3167-
31683126
}
31693127

3128+
/* allocate the reply before publishing the handle so a failure leaves
3129+
* no handle the peer was never told about */
3130+
cur = (WS_DIR_LIST*)WMALLOC(sizeof(WS_DIR_LIST),
3131+
ssh->ctx->heap, DYNTYPE_SFTP);
31703132
out = (byte*)WMALLOC(outSz, ssh->ctx->heap, DYNTYPE_BUFFER);
3171-
if (out == NULL) {
3133+
if (cur == NULL || out == NULL) {
3134+
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
3135+
WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER);
3136+
WFREE(dirName, ssh->ctx->heap, DYNTYPE_BUFFER);
31723137
return WS_MEMORY_E;
31733138
}
3139+
cur->dir = INVALID_HANDLE_VALUE;
3140+
SFTP_HandleIdNext(ssh, cur->id);
3141+
SFTP_HandleIdEncode(cur->id, idFlat);
3142+
cur->isEof = 0;
3143+
cur->dirName = dirName; /* take over ownership of buffer */
3144+
cur->next = ssh->dirList;
3145+
ssh->dirList = cur;
31743146

3175-
if (ret == WS_SUCCESS) {
3176-
SFTP_CreatePacket(ssh, WOLFSSH_FTP_HANDLE, out, outSz,
3177-
idFlat, sizeof(idFlat));
3178-
}
3179-
else {
3180-
if (wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_NOFILE, reqId,
3181-
"Unable To Open Directory", "English", out, &outSz)
3182-
!= WS_SUCCESS) {
3183-
WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER);
3184-
return WS_FATAL_ERROR;
3185-
}
3186-
}
3147+
SFTP_CreatePacket(ssh, WOLFSSH_FTP_HANDLE, out, outSz,
3148+
idFlat, sizeof(idFlat));
31873149

31883150
/* set send out buffer, "out" is taken by ssh */
31893151
wolfSSH_SFTP_RecvSetSend(ssh, out, outSz);
31903152

3191-
return ret;
3153+
return WS_SUCCESS;
31923154
}
31933155
#endif /* USE_WINDOWS_API */
31943156

‎tests/regress.c‎

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17402,6 +17402,60 @@ static void TestSftpReadDirAllocFailure(void)
1740217402
wolfSSH_CTX_free(ctx);
1740317403
}
1740417404

17405+
/* An OPENDIR that cannot build its reply must not leave a directory handle
17406+
* the peer was never told about. Fail each allocation of one OPENDIR in turn:
17407+
* no handle is left open and nothing the request allocated is left behind. */
17408+
static void TestSftpOpenDirAllocFailure(void)
17409+
{
17410+
WOLFSSH_CTX* ctx;
17411+
WOLFSSH* ssh;
17412+
int rid = 900;
17413+
int allocs;
17414+
int nth;
17415+
int ret;
17416+
byte pkt[UINT32_SZ + 1];
17417+
word32 idx = 0;
17418+
byte handle[WOLFSSH_HANDLE_ID_SZ];
17419+
char cwd[WOLFSSH_MAX_FILENAME];
17420+
17421+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
17422+
AssertNotNull(ctx);
17423+
ssh = wolfSSH_new(ctx);
17424+
AssertNotNull(ssh);
17425+
AssertIntEQ(wolfSSH_SFTP_TestRecvStateInit(ssh), WS_SUCCESS);
17426+
17427+
WMEMSET(cwd, 0, sizeof(cwd));
17428+
AssertNotNull(WGETCWD(ssh->fs, cwd, sizeof(cwd) - 1));
17429+
AssertIntEQ(wolfSSH_SFTP_SetDefaultPath(ssh, cwd), WS_SUCCESS);
17430+
AssertIntEQ(wolfSSH_SFTP_SetConfinePath(ssh, cwd), WS_SUCCESS);
17431+
17432+
SftpPutU32(1, pkt + idx); idx += UINT32_SZ;
17433+
pkt[idx++] = '.';
17434+
17435+
/* count the allocations of a clean OPENDIR */
17436+
SftpFailAllocBegin(0);
17437+
ret = wolfSSH_SFTP_RecvOpenDir(ssh, rid++, pkt, idx);
17438+
SftpFailAllocEnd();
17439+
allocs = sftpAllocHits;
17440+
AssertIntEQ(ret, WS_SUCCESS);
17441+
WMEMCPY(handle, wolfSSH_SFTP_TestRecvReply(ssh, NULL)
17442+
+ SFTP_TEST_HANDLE_OFF, WOLFSSH_HANDLE_ID_SZ);
17443+
AssertIntEQ(SftpCloseHandle(ssh, rid++, handle), WS_SUCCESS);
17444+
AssertTrue(allocs > 1);
17445+
17446+
for (nth = 1; nth <= allocs; nth++) {
17447+
SftpFailAllocBegin(nth);
17448+
ret = wolfSSH_SFTP_RecvOpenDir(ssh, rid++, pkt, idx);
17449+
SftpFailAllocEnd();
17450+
AssertTrue(ret != WS_SUCCESS);
17451+
AssertIntEQ(wolfSSH_SFTP_TestDirHandleCount(ssh), 0);
17452+
AssertIntEQ(sftpAllocLive, 0);
17453+
}
17454+
17455+
wolfSSH_SFTP_TestRecvStateFree(ssh);
17456+
wolfSSH_free(ssh);
17457+
wolfSSH_CTX_free(ctx);
17458+
}
1740517459
#endif /* !WOLFSSL_STATIC_MEMORY && !WOLFSSL_DEBUG_MEMORY */
1740617460
#endif /* NO_WOLFSSH_DIR */
1740717461

@@ -21653,8 +21707,9 @@ int main(int argc, char** argv)
2165321707
/* an entry too long to stat is listed, not an error */
2165421708
TestSftpReadDirLongName();
2165521709
#if !defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_DEBUG_MEMORY)
21656-
/* an allocation failure in READDIR is an error, not EOF or a leak */
21710+
/* allocation failures in READDIR/OPENDIR are errors, not EOF or leaks */
2165721711
TestSftpReadDirAllocFailure();
21712+
TestSftpOpenDirAllocFailure();
2165821713
#endif
2165921714
#endif
2166021715
/* a failed close still drops the handle from the tracking list */

0 commit comments

Comments
 (0)