-
Notifications
You must be signed in to change notification settings - Fork 98
SFTP file handle abstraction #875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces a new file handle abstraction for the SFTP subsystem, replacing the old conditional WOLFSSH_STOREHANDLE mechanism with an always-on tracking system. Instead of sending raw file descriptors or handles to SFTP clients, the implementation now generates unique 8-byte IDs that are tracked internally in a linked list (WS_FILE_LIST), improving security and portability.
Changes:
- Removed old WOLFSSH_STOREHANDLE conditional compilation and associated functions (SFTP_AddHandleNode, SFTP_RemoveHandleNode, SFTP_GetHandleNode, SFTP_FreeHandles)
- Added new file handle tracking system with WS_FILE_LIST structure and helper functions (SFTP_AddFileHandle, SFTP_FindFileHandle, SFTP_RemoveFileHandle, SFTP_FreeAllFileHandles)
- Unified RecvClose implementation across platforms (previously had separate Unix and Windows versions)
- Updated all file operations (Open, Read, Write, Close, FSTAT, FSetSTAT) to use 8-byte handle IDs instead of raw file descriptors
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 14 comments.
| File | Description |
|---|---|
| wolfssh/wolfsftp.h | Removed declarations for old WOLFSSH_STOREHANDLE functions (SFTP_AddHandleNode, SFTP_RemoveHandleNode) |
| wolfssh/internal.h | Added WOLFSSH_HANDLE_ID_SZ constant, WS_FILE_LIST typedef, and fileIdCount/fileList members to WOLFSSH struct, removed conditional WOLFSSH_STOREHANDLE handleList member |
| src/wolfsftp.c | Implemented new file handle tracking system, updated all file operations to use 8-byte IDs, unified cross-platform RecvClose implementation, removed old WOLFSSH_STOREHANDLE code, fixed code style (else statement formatting) |
Comments suppressed due to low confidence (1)
src/wolfsftp.c:2184
- When SFTP_CreatePacket fails on line 2176, the file is closed (lines 2178-2182), but the file handle is not removed from the tracking list. This creates the same issue as with the malloc failure: the handle remains in ssh->fileList with an invalid file descriptor. SFTP_RemoveFileHandle should be called to clean up the tracking list before returning WS_FATAL_ERROR.
if (SFTP_CreatePacket(ssh, WOLFSSH_FTP_HANDLE, out, outSz,
idFlat, sizeof(idFlat)) != WS_SUCCESS) {
#ifdef MICROCHIP_MPLAB_HARMONY
WFCLOSE(ssh->fs, &fd);
#else
WCLOSE(ssh->fs, fd);
#endif
return WS_FATAL_ERROR;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| c32toa(id[1], idFlat + UINT32_SZ); | ||
| if (SFTP_CreatePacket(ssh, WOLFSSH_FTP_HANDLE, out, outSz, | ||
| (byte*)&fileHandle, sizeof(HANDLE)) != WS_SUCCESS) { | ||
| idFlat, sizeof(idFlat)) != WS_SUCCESS) { |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When SFTP_CreatePacket fails on line 2328, no cleanup is performed. The file handle remains in the tracking list and the file is not closed. This is similar to the Unix version's issue but lacks even the file close. CloseHandle should be called and SFTP_RemoveFileHandle should be invoked to clean up the tracking list before returning WS_FATAL_ERROR.
| idFlat, sizeof(idFlat)) != WS_SUCCESS) { | |
| idFlat, sizeof(idFlat)) != WS_SUCCESS) { | |
| /* Cleanup on packet creation failure: close and remove handle, | |
| and free buffer before returning a fatal error. */ | |
| CloseHandle(fileHandle); | |
| (void)SFTP_RemoveFileHandle(ssh, id); | |
| WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER); |
| /* close the file */ | ||
| WCLOSE(ssh->fs, toFree->fd); |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SFTP_FreeAllFileHandles function uses WCLOSE unconditionally, but it should handle different platforms appropriately. For Windows (USE_WINDOWS_API), it should use CloseHandle instead of WCLOSE. For MICROCHIP_MPLAB_HARMONY, it should use WFCLOSE. This inconsistency with the platform-specific close operations used elsewhere (e.g., lines 4307-4314) will cause compilation or runtime errors on Windows and potentially on MICROCHIP_MPLAB_HARMONY platforms.
| /* close the file */ | |
| WCLOSE(ssh->fs, toFree->fd); | |
| /* close the file */ | |
| #ifdef USE_WINDOWS_API | |
| CloseHandle(toFree->fd); | |
| #elif defined(MICROCHIP_MPLAB_HARMONY) | |
| WFCLOSE(ssh->fs, toFree->fd); | |
| #else | |
| WCLOSE(ssh->fs, toFree->fd); | |
| #endif |
| /* get offset into file */ | ||
| ato32(data + idx, &ofst[1]); idx += UINT32_SZ; |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The offset parsing on lines 3789-3791 happens even when ret != WS_SUCCESS (i.e., when the file handle lookup failed). This means idx is incremented even when the file handle is invalid. While this doesn't cause immediate harm since the code checks ret before using fd, it's inconsistent with the error handling pattern and could lead to confusing behavior. The offset and length parsing should be inside a ret == WS_SUCCESS check or moved after the handle validation succeeds.
| } | ||
|
|
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 3870 still contains a check for sz != sizeof(HANDLE) which is from the old implementation. This is redundant with the proper check on line 3879 and should be removed. Additionally, like the Unix version, the offset parsing on lines 3905-3912 happens even when ret != WS_SUCCESS (when file handle validation fails), which is inefficient and inconsistent.
| } | |
| } | |
| } | |
| if (ret == WS_SUCCESS) { |
| /* hold pointers to file handles */ | ||
| struct WS_FILE_LIST { | ||
| WFD fd; /* file descriptor */ | ||
| char* fileName; /* base name of file */ | ||
| word32 id[2]; /* handle ID */ | ||
| struct WS_FILE_LIST* next; | ||
| }; |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Windows, SFTP_AddFileHandle is being passed a HANDLE type (fileHandle on line 2309), but the WS_FILE_LIST structure stores this in a WFD field (line 2363 in diff), which is defined as int on Windows (wolfssh/port.h line 1318). HANDLE is a pointer type (void*) on Windows which is 64-bit on x64 systems, while int is 32-bit. This will cause truncation and data loss on 64-bit Windows systems, leading to invalid file handles being stored and subsequently used. The WS_FILE_LIST structure should use HANDLE on Windows platforms, or a union type that can accommodate both int file descriptors and HANDLE pointers.
| /* Close the file and remove from tracking list */ | ||
| CloseHandle(fileNode->fd); | ||
| #else | ||
| ret = WCLOSE(ssh->fs, fileNode->fd); | ||
| #endif | ||
| if (ret >= 0) { |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Windows (line 4311), CloseHandle is called but the return value is not checked. Unlike the Unix/MICROCHIP paths (lines 4308, 4313), this doesn't set ret which means the check on line 4315 will use an undefined/stale value of ret. The Windows path should set ret = WS_SUCCESS (or check CloseHandle's return value properly) before the check on line 4315.
| @@ -3903,8 +4125,43 @@ int wolfSSH_SFTP_RecvRead(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) | |||
| if (sz > maxSz - idx || sz > WOLFSSH_MAX_HANDLE || sz != sizeof(HANDLE)) { | |||
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 4125 still contains a check for sz != sizeof(HANDLE) which is from the old implementation where raw file handles were sent. With the new abstraction using 8-byte IDs, this check will fail because sizeof(HANDLE) is typically 4 or 8 bytes depending on platform but not necessarily equal to WOLFSSH_HANDLE_ID_SZ. This redundant check conflicts with the proper check on line 4130 and should be removed to avoid confusion and potential bugs.
| if (sz > maxSz - idx || sz > WOLFSSH_MAX_HANDLE || sz != sizeof(HANDLE)) { | |
| if (sz > maxSz - idx || sz > WOLFSSH_MAX_HANDLE) { |
| if (handleSz != (2 * sizeof(word32))) { | ||
| WLOG(WS_LOG_SFTP, "Unexpected handle size for stored handles"); | ||
| } | ||
| else { | ||
| WS_HANDLE_LIST* cur; | ||
| WS_FILE_LIST* cur; | ||
| word32 handleId[2] = {0, 0}; | ||
|
|
||
| cur = SFTP_GetHandleNode(ssh, handle, handleSz); | ||
| ato32(handle, &handleId[0]); | ||
| ato32(handle + UINT32_SZ, &handleId[1]); | ||
|
|
||
| cur = SFTP_FindFileHandle(ssh, handleId); | ||
| if (cur == NULL) { | ||
| WLOG(WS_LOG_SFTP, "Unknown handle"); | ||
| return WS_BAD_FILE_E; | ||
| } | ||
| name = cur->name; | ||
| name = cur->fileName; | ||
| } |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When handleSz != (2 * sizeof(word32)) on line 5270, the code logs a warning but continues execution without setting 'name'. This means the subsequent call to SFTP_GetAttributes_Handle on line 5290 will receive name=NULL (from its initialization on line 5248). This could lead to undefined behavior or incorrect results depending on how SFTP_GetAttributes_Handle handles NULL names. The code should either return an error when the handle size is wrong, or ensure that name is properly set through an alternative path.
| if (ret == WS_SUCCESS) { | ||
| WMEMSET((byte*)&fd, 0, sizeof(HANDLE)); | ||
| WMEMCPY((byte*)&fd, data + idx, sz); | ||
| idx += sz; | ||
| /* Validate file handle size - must be 8 bytes for tracked handles */ | ||
| if (sz != WOLFSSH_HANDLE_ID_SZ) { | ||
| WLOG(WS_LOG_SFTP, "Invalid file handle size - expected 8 bytes"); | ||
| res = err; | ||
| type = WOLFSSH_FTP_FAILURE; | ||
| ret = WS_BAD_FILE_E; | ||
| } | ||
| else { | ||
| word32 handle[2] = {0, 0}; | ||
| WS_FILE_LIST* fileEntry = NULL; | ||
|
|
||
| ato32(data + idx, &handle[0]); | ||
| ato32(data + idx + UINT32_SZ, &handle[1]); | ||
|
|
||
| fileEntry = SFTP_FindFileHandle(ssh, handle); | ||
| if (fileEntry == NULL) { | ||
| WLOG(WS_LOG_SFTP, "Invalid file handle - not found in session"); | ||
| res = err; | ||
| type = WOLFSSH_FTP_FAILURE; | ||
| ret = WS_BAD_FILE_E; | ||
| } | ||
| else { | ||
| fd = (HANDLE)fileEntry->fd; | ||
| } | ||
| idx += sz; | ||
| } |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The offset and length parsing (lines 3905-3919 outside changed region) happens unconditionally even when ret != WS_SUCCESS after the file handle validation fails. This is inefficient and could lead to using an uninitialized fd variable. The logic should guard operations with fd and parsing that depends on successful handle lookup with a ret == WS_SUCCESS check, similar to line 3799 in the Unix version.
| if (SFTP_AddFileHandle(ssh, fileHandle, dir, id) != WS_SUCCESS) { | ||
| WLOG(WS_LOG_SFTP, "Unable to store handle"); | ||
| res = ier; | ||
| if (wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_FAILURE, reqId, res, | ||
| "English", NULL, &outSz) != WS_SIZE_ONLY) { | ||
| return WS_FATAL_ERROR; | ||
| } | ||
| ret = WS_FATAL_ERROR; | ||
| } | ||
| } |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When SFTP_AddFileHandle fails on line 2309, the file handle is not closed in the case where wolfSSH_SFTP_CreateStatus returns WS_SIZE_ONLY (line 2313). This creates a resource leak on Windows. CloseHandle should be called before setting ret to WS_FATAL_ERROR on line 2316.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
ZD20562