Repository navigation
fix(libc): fail freopen of an empty name with ENOENT on Windows - #9768
Conversation
freopen skipped path resolution for an empty file name and handed it to the host, so the MS CRT answered EINVAL where FreeBSD's freopen, which opens the name and keeps that errno (lib/libc/stdio/freopen.c lines 153, 162 and 196), gives ENOENT, as fopen of an empty name already does. An empty name now fails like an unresolvable one: ENOENT, the guest stream is closed and a host standard stream is left as it was.
freopen of an empty name on stdout fails with ENOENT as fopen of an empty name does, the guest stream is closed afterwards and the host standard output is still writable and still reaches its destination. Before the previous commit the call failed with EINVAL.
MarcZX10
left a comment
There was a problem hiding this comment.
The empty-name branch now follows the same cleanup path as a path-resolution failure, with ENOENT: ordinary guest streams close, while host-standard streams detach so the underlying host descriptor remains usable. The regression exercises the important host-standard case by redirecting descriptor 1, calling freopen("", ...), confirming the host write still reaches the original target, and then checking the guest stream reports EBADF. The negative control and neighboring stream tests strengthen the report.
Current-main merge-tree and diff-check are clean, and current-base conventions pass. The author reports focused Windows and Linux runs; the hosted product Linux/Windows jobs are skipped for this path. I did not run the full libc target locally because this checkout has no configured build and less than 1 GB free disk. No duplicate implementation or actionable code issue found.
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
|
Independent Windows run, as data only. Machine: Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X, WinLibs GCC 15.2.0 UCRT r7, Release, AI-assisted (Claude Code). |
MarcZX10
left a comment
There was a problem hiding this comment.
The author has now supplied an independent Windows run of guest_freopen_host_standard 3/3 on a combined tree with #9832 and #9793 atop current main 11f976b27c, without conflicts. The negative control and neighboring stream test results are also recorded. Current-head conventions and git diff --check pass; the current PR check set still skips product Linux/Windows builds. No new code issue found.
What
freopenwith an empty file name failed withEINVALon Windows where FreeBSD givesENOENT.freopen_nid_postfix(libc/src/FileAndHeap.cpp) resolved the path onlyif (*filename), so an empty name went toFileStream::Reopenand from there to the host:_wfreopen(L"", ...)in the MS CRT answersEINVAL. FreeBSD 11'sfreopen.copens the name (f = _open(file, oflags, DEFFILEMODE), line 153), keeps the errno (sverrno = errno, line 162), restores it after closing the stream (errno = sverrno, line 196) and returns NULL, so an empty name givesENOENTfromopen.fopenof an empty name already givesENOENTon Windows.An empty name now fails the way an unresolvable one does:
ENOENT, the guest stream is closed (a host standard stream is detached and the host stream is left as it was), andNULLis returned.freopen(NULL, ...)is unchanged: it was already refused withENOTSUPbefore this code.Tested
Windows 11, WinLibs GCC 15.2.0, Release, on
main8b3e39832a:guest_freopen_host_standard(new) passes 3 of 3. It redirects the host standard output to a file, callsfreopen("", "w", stdout), and checks that the result isNULLwithENOENT, that a host write to descriptor 1 still reaches the original destination, and that the guest stream is closed afterwards (fputsgives-1withEBADF, as inGuestStandardStreams).FileAndHeap.cppfrommainand the same test, it aborts at theENOENTcheck witherrno 22.guest_standard_streamswith its--closed-stdoutand--exclusive-stdoutforms,file_streams,guest_filesystem, the sixguest_file_lock_*tests,guest_append_streams,guest_append_writes,guest_fopen_long_host_pathandguest_host_standard_error_windows.python tools/check_conventions.py --base origin/mainpasses.Linux (Ubuntu 24.04 in WSL2, GCC 13.3.0, Release,
main3ca07d086): the new test passes 3/3, withguest_standard_streams,file_streamsandguest_filesystem. I did not run the new test without the fix on Linux, where the host already answersENOENT.Overlap: #8873 (
test(libc): check host stdout after freopen) tests the host handle after a successfulfreopen; this test covers the failing empty name and does not repeat it.tools/pr_overlap.py --branch(the version from #9539) reports nine pull requests as not compared because they conflict withmainthemselves; none of them changesfreopen_nid_postfix. #9527 (freopen_s) callsfreopen_nid_postfixand does not touch the guard.Not run on macOS.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)