Repository navigation
feat(graalvm): run the native image under the macOS App Sandbox - #899
Merged
Merged
Conversation
A Mac App Store PKG of a GraalVM app is now packaged from a second, sandboxed image, built only when such a format is configured: - third-party native libraries are extracted at build time into Contents/Frameworks and their load sites routed to NucleusSandboxLoader, reusing the JVM store pipeline's strip/extract tasks; - the image is compiled without Substrate signal handling, whose named POSIX semaphore the sandbox denies; - GraalVmInitializer wires Frameworks, the shim manifest and JNA for that bundle, and points user.home at the container when sandboxed. The regular image, bundle and packages are unchanged.
The sandboxed image is compiled without Substrate's signal handling, so SIGINT/SIGTERM/SIGHUP killed it without running the shutdown hooks. TerminationSignals receives them through libdispatch (kqueue, allowed by the App Sandbox) and exits like HotSpot: shutdown hooks, then 128 + signal. The handler is an empty sigaction rather than SIG_IGN, which would be inherited across exec and make every child process ignore SIGTERM.
…cit user.home - execDirOf canonicalizes the launch path: an image started through a symlink (a CLI link in /usr/local/bin) looked for its fonts and libraries next to the link and died with "Fontconfig head is null". A relative launch path is resolved against PWD, since SubstrateVM's user.dir is not the start directory inside the App Sandbox. - Inside the sandbox user.home only moves to the container while it still holds SubstrateVM's default, so an explicit -Duser.home wins. - Document the torture run of the sandboxed Lab.
The escape hatch matches dependency JAR names, but the image is compiled from a single uber JAR: a substring either matched nothing or kept every native library verbatim, all of them then extracted at run time and refused by the App Sandbox. Every library is now redirected, with a warning when the option is set.
execDirOf resolved a relative launch path against PWD, which a launcher that changes directory without updating it (Python subprocess with cwd=) leaves stale: the regular image then died with "Fontconfig head is null". Try user.dir first, then PWD, and keep the one that names the executable. Also cover hostile inputs (trailing slashes, accented paths, a manifest that is a directory, installing the signal handlers twice).
… check results - LabPaths moves a plain file that sits where the data directory should be aside (NucleusLab.not-a-dir-<millis>) instead of failing every probe with FileAlreadyExistsException at start-up. - CheckStore applies each change to checks.json as it is on disk, through CheckFile, and writes atomically: two Lab processes sharing a container no longer erase each other's results.
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.
What
A GraalVM app can now ship a Mac App Store PKG. When a macOS App Store format is configured, the plugin builds a second, sandboxed native image beside the regular one and packages the PKG from it. The regular image (command line, inputs, bundle, DMG/ZIP/Developer ID PKG) is unchanged.
Why a regular image could not run sandboxed
/cSunMiscSignal-<pid>at start-up; the App Sandbox denies it and the image died beforemain. The sandboxed image is compiled with-R:-EnableSignalHandling, andTerminationSignalsgives SIGINT / SIGTERM / SIGHUP their shutdown hooks back through libdispatch (exit 128 + signal, like HotSpot). The handler is an emptysigaction, notSIG_IGN, so child processes still die on SIGTERM.user.home: SubstrateVM reads it from the password database, outside the container.GraalVmInitializerfollowsHOMEwhenAPP_SANDBOX_CONTAINER_IDis set.Contents/Frameworks, load sites routed toNucleusSandboxLoader.New tasks
stripGraalvmNativeLibsForSandboxing,stageGraalvmSandboxedJars,extractGraalvmNativeLibsForSandboxing,nativeImageCompileSandboxed,createSandboxedGraalvmNativeDistributable,runSandboxedGraalvmNativeDistributable. Details:docs/internals/graalvm-app-sandbox.md.Verified (Lab, macOS 27 arm64, GraalVM CE 25.4)
sandboxed,checks.jsonwritten in the container and reloaded, the 4 extracted libs load fromContents/Frameworks(zstd round trip identical), no file-access denial.packageGraalvmPkgpackages the sandboxed bundle; the app taken from the PKG payload behaves the same (single instance included).graalvm-runtime: tests (incl. newTerminationSignalsTeston a-Xrschild JVM), apiCheck, detekt, ktlint.:plugin:testlocally shows the same 7 host-dependent failures as the base branch (LinuxSigner, RenameMacAppBundle, ElectronBuilderLinuxDesktopEntryConfig), which pass in CI.Also fixed (found while torturing the sandboxed Lab)
GraalVmInitializer.execDirOfnow canonicalizes the launch path.-Duser.homeis kept inside the sandbox.Torture run: every Lab probe opened sandboxed without crash; signal storms and start-up signals match the regular image; concurrent launches,
kill -9relaunch and accented paths behave. Details in the doc.Not verified
file-read-datadenials on its ancestors (notify'sWatchRootflag), independent of GraalVM.