Buffer client SFTP VERSION header across partial reads - #1138
Buffer client SFTP VERSION header across partial reads#1138yosuke-wolfssl wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a client-side SFTP negotiation bug where the 9-byte SSH_FXP_VERSION header could be partially read, consumed, and then lost across retries—preventing recovery on WS_WANT_READ/partial delivery. The client negotiation path is updated to buffer the VERSION message across calls, matching the server-side buffering approach already used elsewhere in the codebase.
Changes:
- Reworks client VERSION receive (
SFTP_ClientRecvInit) to accumulate header and extension bytes using the reusable SFTP buffer/state mechanism instead of a stack buffer and single-shot reads. - Updates
wolfSSH_SFTP_connect()to preserve buffered negotiation state on retryable conditions (viaNoticeError()), and to clear the recv-init state on success. - Adds unit tests covering split VERSION delivery and various VERSION validation outcomes; removes the now-dead
sftpExtSzfield from the internal session struct.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/wolfsftp.c |
Buffers client-side VERSION header/extensions across partial reads and adjusts connect cleanup semantics to preserve retryable state. |
tests/unit.c |
Adds unit tests for split VERSION delivery and VERSION validation/teardown behavior. |
wolfssh/internal.h |
Removes unused internal sftpExtSz field now that client extensions are buffered via recv-init state. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3396c9a to
05b1073
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1138
Scan targets checked: wolfssh-bugs, wolfssh-src
Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
05b1073 to
713180a
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1138
Scan targets checked: wolfssh-bugs, wolfssh-src
No new issues found in the changed files. ✅
Problem
SFTP_ClientRecvInit()read the 9-byte SFTP VERSION header into a stack buffer and required all 9 bytes from a singlewolfSSH_stream_read():A short-but-positive read consumed those bytes off the channel and discarded them — nothing recorded an offset.
wolfSSH_SFTP_connect()collapsed the result toWS_FATAL_ERRORwhile leavingssh->sftpStateatSFTP_RECV, so a retry parsed the remaining header bytes as a new packet and negotiation could not recover.wolfSSH_stream_read()also leavesssh->erroratWS_SUCCESSon a short read, so the standardWS_WANT_READretry loop treated it as fatal and gave up. Triggered by any server whose VERSION message arrives split across SSH channel-data messages, or a non-blocking read that returns partway.Fix (
src/wolfsftp.c)The server path already solved this; the client is now the same shape.
SFTP_ClientRecvInit()accumulates intossh->recvInitState->bufferviawolfSSH_SFTP_buffer_read(), which retainsbuffer->idxacross calls and reportsWS_WANT_READuntil complete. TheSFTP_EXTarm went the same way, replacing its per-callWMALLOC/discard logic.wolfSSH_SFTP_connect()gates cleanup onNoticeError()likewolfSSH_SFTP_accept()does: the partial buffer survives a retryable status, is freed otherwise, andSTATE_ID_RECV_INITis cleared on success. Return value staysWS_FATAL_ERROR— no API change.wolfssh/internal.h:sftpExtSzwas the client path's only user and is now dead; removed (noinstheader, not public ABI).This closes f-7505.
Tests (
tests/unit.c)SftpClientRecvInitSplitdelivers a VERSION message in two pieces, split once inside the header and once inside the trailing extension data.SftpClientRecvInitVersioncovers the four single-read outcomes: bad declared size, wrong message type, version below v3 (all must fail and release the buffered state), and version above v3 (must still negotiate).Verification
make check: 10 passed, 1 skipped, 0 failed.-Werrorpreflight clean across 6 configs; ASan + UBSan clean onunit.testandtestsuite.test.<=and!=) each fail the corresponding test.