Skip to content

Commit 6cc7f9e

Browse files
ssh.c: credit the channel window for the bytes a read consumes
- wolfSSH_stream_read() advances inputBuffer->idx before _UpdateChannelWindow(), records a non-success result in ssh->error, logs anything other than WS_WANT_WRITE, and reports the byte count. - tests/unit.c adds test_stream_read_deferredWindowAdjust(), which puts a full window of channel data and reads it back with an IO send that reports WS_CBIO_ERR_WANT_WRITE, checking the byte count, the payload, ssh->error, the credited window and the consumed buffer.
1 parent 581053b commit 6cc7f9e

2 files changed

Lines changed: 90 additions & 6 deletions

File tree

src/ssh.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,11 +1288,17 @@ int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz)
12881288
ret = WS_BUFFER_E;
12891289
else {
12901290
WMEMCPY(buf, inputBuffer->buffer + inputBuffer->idx, n);
1291+
inputBuffer->idx += n;
12911292
ret = _UpdateChannelWindow(ssh->channelList);
1292-
if (ret == WS_SUCCESS) {
1293-
inputBuffer->idx += n;
1294-
ret = n;
1293+
if (ret != WS_SUCCESS) {
1294+
ssh->error = ret;
1295+
if (ret != WS_WANT_WRITE) {
1296+
WLOG(WS_LOG_ERROR,
1297+
"wolfSSH_stream_read: window adjust send failed "
1298+
"(%d); read still succeeded", ret);
1299+
}
12951300
}
1301+
ret = n;
12961302
}
12971303
}
12981304

@@ -3800,6 +3806,8 @@ static int _UpdateChannelWindow(WOLFSSH_CHANNEL* channel)
38003806
}
38013807

38023808

3809+
/* Drains buffered channel data and credits the window for the bytes taken.
3810+
* Always reports the bytes copied */
38033811
static int _ChannelRead(WOLFSSH_CHANNEL* channel, byte* buf, word32 bufSz)
38043812
{
38053813
WOLFSSH_BUFFER* inputBuffer;
@@ -3814,10 +3822,18 @@ static int _ChannelRead(WOLFSSH_CHANNEL* channel, byte* buf, word32 bufSz)
38143822
inputBuffer->idx += bufSz;
38153823

38163824
updateResult = _UpdateChannelWindow(channel);
3817-
if (updateResult == WS_SUCCESS)
3818-
updateResult = bufSz;
3825+
if (updateResult != WS_SUCCESS) {
3826+
/* SendPacket() sets ssh->error only for WS_WANT_WRITE, so hard
3827+
* failures must be recorded here or they stay hidden. */
3828+
channel->ssh->error = updateResult;
3829+
if (updateResult != WS_WANT_WRITE) {
3830+
WLOG(WS_LOG_ERROR,
3831+
"_ChannelRead: window adjust send failed (%d); read still "
3832+
"succeeded", updateResult);
3833+
}
3834+
}
38193835

3820-
return updateResult;
3836+
return (int)bufSz;
38213837
}
38223838

38233839

tests/unit.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6063,6 +6063,69 @@ static int test_SendChannelData_zeroPeerMaxPacket(void)
60636063
return result;
60646064
}
60656065

6066+
/* wolfSSH_stream_read() counterpart of test_ChannelExtDataCreditWantWrite():
6067+
* a deferred credit must not cost the caller the bytes already consumed. */
6068+
static int test_stream_read_deferredWindowAdjust(void)
6069+
{
6070+
WOLFSSH_CTX* ctx = NULL;
6071+
WOLFSSH* ssh = NULL;
6072+
WOLFSSH_CHANNEL* ch = NULL;
6073+
int result = 0;
6074+
int ret;
6075+
byte in[64];
6076+
byte out[64];
6077+
word32 i;
6078+
6079+
for (i = 0; i < (word32)sizeof(in); i++) {
6080+
in[i] = (byte)i;
6081+
}
6082+
6083+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
6084+
if (ctx == NULL)
6085+
return -6980;
6086+
wolfSSH_SetIOSend(ctx, WantWriteIoSend);
6087+
6088+
ssh = wolfSSH_new(ctx);
6089+
if (ssh == NULL) { result = -6981; goto done; }
6090+
/* Allow MSGID_CHANNEL_WINDOW_ADJUST on this bare session. */
6091+
ssh->acceptState = ACCEPT_SERVER_USERAUTH_SENT;
6092+
6093+
/* A window the size of the payload, so draining it in one read leaves
6094+
* windowSz at zero and _UpdateChannelWindow() has to credit. */
6095+
ch = ChannelNew(ssh, ID_CHANTYPE_SESSION,
6096+
(word32)sizeof(in), DEFAULT_MAX_PACKET_SZ);
6097+
if (ch == NULL) { result = -6982; goto done; }
6098+
if (ChannelAppend(ssh, ch) != WS_SUCCESS) {
6099+
ChannelDelete(ch, ssh->ctx->heap);
6100+
result = -6983;
6101+
goto done;
6102+
}
6103+
ch->openConfirmed = 1;
6104+
6105+
if (wolfSSH_TestChannelPutData(ch, in, (word32)sizeof(in)) != WS_SUCCESS) {
6106+
result = -6984; goto done;
6107+
}
6108+
if (ch->windowSz != 0) { result = -6985; goto done; }
6109+
6110+
/* Every byte is reported, and they are the bytes that were put. */
6111+
ret = wolfSSH_stream_read(ssh, out, (word32)sizeof(out));
6112+
if (ret != (int)sizeof(in)) { result = -6990; goto done; }
6113+
if (WMEMCMP(out, in, sizeof(in)) != 0) { result = -6991; goto done; }
6114+
6115+
/* The deferral is observable, the window is credited locally, and the
6116+
* bytes are consumed rather than left for a re-read. */
6117+
if (ssh->error != WS_WANT_WRITE) { result = -6992; goto done; }
6118+
if (ch->windowSz != (word32)sizeof(in)) { result = -6993; goto done; }
6119+
if (ch->inputBuffer.length - ch->inputBuffer.idx != 0) {
6120+
result = -6994; goto done;
6121+
}
6122+
6123+
done:
6124+
wolfSSH_free(ssh);
6125+
wolfSSH_CTX_free(ctx);
6126+
return result;
6127+
}
6128+
60666129
/* BuildNameList() returns a C string. On an empty id list it must still
60676130
* terminate the buffer: SendKexInit() measures the result with WSTRLEN
60686131
* through AlgoListSz() and copies that many bytes into the KEXINIT. */
@@ -16400,6 +16463,11 @@ int wolfSSH_UnitTest(int argc, char** argv)
1640016463
(unitResult == 0 ? "SUCCESS" : "FAILED"));
1640116464
testResult = testResult || unitResult;
1640216465

16466+
unitResult = test_stream_read_deferredWindowAdjust();
16467+
printf("stream_read_deferredWindowAdjust: %s\n",
16468+
(unitResult == 0 ? "SUCCESS" : "FAILED"));
16469+
testResult = testResult || unitResult;
16470+
1640316471
unitResult = test_BuildNameList_emptySrc();
1640416472
printf("BuildNameList_emptySrc: %s\n",
1640516473
(unitResult == 0 ? "SUCCESS" : "FAILED"));

0 commit comments

Comments
 (0)