Skip to content

Fix Issue 22614 - [Windows] tmpnam() in runPreprocessor generates roo…#22615

Merged
thewilsonator merged 2 commits intodlang:stablefrom
divyansharma001:fix-22614-windows-importc-tmpnam-permission-denied
Mar 2, 2026
Merged

Fix Issue 22614 - [Windows] tmpnam() in runPreprocessor generates roo…#22615
thewilsonator merged 2 commits intodlang:stablefrom
divyansharma001:fix-22614-windows-importc-tmpnam-permission-denied

Conversation

@divyansharma001
Copy link

Problem

On Windows, building druntime/phobos fails for non-admin users when DMD
preprocesses ImportC files (.c). The error looks like:

src\core\stdc\errno.c: fatal error C1083: Cannot open compiler generated
file: '\sla8.': Permission denied

Root Cause

runPreprocessor() in link.d used tmpnam(null) to generate a temp
filename passed to cl.exe's /Fi flag. On Windows, the MSVC CRT
implementation of tmpnam() always returns paths in the format \sXXXXXX.
(at the root of the current drive), regardless of the TMP/TEMP
environment variables. Non-admin users cannot write to the drive root
(C:\), causing cl.exe to fail.

Fix

Replace tmpnam(null) with GetTempPathA + GetTempFileNameA, which
are the correct Windows APIs for generating temp file paths. These
respect the user's TMP/TEMP environment variables and write to
an accessible directory.

Tested

Confirmed by successfully building druntime and phobos on Windows 11
as a non-admin user after applying this fix.

Fixes #22614

Copilot AI review requested due to automatic review settings February 22, 2026 21:35
@dlang-bot
Copy link
Contributor

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 verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

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 references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

⚠️⚠️⚠️ Warnings ⚠️⚠️⚠️

  • In preparation for migrating from Bugzilla to GitHub Issues, the issue reference syntax has changed. Please add the word "Bugzilla" to issue references. For example, Fix Bugzilla Issue 12345 or Fix Bugzilla 12345.(Reminder: the edit needs to be done in the Git commit message, not the GitHub pull request.)

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "stable + dmd#22615"

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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) with GetTempPathA and GetTempFileNameA Windows 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.

Comment on lines +965 to +966
GetTempPathA(260, tempDir.ptr);
GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr);
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

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).

Suggested change
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;
}

Copilot uses AI. Check for mistakes.
char[260] tempDir = void; // MAX_PATH
char[260] tempFile = void;
GetTempPathA(260, tempDir.ptr);
GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr);
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

Check return values, and if its an error return STATUS_FAILED.

Comment on lines 966 to 968
GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr);
const(char)[] ifilename = tempFile[0 .. strlen(tempFile.ptr) + 1];
ifilename = xarraydup(ifilename);
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
@rikkimax
Copy link
Contributor

tmpnam doesn't give you a file name against root drive, its against the temporary directory, only they don't prepend it.

It is deprecated, so swapping it out is a good thing.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

@divyansharma001 divyansharma001 force-pushed the fix-22614-windows-importc-tmpnam-permission-denied branch from 9a14c10 to 22a997c Compare February 23, 2026 17:49
…t-drive paths, fails with permission denied for non-admin users
@divyansharma001 divyansharma001 force-pushed the fix-22614-windows-importc-tmpnam-permission-denied branch from 22a997c to 15b526b Compare February 23, 2026 17:52
@divyansharma001
Copy link
Author

Could you please take another look and approve the workflows so CI can run?

char[260] tempDir = void; // MAX_PATH
char[260] tempFile = void;
GetTempPathA(260, tempDir.ptr);
GetTempFileNameA(tempDir.ptr, "dmd", 0, tempFile.ptr);
Copy link
Contributor

Choose a reason for hiding this comment

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

Check return values, and if its an error return STATUS_FAILED.

@thewilsonator thewilsonator dismissed rikkimax’s stale review March 2, 2026 05:24

Now returns STATUS_FAILED

@thewilsonator thewilsonator merged commit 6da2d9e into dlang:stable Mar 2, 2026
77 of 78 checks passed
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)
Copy link
Member

Choose a reason for hiding this comment

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

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.

Copy link
Author

Choose a reason for hiding this comment

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

I'll do that.

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.

6 participants