Skip to content

port, scp: distinguish end of directory from enumeration failure - #1242

Merged
ejohnstown merged 2 commits into
wolfSSL:masterfrom
yosuke-wolfssl:fix/f_13316
Sep 8, 2026
Merged

port, scp: distinguish end of directory from enumeration failure#1242
ejohnstown merged 2 commits into
wolfSSL:masterfrom
yosuke-wolfssl:fix/f_13316

Conversation

@yosuke-wolfssl

Copy link
Copy Markdown
Contributor

Problem

WS_FindNextFileA() returns 0 for both a real enumeration failure and the normal ERROR_NO_MORE_FILES end-of-directory. FindNextDirEntry() mapped both to WS_FATAL_ERROR, which matches neither arm of the SCP send callback's WS_SUCCESS || WS_NEXT_ERROR check, so the directory was never popped and the callback aborted the session instead.

Any scp -r against a server built with USE_WINDOWS_API + WOLFSSH_SCP truncates silently: entries enumerated before the first end-of-directory arrive, everything after is lost, and the only symptom is a partial tree. Recursive SCP send on Windows could never complete. POSIX builds are unaffected.

Fix (src/port.c)

  • WS_FindNextFileA_ex() carries the body and adds a lastError out-parameter.
  • WS_FindNextFileA() becomes a wrapper passing NULL, so its signature and WOLFSSH_API tag are unchanged and the exported symbol keeps its contract.
lastError on a 0 return Meaning
ERROR_NO_MORE_FILES end of directory
ERROR_NO_UNICODE_TRANSLATION wcstombs_s() could not convert the name
otherwise the FindNextFileW() error

The value travels through an out-parameter rather than the return code because the return is boolean and 0 already means failure, which is the value every caller tests.

In src/wolfscp.c, FindNextDirEntry() translates ERROR_NO_MORE_FILES to WS_NEXT_ERROR and clears ctx->entry, so the caller's existing entry == NULL test pops the directory. Every other error still returns WS_FATAL_ERROR.

Closes f-13316.

Tests (.github/workflows/windows-sftp.yml)

Windows CI already compiled this code but never ran it, and scp-test.yml is Linux only. A new step pulls a tree of two files, a nested subdirectory and an empty one, then fails unless all of it arrives.

  • -O keeps OpenSSH on the legacy SCP protocol; without it OpenSSH 9 and later transfer over SFTP and never reach this code.
  • The assertion is the tree, not the exit status, because the bug truncates rather than failing cleanly.
  • The empty subdirectory is the tightest case: end of directory immediately after . and ...

Verification

  • GCC preflight clean across 6 configs; make check 8 passed, 3 skipped, 0 failed.
  • MSVC Debug x64 clean, no new warnings.
  • Manual scp -O -r against echoserver on Windows 11: full tree, exit status 0.
  • Negative control with the fix reverted: aborts inside the empty directory, nested/gamma.txt never transfers.

Not covered: the two failure sentinels above are reachable only on a genuine Win32 error, which a test cannot force.

- WS_FindNextFileA_ex() takes a lastError out-parameter and carries
  the body of WS_FindNextFileA(), which now calls it with NULL. A
  failing return reports the FindNextFileW() error, or
  ERROR_NO_UNICODE_TRANSLATION when the name would not convert.
- FindNextDirEntry() calls WS_FindNextFileA_ex() on Windows. An
  ERROR_NO_MORE_FILES result frees ctx->entry, sets it to NULL and
  returns WS_NEXT_ERROR so the caller pops the directory; every other
  error still returns WS_FATAL_ERROR.

Issue: F-13316
- windows-sftp.yml generates an ecdsa key for testuser, authorizes it,
  and builds a source tree holding two files, a nested subdirectory
  and an empty one.
- A new step pulls that tree with scp -O -r and fails unless every
  file and directory arrives. -O keeps OpenSSH on the legacy SCP
  protocol rather than SFTP.
- The header comment lists the recursive SCP scenario.
@yosuke-wolfssl yosuke-wolfssl self-assigned this Sep 8, 2026
Copilot AI lite review requested due to automatic review settings September 8, 2026 03:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The functional change correctly resolves the Windows SCP end-of-directory handling and is backed by a targeted CI regression test (only a minor doc nit noted).

Pull request overview

Fixes Windows recursive SCP (scp -r) directory enumeration by distinguishing “end of directory” (ERROR_NO_MORE_FILES) from real enumeration failures, preventing premature aborts/truncation in USE_WINDOWS_API builds.

Changes:

  • Added WS_FindNextFileA_ex() to return GetLastError()-style detail via an out-parameter while keeping WS_FindNextFileA()’s public API unchanged.
  • Updated Windows FindNextDirEntry() to translate ERROR_NO_MORE_FILES into WS_NEXT_ERROR and clear ctx->entry so directory pop logic executes.
  • Extended Windows CI workflow to exercise legacy-protocol recursive SCP (scp -O -r) and assert the full tree arrives (including an empty subdir).
File summaries
File Description
wolfssh/port.h Declares internal WS_FindNextFileA_ex() API with lastError out-parameter.
src/port.c Implements WS_FindNextFileA_ex() and keeps WS_FindNextFileA() as a wrapper for ABI stability.
src/wolfscp.c Maps end-of-directory to WS_NEXT_ERROR on Windows so SCP send logic pops the directory instead of aborting.
.github/workflows/windows-sftp.yml Adds a Windows recursive SCP regression test using scp -O -r and verifies the transferred tree.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread wolfssh/port.h

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #1242

Scan targets checked: wolfssh-bugs, wolfssh-src

Fenrir result: Approved ✅

No new issues found in the changed files.

Advisory only — this automated result does not count as a GitHub approval.

@ejohnstown
ejohnstown merged commit dee02b2 into wolfSSL:master Sep 8, 2026
183 checks passed
@yosuke-wolfssl
yosuke-wolfssl deleted the fix/f_13316 branch September 8, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants