[Chrome] Sandbox profiles referring to /System/Volumes/Data are broken

Originator:mark
Number:rdar://FB9738355 Date Originated:2021-11-03
Status:Open Resolved:
Product:macOS/‌Something else not on this list Product Version:12.0.1 21A559
Classification:Application Crash Reproducible:Always
 
When I place my application, Google Chrome, into /System/Volumes/Data and try to launch it from there, it aborts.

Upon investigation, I determined that the sandbox is blocking files from my own app bundle from being opened. My application attempts to open these files using paths within /System/Volumes/Data, and the sandbox profile is set to allow file-read* access to /System/Volumes/Data. However, these opens are not permitted in this location unless the sandbox profile is set to allow file-read* access to the same paths _without_ /System/Volumes/Data prepended. This is a problem because (1) aside from some special paths set aside by Apple, like Users and Private, /System/Volumes/Data/x doesn’t necessarily have any relation to /x, and (2) my application determined its path to be within /System/Volumes/Data by asking the OS for its executable path (via a realpath or fcntl(F_GETPATH) of _NSGetExecutablePath or [[NSBundle mainBundle] executablePath]) or main bundle path (via a realpath or fcntl(F_GETPATH) [[NSBundle mainBundle] bundlePath]), and if that’s what the OS told it, then that path should be usable.

This is a regression. It worked correctly as recently as 10.15.7 19H15. It’s not working in 11.0.1 20B29, 11.6 21G165, or 12.0.1 21A559. The read-only system volume and the system/data split originated in macOS 10.15, so this did previously work even under that split.

Steps to reproduce:
1. Download Chrome from https://google.com/chrome/ and open the disk image
2. Drag Google Chrome from the disk image volume to /System/Volumes/Data. Don’t drag it to /Applications. Put it in the root of the Data volume. You can open /System/Volumes/Data from the Finder by doing Go:Go to Folder… or pressing Command-Shfit-G and typing /System/Volumes/Data. Authenticate if necessary.
3. Attempt to launch Google Chrome by double-clicking it from /System/Volumes/Data.

Expected behavior:
Chrome should launch.

Observed behavior:
A browser window with no content appears briefly, and disappears.

Launching from a shell and watching stderr gives information about the nature of the failure:

dlopen /System/Volumes/Data/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/95.0.4638.69/Helpers/Google Chrome Helper (GPU).app/Contents/MacOS/../../../../Google Chrome Framework: dlopen(/System/Volumes/Data/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/95.0.4638.69/Helpers/Google Chrome Helper (GPU).app/Contents/MacOS/../../../../Google Chrome Framework, 0x0105): tried: '/System/Volumes/Data/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/95.0.4638.69/Helpers/Google Chrome Helper (GPU).app/Contents/MacOS/../../../../Google Chrome Framework' (file system sandbox blocked open()), '/System/Library/Frameworks/Google Chrome Framework.framework/Versions/95.0.4638.69/Helpers/Google Chrome Helper (GPU).app/Contents/MacOS/../../../../Google Chrome Framework' (no such file).
[87806:259:1103/115001.120257:ERROR:gpu_process_host.cc(979)] GPU process exited unexpectedly: exit_code=6
[87806:259:1103/115001.120280:FATAL:gpu_data_manager_impl_private.cc(417)] GPU process isn't usable. Goodbye.

The “dlopen” error comes from a child process, Google Chrome Helper (GPU).app, which exits when it can’t dlopen a framework that it requires from within our outer .app bundle. The other two messages come from the main browser process, which gives up and aborts when it can’t establish a working GPU helper process. The dlerror message set by dlopen is relevant:

file system sandbox blocked open()

This message comes from dyld3::Loader::mapImage when it determines that an open failure was the result of a sandbox denial (11.5 https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/Loading.cpp.auto.html.)

In this case, we have engaged the sandbox in the GPU helper process with a profile that specifies (deny default) but that allows files from within our own outer .app bundle to be read by (allow file-read* (subpath (param bundle-path))) (from https://chromium.googlesource.com/chromium/src/+/4d4d32015c3683a0c29b0381d0c06372e53112b9/sandbox/policy/mac/common.sb#111); the (param bundle-path) is "/System/Volumes/Data/Google Chrome.app".

This is only seen when Google Chrome.app is located within /System/Volumes/Data, but not within a “special” path that has a defined /x→/System/Volumes/Data/x correspondence such as /Users, /private/tmp, or /Applications.

I have a reduced testcase, in the form of t_sandbox.c.

Usage: t_sandbox <open-path> [allow-path]
If allow-path is absent, it defaults to open-path.

This enables the sandbox with the following profile:

  (version 1)
  (deny default)
  (allow file-read-metadata (subpath "/"))  ; to resolve symlinks
  (allow file-read* (subpath (param "allow_path")))

and then attempts to open whatever is at open-path.

This shows it working:

mark@sweet16 zsh% clang -Wall -Werror t_sandbox.c -o t_sandbox
mark@sweet16 zsh% touch /private/tmp/z
mark@sweet16 zsh% ./t_sandbox /private/tmp/z && echo ok
ok
mark@sweet16 zsh% ./t_sandbox /private/tmp/z /private/tmp/y && echo ok
t_sandbox: open: Operation not permitted

And I can’t read /tmp/z at that path without a sandbox profile that allows file-read-data at the same path with symbolic links resolved, since /tmp → private/tmp:

mark@sweet16 zsh% ./t_sandbox /tmp/z && echo ok
t_sandbox: open: Operation not permitted
mark@sweet16 zsh% ./t_sandbox /tmp/z /private/tmp/z && echo ok
ok

This is all fine so far.

With Google Chrome.app in /Applications:

mark@sweet16 zsh% ./t_sandbox '/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/95.0.4638.69/Google Chrome Framework' && echo ok
ok

This is fine too. And I also see a positive result for for Google Chrome.app when placed at most other paths, but not within /System/Volumes/Data when the allow-path is also within /System/Volumes/Data:

mark@sweet16 zsh% ./t_sandbox '/System/Volumes/Data/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/95.0.4638.69/Google Chrome Framework' && echo ok
t_sandbox: open: Operation not permitted

However, this DOES work when the allow-path has the leading /System/Volumes/Data removed:

mark@sweet16 zsh% ./t_sandbox '/System/Volumes/Data/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/95.0.4638.69/Google Chrome Framework' '/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/95.0.4638.69/Google Chrome Framework' && echo ok
ok

What’s interesting to note is that this last test case succeeds (when it seems that it should fail) even though I haven’t set up a mapping for Google Chrome.app in /etc/synthetic.conf. However, note that even if a firm link is set up in /etc/synthetic.conf, the OS still reports to my application that its path is within /System/Volumes/Data (via realpath or fcntl(F_GETPATH) of _NSGetExecutablePath, [[NSBundle mainBundle] executablePath], or [[NSBundle mainBundle] bundlePath]), and the sandbox still denies access to my bundle when configured to allow access via the path that it was given at /System/Volumes/Data.

The core of the problem is that /System/Volume/Data paths are valid on the system, but the sandbox is not enforcing restrictions to those paths, or restrictions that refer to those paths, correctly.

On our end, this is https://crbug.com/1266490.

Comments

t_sandbox.c

base64 --decode <<< 'H4sIAFPvgmECA42TbW/aMBDH3+dT3DJtClEotKv2ojxMU8ekSqiVytA2AYpM4oC1YCPboa1a vnvvEkjCxtrmRRKf73734L9bPkQpkwto/mRpim+utdJgQ8NkPFf3JxE0VbUEv+U474WM0izm 0EXnk2W/ZkgiadND0x50aLSxUP+YUjEnmyOkhV1YKKSw4Z2wy3DNNFtxy7XxoiXTEClpLPhr rRKR8sCB154MuZ/PQwtJyhbmDQH1NMWnqmEyeyvA9/OZzrOk0Sl6WzEhPfphehEFOy/830xm DXhErEjAoz3owhk8PeV+0IfzYhcgWWsMTzycGbIDcDPDFvwCPhjoqjWXzTWzyz5M8EjVXb6Y TaUbQJ6jPcM6iKK5zbSEwa+rH+H3r1fD8e2ANraOc6x14oaEgl7BOZ11jjrmSWue1MUn+FID XBSAMwQ4r00ZGY9uhaQmykUA1+PhcEtlGMusiI6xCnlAb3darrdBrlASThs4k9Iac/kAMU9Y ltqDjTwdEKOpOYubWBiLmWXgmWyetzN1W1O3QUEALR+swskalW44mIdVKuQfQ9fmPzy/4nh5 34irOiQugctJF1oytCYB6Q3O58Wrsr8e0A5qcw3gYwHKpUByQ9K7HrT3CsPde6+uDBTZS3lI e3g2NWguI6oxibFGOnyvVEAAN+Htt5vr4e8yP3p1D9L/nZ2C3YpcF+9ofHk5GI06ztZ5Boxt DAfQBAAA' | gunzip > t_sandbox.c


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!