Repository navigation
Conversation
Returns 1 when the process runs with an AppContainer (lowbox) token, 0 otherwise and on non-Windows platforms. Several Windows facilities (the named pipe namespace, the mount manager) are restricted inside an AppContainer; embedders and upcoming libuv code need a cheap way to detect that situation.
An AppContainer (lowbox token) process may only create named pipes under \\?\pipe\LOCAL\. uv__unique_pipe_name generated names outside that prefix, so every CreateNamedPipe call failed with ERROR_ACCESS_DENIED, which uv__pipe_server treated as a name collision and retried with a new name forever, spinning at 100% CPU. Any uv_spawn with piped stdio or uv_pipe() hit this. - Prefix the generated names with LOCAL\. The names are internal and never surfaced (they embed a pointer value and the pid), and outside an AppContainer the prefix is just part of the name, so this is not observable. - Cap ERROR_ACCESS_DENIED retries in uv__pipe_server at one so a process that is denied access to the pipe namespace for other reasons fails instead of looping forever. - In uv_pipe_bind2, report ERROR_ACCESS_DENIED as UV_EACCES instead of UV_EADDRINUSE when running inside an AppContainer: for a sandboxed process the name is not in use, it is off-limits.
GetFinalPathNameByHandleW(VOLUME_NAME_DOS) fails with
ERROR_ACCESS_DENIED for every handle inside an AppContainer (lowbox
token) because translating the volume device to a DOS name opens the
mount manager device, which the sandbox denies. That made
uv_fs_realpath fail for all paths.
VOLUME_NAME_NT performs the same kernel-side normalization but names
the volume as an NT device ("\Device\HarddiskVolume4\..."), and is
not restricted. When the DOS variant fails with ERROR_ACCESS_DENIED
inside an AppContainer, fall back to the NT variant and translate the
device prefix back to a drive letter:
- \Device\Mup\server\share\... is emitted directly as
\\?\UNC\server\share\...
- Other device prefixes are resolved through a process-global
device-to-letter map seeded from well-known process paths (cwd,
executable, Windows directory, temp directory) via their NT names.
- On a map miss, probe every logical drive by opening
\\?\X:<relative path> with dwDesiredAccess=0 (only the leaf ACL is
checked, so this works in the sandbox) and comparing
FileIdInformation (volume serial + 128-bit file id) against the
original handle. A confirmed match is cached; if no drive matches
but exactly one refused the probe, use it without caching.
Outside an AppContainer nothing changes.
Also stop overwriting the error from the second
GetFinalPathNameByHandleW call with ERROR_INVALID_HANDLE so the real
failure reason reaches the caller.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a process runs inside a Windows AppContainer (lowbox token), three libuv facilities break (verified on Windows 11 26200):
\\?\pipe\LOCAL\.uv__unique_pipe_namegenerates names outside that prefix, soCreateNamedPipealways fails withERROR_ACCESS_DENIED, whichuv__pipe_servertreats as a name collision and retries with a new name forever: anyuv_spawnwith piped stdio, anduv_pipe(), busy-loop at 100% CPU.GetFinalPathNameByHandleW(VOLUME_NAME_DOS)fails withERROR_ACCESS_DENIEDon every handle (the DOS name translation opens the mount manager device, which the sandbox denies), souv_fs_realpathfails for all paths.uv_pipe_bind2mapsERROR_ACCESS_DENIEDtoUV_EADDRINUSE, which is misleading in a sandbox where the name is off-limits rather than in use.Changes
Three commits:
win: add uv_os_is_app_container(): new API returning 1 when the process token is an AppContainer token (viaGetTokenInformation(TokenIsAppContainer), cached withuv_once), 0 otherwise and on non-Windows platforms. Declared inuv.h, documented inmisc.rst, exercised intest-platform-output. Internal callers useuv__is_app_container().win,pipe: make internal pipe names usable inside an AppContainer:uv__unique_pipe_namenow generates\\?\pipe\LOCAL\uv\%llu-%lu. The names are internal (they embed a pointer value and the pid, and are never surfaced), and outside an AppContainerLOCAL\is just six more bytes of the name, so this is unconditional and not observable.uv__pipe_serverallows one retry onERROR_ACCESS_DENIED(it doubles as a collision error when the existing pipe was created by another user or with different settings), then gives up instead of looping forever.uv_pipe_bind2reportsERROR_ACCESS_DENIEDasUV_EACCESinstead ofUV_EADDRINUSEwhen inside an AppContainer.win,fs: make uv_fs_realpath work inside an AppContainer: when theVOLUME_NAME_DOSsize probe fails withERROR_ACCESS_DENIEDinside an AppContainer, retry withVOLUME_NAME_NT(same kernel-side normalization, not restricted) and translate the NT device prefix back to a drive letter:\Device\Mup\server\share\...is emitted directly as\\?\UNC\server\share\....\\?\X:<relative path>withdwDesiredAccess=0(only the leaf ACL is checked, so this works in the sandbox) and comparingFileIdInformation(volume serial + 128-bit file id) with the original handle. A confirmed match is cached; if no drive matches but exactly one denied the probe, that letter is used without caching.GetFinalPathNameByHandleWcall no longer overwrites the real error withERROR_INVALID_HANDLE.FILE_ID_INFORMATIONis added towinapi.h(theFileIdInformationenumerator andpNtQueryInformationFilewere already there).Verification
uv_os_is_app_container()returns 0; pipe/spawn/stdio tests pass (pipe_bind_error_addrinuseandpipe_getsocknamefail identically on the base commit in this container, environment-related EACCES on/tmp).-Wall -Wextra.dwDesiredAccess=0leaf-ACL opens, workingVOLUME_NAME_NT/FileIdInformation) follows observations collected on Windows 11 26200 with a lowbox token; I could not run a lowbox process from this Linux environment.A follow-up PR to oven-sh/bun bumps
LIBUV_COMMITand routes Bun's ownGetFinalPathNameByHandlecallers through the same fallback.