diff --git a/src/port.c b/src/port.c index bfd3fdc17..1f39d1d2a 100644 --- a/src/port.c +++ b/src/port.c @@ -129,11 +129,15 @@ int wfopen(WFILE** f, const char* filename, const char* mode) int wPwrite(WFD fd, unsigned char* buf, unsigned int sz, const unsigned int* shortOffset) { + word64 offset; int ret = -1; - if (WFSEEK_SUCCESS(WFSEEK( - NULL, &fd, shortOffset[0], SYS_FS_SEEK_SET))) { - ret = (int)WFWRITE(NULL, buf, 1, sz, &fd); + if (wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET, + &offset) == 0) { + if (WFSEEK_SUCCESS(WFSEEK(NULL, &fd, (int32_t)offset, + SYS_FS_SEEK_SET))) { + ret = (int)WFWRITE(NULL, buf, 1, sz, &fd); + } } return ret; @@ -142,11 +146,16 @@ int wfopen(WFILE** f, const char* filename, const char* mode) int wPread(WFD fd, unsigned char* buf, unsigned int sz, const unsigned int* shortOffset) { + word64 offset; int ret = -1; - if (WFSEEK_SUCCESS(WFSEEK( - NULL, &fd, shortOffset[0], SYS_FS_SEEK_SET))) - ret = (int)WFREAD(NULL, buf, 1, sz, &fd); + if (wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET, + &offset) == 0) { + if (WFSEEK_SUCCESS(WFSEEK(NULL, &fd, (int32_t)offset, + SYS_FS_SEEK_SET))) { + ret = (int)WFREAD(NULL, buf, 1, sz, &fd); + } + } return ret; } @@ -643,12 +652,15 @@ int wssh_z_close(WFD fd) int wPwrite(WFD fd, unsigned char* buf, unsigned int sz, const unsigned int* shortOffset) { + word64 offset; int ret = -1; - if (fd >= 0 && fd < WOLFSSH_MAX_DESCIPRTORS) { + + if (fd >= 0 && fd < WOLFSSH_MAX_DESCIPRTORS && + wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET, + &offset) == 0) { if (wc_LockMutex(&z_fds_mutex) == 0) { if (z_fds[fd].open) { - const word32* offset = (const word32*)shortOffset; - if (fs_seek(&z_fds[fd].zfp, offset[0], FS_SEEK_SET) == 0) + if (fs_seek(&z_fds[fd].zfp, (off_t)offset, FS_SEEK_SET) == 0) ret = fs_write(&z_fds[fd].zfp, buf, sz); } wc_UnLockMutex(&z_fds_mutex); @@ -660,12 +672,15 @@ int wPwrite(WFD fd, unsigned char* buf, unsigned int sz, int wPread(WFD fd, unsigned char* buf, unsigned int sz, const unsigned int* shortOffset) { + word64 offset; int ret = -1; - if (fd >= 0 && fd < WOLFSSH_MAX_DESCIPRTORS) { + + if (fd >= 0 && fd < WOLFSSH_MAX_DESCIPRTORS && + wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET, + &offset) == 0) { if (wc_LockMutex(&z_fds_mutex) == 0) { if (z_fds[fd].open) { - const word32* offset = (const word32*)shortOffset; - if (fs_seek(&z_fds[fd].zfp, offset[0], FS_SEEK_SET) == 0) + if (fs_seek(&z_fds[fd].zfp, (off_t)offset, FS_SEEK_SET) == 0) ret = fs_read(&z_fds[fd].zfp, buf, sz); } wc_UnLockMutex(&z_fds_mutex); diff --git a/tests/unit.c b/tests/unit.c index cdd8d45d6..bae115503 100644 --- a/tests/unit.c +++ b/tests/unit.c @@ -94,6 +94,24 @@ #define WOLFSSH_TEST_PREAD_PWRITE #endif +/* A port whose seek type cannot reach 4 GiB rejects the offset instead of + * truncating it. Needs a mounted filesystem for the scratch file. */ +#if defined(WOLFSSH_ZEPHYR) && !defined(NO_FILESYSTEM) && \ + (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \ + !defined(NO_WOLFSSH_SERVER) && defined(CONFIG_WOLFSSH_SFTP_DEFAULT_DIR) && \ + defined(SIZEOF_OFF_T) && SIZEOF_OFF_T == 4 +#define WOLFSSH_TEST_PREAD_PWRITE_CEILING +#endif + +/* wResolveOffset() is defined whenever a port that does file transfers is + * built, so testing it directly needs no filesystem. */ +#if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP) || \ + defined(WOLFSSH_SSHD)) && \ + !(defined(NO_WOLFSSH_SERVER) && defined(NO_WOLFSSH_CLIENT)) && \ + (!defined(NO_FILESYSTEM) || defined(WOLFSSH_FATFS)) +#define WOLFSSH_TEST_RESOLVE_OFFSET +#endif + /* SendChannelTerminalRequest() reads the terminal settings of stdin, so the * no-tty case needs POSIX file descriptors to point stdin at /dev/null. */ #if defined(WOLFSSH_TEST_INTERNAL) && defined(WOLFSSH_TERM) && \ @@ -15939,6 +15957,130 @@ static int test_PreadPwriteHighOffset(void) #endif /* WOLFSSH_TEST_PREAD_PWRITE */ +#ifdef WOLFSSH_TEST_PREAD_PWRITE_CEILING +/* The offset arrives as two 32-bit words, low first. A port that seeks with + * a 32-bit type must reject anything the high word reaches, not wrap to the + * low word and read or write the wrong part of the file. */ +static int test_PreadPwriteOffsetCeiling(void) +{ + static const char contents[] = "0123456789ABCDEF"; + char name[64]; + unsigned char buf[8]; + unsigned int shortOffset[2]; + int result = 0; + WFD fd; + + WSNPRINTF(name, sizeof(name), "%s/%s", CONFIG_WOLFSSH_SFTP_DEFAULT_DIR, + "pofst.bin"); + + fd = WOPEN(NULL, name, WOLFSSH_O_RDWR | WOLFSSH_O_CREAT, 0); + if (fd < 0) + return -942; + + shortOffset[0] = 0; + shortOffset[1] = 0; + if (wPwrite(fd, (unsigned char*)contents, + (unsigned int)(sizeof(contents) - 1), shortOffset) + != (int)(sizeof(contents) - 1)) { + result = -943; + } + + /* An in-range offset must land where it points, not at the file start. */ + if (result == 0) { + shortOffset[0] = 8; + WMEMSET(buf, 0, sizeof(buf)); + if (wPread(fd, buf, (unsigned int)sizeof(buf), shortOffset) + != (int)sizeof(buf)) { + result = -947; + } + else if (WMEMCMP(buf, contents + 8, sizeof(buf)) != 0) { + result = -948; + } + } + + /* Exactly 4 GiB, so the offset lives only in the high word. Dropping it + * rereads the file start instead of failing the request. */ + shortOffset[0] = 0; + shortOffset[1] = 1; + + if (result == 0) { + WMEMSET(buf, 0, sizeof(buf)); + if (wPread(fd, buf, (unsigned int)sizeof(buf), shortOffset) >= 0) + result = -944; + } + + if (result == 0) { + if (wPwrite(fd, (unsigned char*)"Z", 1, shortOffset) >= 0) + result = -945; + } + + if (WCLOSE(NULL, fd) != 0 && result == 0) + result = -946; + (void)WREMOVE(NULL, name); + + return result; +} +#endif /* WOLFSSH_TEST_PREAD_PWRITE_CEILING */ + + +#ifdef WOLFSSH_TEST_RESOLVE_OFFSET +/* The ports pass their own seek-type ceiling, so check the boundary against + * an explicit maxOffset rather than whatever this build resolved to. */ +static int test_ResolveOffset(void) +{ + unsigned int shortOffset[2]; + word64 offset; + + /* a low word on its own arrives unchanged */ + shortOffset[0] = 0x100; + shortOffset[1] = 0; + offset = 1; + if (wResolveOffset(shortOffset, W64LIT(0x7FFFFFFF), &offset) != 0) + return -949; + if (offset != W64LIT(0x100)) + return -950; + + /* exactly at the ceiling is accepted */ + shortOffset[0] = 0x7FFFFFFF; + if (wResolveOffset(shortOffset, W64LIT(0x7FFFFFFF), &offset) != 0) + return -951; + if (offset != W64LIT(0x7FFFFFFF)) + return -952; + + /* one past it is not */ + shortOffset[0] = 0x80000000; + if (wResolveOffset(shortOffset, W64LIT(0x7FFFFFFF), &offset) == 0) + return -953; + + /* the high word must reach the result rather than be dropped */ + shortOffset[0] = 0; + shortOffset[1] = 1; + if (wResolveOffset(shortOffset, W64LIT(0x1FFFFFFFF), &offset) != 0) + return -954; + if (offset != W64LIT(0x100000000)) + return -955; + + /* both words together, still inside a wide ceiling */ + shortOffset[0] = 0x10; + if (wResolveOffset(shortOffset, W64LIT(0x1FFFFFFFF), &offset) != 0) + return -956; + if (offset != W64LIT(0x100000010)) + return -957; + + /* the same offset is refused by a 32-bit ceiling */ + if (wResolveOffset(shortOffset, W64LIT(0x7FFFFFFF), &offset) == 0) + return -958; + + if (wResolveOffset(NULL, W64LIT(0x7FFFFFFF), &offset) == 0) + return -959; + if (wResolveOffset(shortOffset, W64LIT(0x1FFFFFFFF), NULL) == 0) + return -960; + + return 0; +} +#endif /* WOLFSSH_TEST_RESOLVE_OFFSET */ + + int wolfSSH_UnitTest(int argc, char** argv) { int testResult = 0, unitResult = 0; @@ -16590,6 +16732,18 @@ int wolfSSH_UnitTest(int argc, char** argv) (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; #endif +#ifdef WOLFSSH_TEST_PREAD_PWRITE_CEILING + unitResult = test_PreadPwriteOffsetCeiling(); + printf("PreadPwriteOffsetCeiling: %s\n", + (unitResult == 0 ? "SUCCESS" : "FAILED")); + testResult = testResult || unitResult; +#endif +#ifdef WOLFSSH_TEST_RESOLVE_OFFSET + unitResult = test_ResolveOffset(); + printf("ResolveOffset: %s\n", + (unitResult == 0 ? "SUCCESS" : "FAILED")); + testResult = testResult || unitResult; +#endif #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 unitResult = test_OpenSshFormatNonCompositeRejected(); printf("OpenSshFormatNonCompositeRejected: %s\n", diff --git a/wolfssh/port.h b/wolfssh/port.h index 3a2166fa2..44e1f572f 100644 --- a/wolfssh/port.h +++ b/wolfssh/port.h @@ -1537,6 +1537,11 @@ extern "C" { /* Our "file descriptor" wrapper */ + /* SYS_FS_FileSeek takes an int32_t offset */ + #ifndef WOLFSSH_MAX_FILE_OFFSET + #define WOLFSSH_MAX_FILE_OFFSET W64LIT(0x7FFFFFFF) + #endif + #define WFD SYS_FS_HANDLE int wPwrite(WFD fd, unsigned char* buf, unsigned int sz, const unsigned int* shortOffset);