Skip to content

ProcessContainer: MSYS2/Cygwin runtimes (git-bash) cannot initialize — NtCreateDirectoryObject(\BaseNamedObjects\...) denied #1061

Open

Description

Relevant area(s)

Linux, Windows

Brief description of your issue

Summary

MSYS2/Cygwin-based executables (git-bash, and anything else linking msys-2.0.dll)
cannot run under the processcontainer backend. The process launches successfully,
but the MSYS runtime aborts during initialization when it tries to create its shared
state directory in the session-global object namespace, which AppContainer processes
cannot write.

This is not a policy or configuration problem — no filesystem, capability, or UI
setting can grant it.

Environment

  • MXC: main
  • Windows 11 25H2 (build 26200)
  • Selected tier: appcontainer-dacl (T3); --probe reports
    baseContainerApiPresent: true, bfsCompiledIn: false
  • git-bash / MSYS2 runtime 3.6.5 (x86_64-pc-msys)

Steps to reproduce

Repro

Config:

{
  "version": "0.8.0-alpha",
  "containment": "processcontainer",
  "process": {
    "commandLine": "\"C:\\tools\\git-bash\\bin\\bash.exe\" --version",
    "cwd": "C:\\tools\\git-bash",
    "timeout": 30000
  },
  "filesystem": {
    "readonlyPaths": ["C:\\tools\\git-bash", "C:\\Windows"],
    "readwritePaths": []
  },
  "fallback": { "allowDaclMutation": true },
  "ui": { "disable": false }
}
wxc-exec.exe config.json

Expected behavior

bash.exe --version
GNU bash, version 5.2.37(1)-release (x86_64-pc-msys)
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Actual behavior

Observed

0 [main] bash (NNNN) C:\tools\git-bash\usr\bin\bash.exe: *** fatal error -
  NtCreateDirectoryObject(\BaseNamedObjects\msys-2.0S5-<hash>): 0xC0000022
Exit code: -1073741502 (0xC0000142)

0xC0000022 is STATUS_ACCESS_DENIED; 0xC0000142 is STATUS_DLL_INIT_FAILED.

Root cause

msys-2.0.dll passes an absolute NT path to NtCreateDirectoryObject.
AppContainer's named-object redirection lives in user-mode kernelbase
(BaseGetNamedObjectDirectory) and only covers the Win32 named-object APIs — it does
not rewrite absolute NT paths. The call therefore reaches the real global
\BaseNamedObjects and fails the DIRECTORY_CREATE_SUBDIRECTORY access check
(SeChangeNotifyPrivilege bypasses traverse, not create).

Verified by direct probe from inside the container, same container SID:

Path Result
\BaseNamedObjects\test 0xC0000022
\Sessions\1\AppContainerNamedObjects\<SID>\test STATUS_SUCCESS

The container's own namespace is writable; only the global one is denied.

An API trace of bash -c "echo hi | tr a-z A-Z" shows the full dependency is small —
exactly two absolute paths, with every child object created relative to the resulting
directory handle:

CreateDir     Root=0x0    "\BaseNamedObjects\msys-2.0S5-<hash>"
CreateDir     Root=0x0    "\Sessions\BNOLINKS\1\msys-2.0S5-<hash>"
CreateSection Root=0x124  "shared.5"
CreateSection Root=0x124  "cygpid.<pid>"        <- fork
CreateMutant  Root=0x124  "cygpipe.input.mutex.<pid>-0x1"   <- pipe
CreateMutant  Root=0x124  "sig_send.<pid>"      <- signals
CreateSymlink Root=0x124  "winpid.<winpid>"

Ruled out

  • Not Win32k. Identical failure with ui.disable: false.
  • Not filesystem policy. \BaseNamedObjects is an object-manager path; no
    readonlyPaths / readwritePaths entry reaches it. Granting read-write on the
    install directory changes nothing.
  • Not ACLs. The install tree already carries
    ALL APPLICATION PACKAGES:(OI)(CI)(RX).
  • Not tier-specific. Same result on appcontainer-dacl; the object namespace
    boundary is a property of AppContainer, not of the tier.

Impact

Any MSYS2/Cygwin-based tool is unusable under processcontainer. This includes
git-bash, which is a common developer shell on Windows.

docs/learning-mode/capabilities.md already classifies \BaseNamedObjects as
unsupportedObjectType ("outside the supported diagnostic model"), so even with
captureDenials enabled this denial is filtered out of the actionable output and
appears only in verbose logging.

Activity

  1. added
    OS-LinuxApplies specifically to Linux hosts or Linux behavior.
    OS-WindowsApplies specifically to Windows hosts or Windows behavior.
    on Aug 28, 2026
  2. github-actions commented on Aug 28, 2026

    @github-actions

    Triage summary

    • Label added: Container-Process — this is a processcontainer / AppContainer backend issue on Windows.
    • Assigned: @jsidewhite and @bbonaby (AppContainer / process isolation area).
    • Needs-Triage has been removed.

    The root cause is well-documented in the issue: AppContainer's named-object redirection (via BaseGetNamedObjectDirectory) only covers Win32 APIs and does not rewrite absolute NT paths. msys-2.0.dll calls NtCreateDirectoryObject with an absolute \BaseNamedObjects\... path, which bypasses the redirection and hits the global namespace access check.

    Maintainers: Comment /investigate to check whether this issue or bug is valid against the most current code. Copilot will also produce a report with the changes that will be needed. For a small, unambiguous documentation or test fix, it may also create one draft PR.

    Generated by 🏷️ Issue Triage for #1061 · sonnet46 · 27.6 AIC · ⌖ 7.84 AIC · ⊞ 7.7K · ◷

  3. jsidewhite commented on Sep 14, 2026

    @jsidewhite
    Member

    For Tier 1 to support,

    1. Tools should be fixed in MSYS's get_session_parent_dir() to use a non-global namespace
    2. In OS, we will need true namespace virtualization support for BNO
      Salah Khan (@SalahKhan-MSFT)
  4. added
    Issue-FeatureA request for new or expanded product functionality.
    and removed
    Issue-BugSomething is not working as intended.
    on Sep 14, 2026
  5. jsidewhite commented on Sep 14, 2026

    @jsidewhite
    Member

    I tried to workaround it by ACL'ing the global object, but it didn't pan out.

    param(
        [Parameter(Mandatory)]
        [scriptblock]$Command,
    
        [string]$MsysDirectoryName = "msys-2.0S5-1888ae32e00d56aa"
    )
    
    $ErrorActionPreference = "Stop"
    Import-Module NtObjectManager
    
    $sessionId = (Get-Process -Id $PID).SessionId
    
    # AC = ALL APPLICATION PACKAGES (S-1-15-2-1).
    # The Low mandatory label permits writes from Low-IL AppContainer processes.
    $securityDescriptor = New-NtSecurityDescriptor `
        "D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;AC)S:(ML;;NW;;;LW)"
    
    $globalDirectory = $null
    $sessionDirectory = $null
    
    try {
        $globalDirectory = New-NtDirectory `
            "\BaseNamedObjects\$MsysDirectoryName" `
            -SecurityDescriptor $securityDescriptor
    
        $sessionDirectory = New-NtDirectory `
            "\Sessions\BNOLINKS\$sessionId\$MsysDirectoryName" `
            -SecurityDescriptor $securityDescriptor
    
        & $Command
        $exitCode = $LASTEXITCODE
    }
    finally {
        if ($null -ne $sessionDirectory) {
            $sessionDirectory.Dispose()
        }
    
        if ($null -ne $globalDirectory) {
            $globalDirectory.Dispose()
        }
    }
  6. 11 remaining items

  7. xoviat commented on Oct 28, 2026

    @xoviat
  8. xoviat commented on Oct 28, 2026

    @xoviat

    it would be nice if the OS team could make processcontainer available on windows server github runners

  9. xoviat commented on Oct 29, 2026

    @xoviat

    Root cause found and fixed — plus two things that may help the OS side

    The chain behind this issue turned out to be one missing ACE in the Cygwin/MSYS runtime, with everything else downstream:

    1. _everyone_sd() (winsup/cygwin/sec/helper.cc) granted only World (S-1-1-0). AppContainer processes are not covered by "Everyone", so every open of the runtime's named objects from inside the container was denied (0xC0000022) — including the winpid.<N> symbolic link that cygwin_pid() opens to map Windows PIDs to Cygwin PIDs.
    2. cygwin_pid() therefore returned 0 in the parent, so fork() returned 0 to the parent: both processes took the child branch. A pipeline ran its first element twice, tr never started, and script tails were lost.
    3. Both sides then closed the pipe's read (server) end, the named-pipe instance died, and writes failed 0xC00000B1 (echo: write error, exit 3328) — which is why the failure looked like named-object "degradation" rather than an access check.

    Fixes (all in winsup/cygwin, shipped as a patch against this tree)

    • sec/helper.cc: _everyone_sd now adds an ALL APPLICATION PACKAGES (S-1-15-2-1) ACE; sec_acl additionally grants the confined token's logon SID (restricted tokens keep it enabled even when the user SID is deny-only) and the AppContainer package SID. This also fixes the signal-pipe Win32 error 5 from Windows confinement after async fix: MSYS private-pipe DACL mismatch and named-object refusal; AllocConsole alone is insufficient buhuikongpan/dsh-win-gitbash#10.
    • mm/shared.cc: shared-object parent directories fall back into the container's private namespace (\Sessions\<n>\AppContainerNamedObjects\<package SID>); DACL-less sections get an explicit descriptor (hardened sandboxes silently rewrite NULL DACLs and then deny siblings); an unmappable borrowed handle falls back to name-based reopen.
    • fhandler/pipe.cc: absolute NT pipe names (opening the \Device\NamedPipe root is denied in-container), CreatePipe-anonymous naming, create without OBJ_INHERIT + SetHandleInformation afterwards, re-duplicate pipe handles in fork fixup, sec_user for pipe SAs.
    • wincap.cc: fork-friendly capabilities only from the real kernel image (in_this_image).

    One PSEC gotcha worth documenting

    PSEC force-relocates images that have not opted into ASLR per process, which breaks Cygwin's fork (it reconstructs the parent's address space in the child and needs identical layouts). Every image taking part in a fork — not just the runtime DLL — must carry the DYNAMICBASE PE flag; a pristine Git-for-Windows install fails fork with 0xC0000142 in the child even with a patched msys-2.0.dll. We ship a tiny tool to set the flag (tools/ci/peflag.py), but if PSEC could keep non-opted images at stable per-file bases — or fail with a clearer diagnostic — that would save the next person a lot of time.

    Availability on GitHub runners (re: the note above)

    Confirmed today: windows-2022/windows-2025 (Server SKUs) report Windows ProcessContainer is unavailable: PSEC is not enabled, so the backend cannot run there at all. The windows-11-arm image (Windows 11 client) does have PSEC enabled and runs the ProcessContainer fine under x64 emulation — that is where our full verification runs in CI.

    Verification

    tools/final-verify.sh runs Git Bash inside the ProcessContainer and asserts fork/exec, pipelines, file I/O and job control (11 checks):

    ==================== RESULT: 11 passed, 0 failed ====================
    GOAL VERIFIED: git bash works inside the mxc sandbox.
    

    Green locally (x64 Windows 11) and on the windows-11-arm CI image. The fixes live in the mxc branch linked above; built msys-2.0.dll/cygwin1.dll are attached to the v0.1.0 release of that repository, and the DLL can be dropped into a Git-for-Windows install (plus the PE flag) to work in the container today.

  10. JimmyMederos commented on Nov 2, 2026

    @JimmyMederos

    Another data point from a second product, plus a question about getting the fix into stock runtimes.

    We build SE Code, an open-source coding agent harness, and run each agent command in a ProcessContainer via mxc-sdk 1.0.1 (see our console use case in #1544). On Windows 11 25H2 build 26200.9457 with stock Git for Windows 2.49.0.windows.1 (msys-2.0.dll 3.5.7), we hit the same failure through an indirect chain: native PowerShell → native git.exe → Git's usr/bin/sh.exe during a local git fetch:

    *** fatal error - NtCreateDirectoryObject(\BaseNamedObjects\msys-2.0S5-1888ae32e00d56aa): 0xC0000022
    fatal: Could not read from remote repository.
    

    git status and git commit in the same container succeed; the failure only appears once an MSYS descendant starts. That matters for harnesses: checking whether the initial command is Bash doesn't catch it, so today the only option we can offer users is an explicit per-command escape that drops filesystem and network containment, which is exactly what we'd like to avoid.

    xoviat, thanks for the root-cause write-up; the missing S-1-15-2-1 ACE explains everything we saw. Two questions:

    1. Are the winsup/cygwin patches headed to Cygwin upstream (and from there to MSYS2 / Git for Windows)? Asking users to drop a patched msys-2.0.dll into their Git install and re-flag PE images isn't something we can ship, but a stock runtime that works in AppContainer would let us keep containment on by default.
    2. For the MXC side: the per-process ASLR relocation of non-DYNAMICBASE images breaking Cygwin fork seems worth either keeping stable per-file bases or emitting a clear diagnostic, as suggested above. A supported way to detect "this runtime cannot initialize in a container" before running the command would also help harnesses explain the failure instead of surfacing 0xC0000022.

    Happy to test candidate builds of either on our host.

  11. xoviat commented on Nov 2, 2026

    @xoviat

    JimmyMederos I've been trying to get in touch with an msys2 maintainer, but I haven't received a response yet. I am willing to submit the patches but it would like a comment from an msys2 maintainer on the direction before I push patches to cygwin. I tried on chat, but will open an issue for this.

  12. xoviat commented on Nov 2, 2026

    @xoviat

    Also, it would be nice to get thoughts from a Windows OS developer to confirm that I'm on the right track.

  13. added
    Issue-BugSomething is not working as intended.
    and removed
    Needs-AttentionRequires attention or a decision from the MXC maintainers.
    Issue-FeatureA request for new or expanded product functionality.
    on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Backend-ProcessContainer-FilesystemFor filesystem related Windows process containment issuesIssue-BugSomething is not working as intended.OS-WindowsApplies specifically to Windows hosts or Windows behavior.

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions