Why won’t a system app or command tool run? Launch constraints and trust caches

If your Mac is running Ventura or later and it has refused to run a copy of one of the apps bundled with macOS, or a system command tool that it used to run without problems, it may be because of launch constraints. These were first described a year ago by Csaba Fitzl, and now control what can run system code, and how.

Crash logs

Csaba Fitzl’s two examples make a good starting point for understanding what is happening when macOS refuses to run system apps and command tools because of launch constraints.

One example is running the command
/usr/libexec/periodic-wrapper
something that the user isn’t intended to try, as it’s part of the periodic scheduling system. In Ventura, this is refused and the command crashed; if you look in the crash log it reports
Exception Type: EXC_CRASH (SIGKILL (Code Signature Invalid))
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: CODESIGNING 4 Launch Constraint Violation

which might suggest the tool itself is broken, but does include the key words Launch Constraint Violation.

The second is making a copy of a bundled app, in this case Terminal, and running that app from a new location. The simplest way to do this now is to make an archive of the app, move that to somewhere in your Documents folder, unarchive and launch it from there. Once again the app will be crashed, and its crash log reports
Exception Type: EXC_CRASH (SIGKILL (Code Signature Invalid))
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: CODESIGNING 4 Launch Constraint Violation

Unified log

Fuller reports are to be found in the Unified log for the times of the crashes.

The command is recorded with
AMFI: Launch Constraint Violation (enforcing), error info: c[1]p[1]m[1]e[1], (Constraint not matched) launching proc[vc: 1 pid: 1225]: /usr/libexec/periodic-wrapper, launch type 0, failure proc [vc: 1 pid: 1225]: /usr/libexec/periodic-wrapper

And when Terminal is crashed, you’ll see
AMFI: Launch Constraint Violation (enforcing), error info: c[1]p[1]m[1]e[2], (Constraint not matched) launching proc[vc: 1 pid: 2440]: /Users/hoakley/Documents/00crypt/Terminal.app/Contents/MacOS/Terminal, launch type 0, failure proc [vc: 1 pid: 2440]: /Users/hoakley/Documents/00crypt/Terminal.app/Contents/MacOS/Terminal
ASP: Security policy would not allow process: 2440, /Users/hoakley/Documents/00crypt/Terminal.app/Contents/MacOS/Terminal
xpcproxy exited due to OS_REASON_CODESIGNING | Launch Constraint Violation, error info: c[1]p[1]m[1]e[2], (Constraint not matched) launch type 0, failure proc [vc: 1]: /Users/hoakley/Documents/00crypt/Terminal.app/Contents/MacOS/Terminal

Launch constraints

In macOS 13 (and iOS 16) and later, every executable binary in the system has a set of rules to determine the requirements for that binary to be launched. These include self constraints that the binary itself must meet, parent constraints that must be met by its parent process, and responsible constraints that must be met by the process requesting the launch. Together these form that code’s launch constraints.

To make those constraints simpler, they come in different categories, ranging from 0, in which there are no constraints at all, to combinations that prevent launch by processes that aren’t themselves part of the system and require the code itself to be on the System volume. In the case of those two examples above, periodic-wrapper was still on the System volume, but was being launched by the user rather than through its launchd property list, so was refused. The copy of the Terminal app was being launched correctly by the user, but its code wasn’t on the System volume, so it too was refused.

Linus Henze has set out in full detail all the categories used in iOS 16, which are probably almost identical to those in macOS 13.

Trust caches

Instead of macOS looking up each binary’s launch constraints from the binary itself, all those constraints are assembled into Trust Caches, where they’re listed by the code directory’s hash (cdhash). To look up the launch constraints for the Terminal app, the system first calculates the cdhash for its code directory, then looks in the Trust Cache for the launch constraints for that cdhash.

The System volume contains a static Trust Cache that covers all the executable binaries that come as part of the system. That is locked into read-only storage during the early kernel boot phase of startup. Additional Trust Caches are authenticated to ensure they haven’t been tampered with, and loaded when required. Apple cites the example of the Trust Cache required by the code within macOS software updates (known as the update brain) that runs the process, allowing it to run with platform privileges, as it requires to perform the update. Apple gives further details on Trust Caches in its Platform Security Guide.

Disabling launch constraints

What if you need to ignore the launch constraints imposed by macOS? Because system executables are laid out in the static Trust Cache, there’s no way to modify that, and no way to override it. All you can do is disable System Integrity Protection (SIP), which is required for launch constraints.

On an Apple silicon Mac, that requires downgrading its secure boot setting to Permissive Security before disabling SIP at the command line.

Environment constraints

Launch constraints and the Trust Cache system are complete and fully enforced as of Ventura 13.3, but can’t be used by third-party developers. For that, you’ll have to wait for Sonoma’s environment constraints, as described by Robert Kendall-Kuppe at WWDC, and in Apple’s documentation.

Constraint dictionaries are either saved in property lists for launchd, or in those used for signing code. These too are associated with cdhashes, use some categories common to other trust caches, and work similarly to protect third-party code such as helper apps.

Summary

  • All system apps and executable binaries in Ventura and later are subject to launch constraints, limiting the circumstances in which they can be launched.
  • Among other things, launch constraints can prevent the user from running copies of bundled apps, and certain command tools (Mach-O binaries).
  • If the user attempts an operation that’s not permitted by launch constraints, that app or executable will be terminated, with a crash report and log entries recording a Launch Constraint Violation.
  • There’s no way to look up what launch constraints apply to any component in the system.
  • The only way to disable launch constraints is to disable SIP.
  • In Sonoma, these will be extended to environment constraints covering third-party code.