Skip to content

Fix MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY leaking to child processes (#5987)#6619

Merged
Scottj1s merged 1 commit into
mainfrom
users/sjones/mrt-basedir-env-5987
Jul 14, 2026
Merged

Fix MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY leaking to child processes (#5987)#6619
Scottj1s merged 1 commit into
mainfrom
users/sjones/mrt-basedir-env-5987

Conversation

@Scottj1s

Copy link
Copy Markdown
Member

Fixes #5987.

Problem

MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY is set by the Windows App SDK auto-initializer (a requirement for PublishSingleFile SxS manifest redirection). Because it is a process environment variable, it is inherited by child processes spawned via CreateProcess: a child Windows App SDK app's MRTCore then resolved the parent's resources.pri, and reg-free WinRT redirection could be steered by a parent-controlled directory. As noted on the issue, this also lets a user-controlled environment variable influence where an elevated process reads a data file from.

Fix — PID stamp

The setter now also stamps MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY_PID with the owning process id. MRTCore (MRM.cpp) and reg-free WinRT (catalog.cpp) honor the base directory only when the stamp matches the current process; inherited (foreign) or absent stamps are ignored and resolution falls back to the module (exe) directory. A parent cannot forge a stamp equal to the not-yet-known child pid, so this also closes the elevation-steering concern.

Refactor — clearer naming

Per feedback on the issue thread, the base-directory logic is moved out of UndockedRegFreeWinRT-AutoInitializer.cs (which is now runtime-load only) into a new, clearly-named WindowsAppRuntimeBaseDirectoryAutoInitializer. Behavior is unchanged — it runs under the same self-contained trigger. Only the C# path sets the base directory (PublishSingleFile is .NET-only); the native URFW auto-initializer never set it, so there is no C++ change.

Tests

New MrtCore EnvironmentVariableIsolationTests:

  • GetFilePath_HonorsBaseDirectoryOnlyWhenStampedByCurrentProcess — in-process: base directory is honored when the stamp matches, ignored when the stamp is foreign or absent.
  • ChildProcessIsInsulatedFromInheritedBaseDirectory — spawns a real child process that inherits the environment and verifies it resolves resources under its own directory, not the inherited one.

Validated locally (VS x64 Debug): MRM.vcxproj and MrmUnitTest.vcxproj build clean (0 warnings / 0 errors); the new tests pass; existing BasicTest suite is 21/21 (no regression).

Servicing

The same issue is present in release/1.8-stable and release/2.0-stable (identical unconditional setter + consumers without a PID guard). Ports to 1.8 and 2.0 will follow after this merges.

…sses (#5987)

The Windows App SDK auto-initializer sets the process environment variable
MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY (a requirement for PublishSingleFile SxS
manifest redirection). Because it is a process environment variable, it is inherited
by child processes spawned via CreateProcess: a child's MRTCore then resolved the
parent's resources.pri, and reg-free WinRT redirection could be steered by a
parent-controlled directory.

Stamp the owning process id in MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY_PID and
honor the base directory only when the stamp matches the current process:

- MRM.cpp and catalog.cpp ignore the base directory unless it was stamped by the
  current process, so inherited (foreign) values fall back to the module directory.
  A parent cannot forge a stamp equal to the not-yet-known child pid, so this also
  prevents steering where an elevated child reads data files from.
- Move the base-directory setter out of the URFW auto-initializer into a new,
  clearly-named WindowsAppRuntimeBaseDirectoryAutoInitializer; the URFW file is now
  runtime-load only. This addresses the filename confusion raised on the issue.

Add MrtCore EnvironmentVariableIsolationTests: an in-process PID-guard test and a
real cross-process test proving a spawned child ignores the inherited base directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dac4165f-ec13-4406-9206-60cd55b24c2b
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@Dreynor87

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).
1 pipeline(s) were filtered out due to trigger conditions.

@Scottj1s
Scottj1s merged commit aa197a5 into main Jul 14, 2026
45 of 47 checks passed
@Scottj1s
Scottj1s deleted the users/sjones/mrt-basedir-env-5987 branch July 14, 2026 18:13
@DHowett

DHowett commented Jul 14, 2026

Copy link
Copy Markdown
Member

but.. but... environment variables are for communicating things to other processes. do we literally not have a better way to make two DLLs loaded into the same address space communicate a single piece of information? now we have to work around inheritance, whereas if we didn't use an inheritable primitive we wouldn't have to work around inheritance...

@Scottj1s

Copy link
Copy Markdown
Member Author

but.. but... environment variables are for communicating things to other processes. do we literally not have a better way to make two DLLs loaded into the same address space communicate a single piece of information? now we have to work around inheritance, whereas if we didn't use an inheritable primitive we wouldn't have to work around inheritance...

unfortunately we do not. this all stems from the need to dynamically assign the location of host dlls for activatableClass entries in fusion manifests. that location is only known at runtime in some scenarios (such as PublilshSingleFile), not statically at build time (manifest generation/modification time). the only mechanism I could find to support that dynamism was env var substitution. that said, the PID check here should be sufficient to protect against accidental/malicious misuse of the env var.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MRTCore using MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY breaks launching one WinAppSDK app from another

3 participants