Fix Issue 22614 - [Windows] tmpnam() in runPreprocessor generates roo…#22615
Conversation
|
Thanks for your pull request and interest in making D better, @divyansharma001! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.
|
There was a problem hiding this comment.
Pull request overview
This pull request fixes a permission issue on Windows where building druntime/phobos fails for non-admin users during ImportC file preprocessing. The root cause was that tmpnam() generates paths at the drive root (e.g., C:\sXXXXXX.), which non-admin users cannot write to. The fix replaces tmpnam() with Windows-specific APIs GetTempPathA and GetTempFileNameA that properly respect the user's TMP/TEMP environment variables.
Changes:
- Replaced
tmpnam(null)withGetTempPathAandGetTempFileNameAWindows APIs in the preprocessor temp file generation - Updated the temp file path generation to use proper Windows temp directory
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
compiler/src/dmd/link.d
Outdated
| GetTempPathA(260, tempDir.ptr); | ||
| GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr); |
There was a problem hiding this comment.
Missing error handling for GetTempPathA. This function returns 0 on failure, which should be checked. If GetTempPathA fails, tempDir will contain uninitialized data, leading to undefined behavior when passed to GetTempFileNameA. Check the return value and handle the error appropriately, similar to how other Windows API calls are handled in the codebase (see examples in common/file.d).
| GetTempPathA(260, tempDir.ptr); | |
| GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr); | |
| const auto tempPathLen = GetTempPathA(tempDir.length, tempDir.ptr); | |
| if (tempPathLen == 0 || tempPathLen >= tempDir.length) | |
| { | |
| eSink.error(loc, "unable to determine temporary directory for C preprocessor output"); | |
| return STATUS_FAILED; | |
| } | |
| const auto tempFileResult = GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr); | |
| if (tempFileResult == 0) | |
| { | |
| eSink.error(loc, "unable to create temporary file for C preprocessor output"); | |
| return STATUS_FAILED; | |
| } |
compiler/src/dmd/link.d
Outdated
| char[260] tempDir = void; // MAX_PATH | ||
| char[260] tempFile = void; | ||
| GetTempPathA(260, tempDir.ptr); | ||
| GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr); |
There was a problem hiding this comment.
Missing error handling for GetTempFileNameA. This function returns 0 on failure, which should be checked. If it fails, tempFile will contain uninitialized data, causing strlen to read past the buffer or produce incorrect results. Additionally, GetTempFileNameA creates a temporary file on disk that needs to be deleted when the function exits unsuccessfully. Check the return value and handle errors appropriately.
There was a problem hiding this comment.
Check return values, and if its an error return STATUS_FAILED.
compiler/src/dmd/link.d
Outdated
| GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr); | ||
| const(char)[] ifilename = tempFile[0 .. strlen(tempFile.ptr) + 1]; | ||
| ifilename = xarraydup(ifilename); |
There was a problem hiding this comment.
Resource leak: GetTempFileNameA creates a temporary file on disk. Early returns at lines 1076 and 1089 bypass the cleanup in returnResult (line 982), leaving the temporary file on disk. Additionally, the memory allocated by xarraydup at line 968 is also leaked. Consider adding cleanup logic using scope guards (scope(exit) or scope(failure)) to ensure the file and memory are always cleaned up on error paths.
|
It is deprecated, so swapping it out is a good thing. |
0acc9a3 to
9a14c10
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
9a14c10 to
22a997c
Compare
…t-drive paths, fails with permission denied for non-admin users
22a997c to
15b526b
Compare
|
Could you please take another look and approve the workflows so CI can run? |
compiler/src/dmd/link.d
Outdated
| char[260] tempDir = void; // MAX_PATH | ||
| char[260] tempFile = void; | ||
| GetTempPathA(260, tempDir.ptr); | ||
| GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr); |
There was a problem hiding this comment.
Check return values, and if its an error return STATUS_FAILED.
| const(char)[] ifilename = tmpname[0 .. strlen(tmpname) + 1]; | ||
| char[MAX_PATH] tempDir = void; | ||
| char[MAX_PATH] tempFile = void; | ||
| if (GetTempPathA(MAX_PATH, tempDir.ptr) == 0) |
There was a problem hiding this comment.
Sorry for the late review, but this should use the wide string API and convert the result to UTF8 to avoid bad encoding of the user home directory if it contains non-ascii characters.
Problem
On Windows, building druntime/phobos fails for non-admin users when DMD
preprocesses ImportC files (
.c). The error looks like:Root Cause
runPreprocessor()inlink.dusedtmpnam(null)to generate a tempfilename passed to
cl.exe's/Fiflag. On Windows, the MSVC CRTimplementation of
tmpnam()always returns paths in the format\sXXXXXX.(at the root of the current drive), regardless of the
TMP/TEMPenvironment variables. Non-admin users cannot write to the drive root
(
C:\), causingcl.exeto fail.Fix
Replace
tmpnam(null)withGetTempPathA+GetTempFileNameA, whichare the correct Windows APIs for generating temp file paths. These
respect the user's
TMP/TEMPenvironment variables and write toan accessible directory.
Tested
Confirmed by successfully building druntime and phobos on Windows 11
as a non-admin user after applying this fix.
Fixes #22614