Skip to content

dotnet --info crashes when DOTNET_ environment variable contains %-string #119566

@nike4613

Description

@nike4613

Description

dotnet --info crashes in hostfxr when printing environment variables if a variable contains a %-string, when stdout is redirected to a file on Windows.

Reproduction Steps

  1. Set a DOTNET_* environment variable to a value which contains a %-specifier.

    For instance, set DOTNET_DbgMiniDumpName=dump_%e_%p.dmp. (Interestingly, the CoreCLR crash handler is actually invoked and generates a crash dump for this.)

  2. Run dotnet --info > file.txt

Expected behavior

No crash, and environment variables are printed.

Actual behavior

Host crashes.

Example failing case: https://github.com/MonoMod/MonoMod/actions/runs/17620993089/job/50066937459?pr=258

Example passing case (running .NET 9.0.9): https://github.com/MonoMod/MonoMod/actions/runs/17620993089/job/50066937838?pr=258

Regression?

This worked in 9.0.9 and before; it seems to be new in 10.0.0-rc.1 hostfxr.

Known Workarounds

No response

Configuration

.NET 10.0.0-rc.1.25451.107

Windows-specific, does not appear to be architecture-specific

Other information

The issue appears to be here:

if (isConsoleOutput == FALSE)
{
// We use file_vprintf to handle UTF-8 formatting. The WriteFile api will output the bytes directly with Unicode bytes,
// while pal::file_vprintf will convert the characters to UTF-8.
pal::file_vprintf(fallbackFileHandle, message, va_list());
}

message here is already formatted to contain the correct string, but it's being re-formatted by the underlying vfwprintf call, but this time with an empty va_list.

I believe the following patch would fix the error:

 namespace
 {
+    void file_printf(FILE* fallbackFileHandle, const pal::char_t* format, ...)
+    {
+        va_list args;
+        va_start(args, format);
+        pal::file_vprintf(fallbackFileHandle, format, args);
+        va_end(args);
+    }
+
     void print_line_to_handle(const pal::char_t* message, HANDLE handle, FILE* fallbackFileHandle) {
         // String functions like vfwprintf convert wide to multi-byte characters as if wcrtomb were called - that is, using the current C locale (LC_TYPE).
         // In order to properly print UTF-8 and GB18030 characters to the console without requiring the user to use chcp to a compatible locale, we use WriteConsoleW.
         // However, WriteConsoleW will fail if the output is redirected to a file - in that case we will write to the fallbackFileHandle
         DWORD output;
         // GetConsoleMode returns FALSE when the output is redirected to a file, and we need to output to the fallback file handle.
         BOOL isConsoleOutput = ::GetConsoleMode(handle, &output);
         if (isConsoleOutput == FALSE)
         {
             // We use file_vprintf to handle UTF-8 formatting. The WriteFile api will output the bytes directly with Unicode bytes,
             // while pal::file_vprintf will convert the characters to UTF-8.
-            pal::file_vprintf(fallbackFileHandle, message, va_list());
+            file_printf(fallbackFileHandle, "%s", message);
         }
         else {
             ::WriteConsoleW(handle, message, (int)pal::strlen(message), NULL, NULL);
             ::WriteConsoleW(handle, _X("\n"), 1, NULL, NULL);
         }
     }
 }

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions