win: fs stat/realpath bounds and tty exact-fill line-read correctness fixes

Merged
RO
4mo ago
+304-14

Cherry-picks of the plain-Windows correctness fixes from the closed #5, split out from the AppContainer-specific changes. These trigger on every Windows build, not just inside a sandbox.

fs

  • fs__realpath_handle: preserve the real error when the second GetFinalPathNameByHandleW call fails instead of overwriting it with ERROR_INVALID_HANDLE (surfaced as EBADF).
  • fs__realpath: capture GetLastError() before the CloseHandle() that can clobber it.
  • fs__stat_directory (the ACCESS_DENIED fallback that reads the parent's directory listing): don't read path[-1] on all-dot relative paths; don't write an uninitialized WCHAR back over the path's last character; keep the root backslash when splitting X:\\name, \\\\?\\X:\\name, \\\\.\\X:\\name, and rooted \\name so FindFirstFileW searches the right directory. Before this, uv_fs_stat("C:\\pagefile.sys") reported ENOENT (or the stats of a same-named file in the cwd) because the split left a drive-relative C: to search.

tty

  • Backport upstream ec0ab5d7 (GHSA-4prr-4742-3ccf): reserve one byte for the NUL terminator in the uv_utf16_to_wtf8 line-read path so an exact-fill conversion doesn't write one byte past the allocation. This bun branch is on f3ce527e, which predates that fix.
  • Size the ReadConsoleW request to (bytes - 1) / 3 code units so the worst-case conversion always fits the reserved buffer; otherwise an exact-fill read is delivered torn with an embedded NUL and nread reporting the required (larger) length.
  • Report UV_ENOBUFS instead of a successful zero-byte read when the allocation is too small to hold even one converted character; the old behaviour silently consumed console input forever.

Tests

fs_stat_locked_root_file (stats C:\\pagefile.sys through the drive-absolute, \\\\?\\-prefixed, and rooted spellings with a same-named decoy in the cwd), tty_line_read_exact_fill (6-byte allocation, two 3-byte characters; ASAN-sized so the old one-byte overflow is out of bounds), tty_line_read_enobufs, and utf16_to_wtf8_exact_fill (direct converter bounds regression).

Verified on Windows Server 2019 x64: all targets build, ctest -C RelWithDebInfo passes 2/2 (100%), and the four new tests pass under MSVC ASAN with no sanitizer reports.

Not included from #5 (tracked separately): the console-read cancellation fallback, uv_pipe_bind EACCES/EADDRINUSE disambiguation, the uv_pipe() error-code mapping, and the Global-prefixed junction readlink.