Skip to content

Commit 65e417d

Browse files
wolfsftp: buffer the SFTP DATA length across partial reads
- STATE_SEND_READ_FTP_DATA reads the four byte string length through wolfSSH_SFTP_buffer_read() into state->buffer and decodes it with ato32() from that buffer. - The szFlat stack array is removed from wolfSSH_SFTP_SendReadPacket(). - tests/unit.c gains test_SftpSendReadPacketSplit(), with the SftpBuildData() and SftpClientDriveReadSplit() helpers, driving a DATA reply split inside the length prefix through wolfSSH_SFTP_SendReadPacket(). Issue: F-8828
1 parent f530d32 commit 65e417d

2 files changed

Lines changed: 144 additions & 4 deletions

File tree

src/wolfsftp.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8423,7 +8423,6 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
84238423
const word32* ofst, byte* out, word32 outSz)
84248424
{
84258425
WS_SFTP_SEND_READ_STATE* state = NULL;
8426-
byte szFlat[UINT32_SZ];
84278426
int ret = WS_SUCCESS;
84288427
word32 sz;
84298428

@@ -8558,17 +8557,29 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
85588557

85598558
case STATE_SEND_READ_FTP_DATA:
85608559
WLOG(WS_LOG_SFTP, "SFTP SEND_READ STATE: FTP_DATA");
8560+
/* The string length can arrive split over several reads */
8561+
ret = wolfSSH_SFTP_buffer_create(ssh, &state->buffer,
8562+
UINT32_SZ);
8563+
if (ret != WS_SUCCESS) {
8564+
state->state = STATE_SEND_READ_CLEANUP;
8565+
continue;
8566+
}
8567+
85618568
/* get size of string and place it into out buffer */
8562-
ret = wolfSSH_stream_read(ssh, szFlat, UINT32_SZ);
8569+
ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer, UINT32_SZ);
85638570
if (ret < 0) {
85648571
if (NoticeError(ssh)) {
85658572
return WS_FATAL_ERROR;
85668573
}
85678574
state->state = STATE_SEND_READ_CLEANUP;
85688575
continue;
85698576
}
8570-
ato32(szFlat, &sz);
8571-
wolfSSH_SFTP_buffer_create(ssh, &state->buffer, sz);
8577+
ato32(wolfSSH_SFTP_buffer_data(&state->buffer), &sz);
8578+
ret = wolfSSH_SFTP_buffer_create(ssh, &state->buffer, sz);
8579+
if (ret != WS_SUCCESS) {
8580+
state->state = STATE_SEND_READ_CLEANUP;
8581+
continue;
8582+
}
85728583
if (wolfSSH_SFTP_buffer_size(&state->buffer) > outSz) {
85738584
WLOG(WS_LOG_SFTP, "Server sent more data then expected");
85748585
ret = WS_FATAL_ERROR;

tests/unit.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15449,6 +15449,130 @@ static int test_SftpClientRecvInitVersion(void)
1544915449

1545015450
return 0;
1545115451
}
15452+
15453+
/* Builds an SFTP DATA reply in "out": the 9 byte header carrying the request
15454+
* id where a VERSION message carries the version, then the data string as the
15455+
* trailing bytes. Returns the size written, or 0 if it does not fit. */
15456+
static word32 SftpBuildData(byte* out, word32 outSz, word32 reqId,
15457+
word32 dataSz)
15458+
{
15459+
word32 msgSz;
15460+
15461+
msgSz = SftpBuildVersion(out, outSz,
15462+
MSG_ID_SZ + UINT32_SZ + UINT32_SZ + dataSz, WOLFSSH_FTP_DATA,
15463+
reqId, UINT32_SZ + dataSz);
15464+
if (msgSz > 0) {
15465+
/* the trailing bytes open with the data string length */
15466+
PutU32BE(out + WOLFSSH_SFTP_HEADER, dataSz);
15467+
}
15468+
15469+
return msgSz;
15470+
}
15471+
15472+
15473+
/* Drives wolfSSH_SFTP_SendReadPacket() over a DATA reply delivered in two
15474+
* pieces, split after "split" bytes, with RecvAlwaysWantRead standing in for a
15475+
* non-blocking socket. Returns 0, or a negative sentinel on a setup failure. */
15476+
static int SftpClientDriveReadSplit(word32 dataSz, word32 split,
15477+
int* firstRet, int* firstErr, int* stateKept, int* secondRet,
15478+
byte* out, word32 outSz)
15479+
{
15480+
WOLFSSH_CTX* ctx = NULL;
15481+
WOLFSSH* ssh = NULL;
15482+
byte msg[WOLFSSH_SFTP_HEADER + UINT32_SZ + 32];
15483+
byte handle[4];
15484+
word32 ofst[2];
15485+
word32 msgSz;
15486+
int result;
15487+
15488+
*firstRet = WS_SUCCESS;
15489+
*firstErr = WS_SUCCESS;
15490+
*stateKept = 0;
15491+
*secondRet = WS_SUCCESS;
15492+
15493+
WMEMSET(handle, 'h', sizeof(handle));
15494+
WMEMSET(out, 0, outSz);
15495+
ofst[0] = 0;
15496+
ofst[1] = 0;
15497+
15498+
result = SftpClientNewSession(&ctx, &ssh);
15499+
if (result == 0) {
15500+
msgSz = SftpBuildData(msg, (word32)sizeof(msg), ssh->reqId, dataSz);
15501+
if (msgSz == 0 || split >= msgSz) {
15502+
result = -1019;
15503+
}
15504+
}
15505+
if (result == 0) {
15506+
/* ChannelNew leaves the peer window at zero, which would fail the
15507+
* READ request before any reply is read */
15508+
ssh->channelList->peerWindowSz = 1024;
15509+
ssh->channelList->peerMaxPacketSz = 1024;
15510+
15511+
if (wolfSSH_TestChannelPutData(ssh->channelList, msg, split)
15512+
!= WS_SUCCESS) {
15513+
result = -1020;
15514+
}
15515+
}
15516+
if (result == 0) {
15517+
*firstRet = wolfSSH_SFTP_SendReadPacket(ssh, handle,
15518+
(word32)sizeof(handle), ofst, out, outSz);
15519+
*firstErr = wolfSSH_get_error(ssh);
15520+
*stateKept = (ssh->sendReadState != NULL);
15521+
15522+
if (wolfSSH_TestChannelPutData(ssh->channelList, msg + split,
15523+
msgSz - split) != WS_SUCCESS) {
15524+
result = -1021;
15525+
}
15526+
}
15527+
if (result == 0) {
15528+
*secondRet = wolfSSH_SFTP_SendReadPacket(ssh, handle,
15529+
(word32)sizeof(handle), ofst, out, outSz);
15530+
}
15531+
15532+
wolfSSH_free(ssh);
15533+
wolfSSH_CTX_free(ctx);
15534+
return result;
15535+
}
15536+
15537+
15538+
/* Regression for the SFTP DATA reply losing bytes when its four byte string
15539+
* length arrives split: the read must report WS_WANT_READ, keep the send read
15540+
* state, then complete with the payload intact on the retry. */
15541+
static int test_SftpSendReadPacketSplit(void)
15542+
{
15543+
static const word32 splits[2] = { WOLFSSH_SFTP_HEADER + 1,
15544+
WOLFSSH_SFTP_HEADER + 2 };
15545+
byte out[16];
15546+
word32 dataSz = 8;
15547+
word32 i;
15548+
word32 j;
15549+
int rc;
15550+
int firstRet;
15551+
int firstErr;
15552+
int stateKept;
15553+
int secondRet;
15554+
15555+
for (i = 0; i < (word32)(sizeof(splits) / sizeof(splits[0])); i++) {
15556+
rc = SftpClientDriveReadSplit(dataSz, splits[i], &firstRet, &firstErr,
15557+
&stateKept, &secondRet, out, (word32)sizeof(out));
15558+
if (rc != 0)
15559+
return rc;
15560+
if (firstRet != WS_FATAL_ERROR)
15561+
return -981;
15562+
if (firstErr != WS_WANT_READ)
15563+
return -982;
15564+
if (!stateKept)
15565+
return -983;
15566+
if (secondRet != (int)dataSz)
15567+
return -984;
15568+
for (j = 0; j < dataSz; j++) {
15569+
if (out[j] != (byte)(UINT32_SZ + j))
15570+
return -985;
15571+
}
15572+
}
15573+
15574+
return 0;
15575+
}
1545215576
#endif /* NO_WOLFSSH_CLIENT */
1545315577
#endif /* WOLFSSH_SFTP */
1545415578

@@ -16567,6 +16691,11 @@ int wolfSSH_UnitTest(int argc, char** argv)
1656716691
printf("SftpClientRecvInitVersion: %s\n",
1656816692
(unitResult == 0 ? "SUCCESS" : "FAILED"));
1656916693
testResult = testResult || unitResult;
16694+
16695+
unitResult = test_SftpSendReadPacketSplit();
16696+
printf("SftpSendReadPacketSplit: %s\n",
16697+
(unitResult == 0 ? "SUCCESS" : "FAILED"));
16698+
testResult = testResult || unitResult;
1657016699
#endif
1657116700
#endif
1657216701

0 commit comments

Comments
 (0)