Skip to content

sandbox: keep system temp dirs writable in offline installs - #24034

Merged
MikeMcQuaid merged 2 commits into
mainfrom
sandbox-offline-system-temp
Sep 19, 2026
Merged

MikeMcQuaid merged 2 commits into
mainfrom
sandbox-offline-system-temp

Conversation

@stefanb

@stefanb stefanb commented Sep 19, 2026

Copy link
Copy Markdown
Member

  • Have you followed our Contributing guidelines?
  • Have you checked for other open Pull Requests for the same change?
  • Have you explained what your changes do? Performance claims (e.g. "this is faster") must include brew benchmark results.
  • Have you explained why you'd like these changes included, not just what they do?
  • For bug fixes, have you given step-by-step brew commands to reproduce the bug?
  • Have you written new tests (excluding integration tests)? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) locally?

  • I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM.

AI-assisted contribution by Claude Code (Fable 5) of ~95% of the work (diagnosis, fix, spec, validation); results reviewed by the user.


What: splits Sandbox#allow_write_temp_and_cache into allow_write_system_temp (system temporary directories, incl. the macOS per-user /private/var/folders/…/[C,T]/ dirs) plus the HOMEBREW_CACHE allowance, and makes FormulaInstaller's offline-install branch (formulae with a fetch phase) call allow_write_system_temp instead of allowing only HOMEBREW_TEMP. A spec asserts the new method keeps the per-user dirs writable without opening the download cache.

Why: the offline branch narrows the sandbox to keep HOMEBREW_CACHE read-only, but as a side effect it also drops the per-user system temp/cache dirs that the normal install path allows. Plenty of Apple tooling writes there via confstr(3), ignoring $TMPDIR, so fetch-phase formulae hit failures that plain formulae never see:

  • Xcode 27's SwiftPM/XCBuild build service creates T/TemporaryDirectory.* and T/TemporaryItems; when denied, every swift build in a fetch-phase formula aborts with a misleading unable to write manifest to '<buildpath>/.build/out/…/XCBuildData/manifest.json' error. With SwiftPM's swiftbuild build system now the default (native is deprecated), this blocks sandboxing any Swift formula.
  • mktemp with an inherited default TMPDIR (seen locally with redis's deploy.sh and pulumi during fetch-phase conversion work).
  • clang's module cache under the per-user C dir (seen as CI-only failures on cgo + Apple-framework builds, e.g. aws-vault/ipatool conversion attempts; locally masked by a warm cache since reads stay allowed).

Reproduce: with Xcode 27 selected and any Swift formula converted to a fetch phase (e.g. add def fetch; system "swift", "package", "resolve", "--disable-sandbox"; end + deny_network_access! to mint):

brew install --build-from-source mint   # fails: unable to write manifest.json (XCBuildData)

Bisected by dumping the generated seatbelt profile and replaying it via sandbox-exec on a plain swift build: the captured 161-line profile reproduces the failure outside brew; adding the single line (allow file-write* (subpath "$(getconf DARWIN_USER_TEMP_DIR)")) makes it pass. With this PR applied, the same brew install --build-from-source mint succeeds end-to-end.

Not changed: the offline branch still denies HOMEBREW_CACHE writes (apart from the package-manager caches) and all network, per its design.

Security posture: this grants fetch-phase installs nothing that every normal formula build does not already have — allow_write_temp_and_cache includes these exact allowances on the ordinary install path today — so the offline sandbox stays strictly tighter than the normal one (network denied, download cache read-only), just no longer accidentally tighter in a way Apple tooling cannot tolerate. The pre-existing breadth of the [C,T] grant (e.g. a build can write another process's clang module cache) is unchanged by this PR; if narrowing is preferred, a follow-up could restrict both paths to the specific patterns tooling needs (T/TemporaryDirectory.*, T/TemporaryItems, C/clang/ModuleCache, …) — kept out of this parity fix to limit regression risk.

The offline install branch (formulae with a fetch phase) allowed only
HOMEBREW_TEMP, dropping the per-user /private/var/folders/…/[C,T]/ and
/private/var/tmp allowances the normal install path gets. macOS tooling
writes there via confstr(3), ignoring TMPDIR: Xcode 27's SwiftPM/XCBuild
build service (T/TemporaryDirectory.*, T/TemporaryItems) aborts every
swift build with a misleading buildpath manifest.json error, mktemp with
an inherited default TMPDIR fails, and clang's module cache under the
per-user C dir breaks cold-cache cgo builds.

Split allow_write_temp_and_cache into allow_write_system_temp plus the
HOMEBREW_CACHE allowance and call the former from the offline branch,
which keeps the download cache read-only there as designed.
@saitanallensantiago26-beep

This comment was marked as low quality.

@stefanb
stefanb marked this pull request as ready for review September 19, 2026 07:17
@stefanb
stefanb requested a review from a team as a code owner September 19, 2026 07:17
Copilot AI lite review requested due to automatic review settings September 19, 2026 07:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Want to do some local analysis and testing here.

- Check that system temporary writes preserve private socket grants,
  child temporary paths and offline build restrictions.
- Keep shared temporary sockets denied while allowing sockets in each
  sandbox's private directory.
- Reuse the shared cache allowance on Linux to avoid duplicated rules.

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good! tweaked slightly and added another regression test. thanks!

@MikeMcQuaid
MikeMcQuaid added this pull request to the merge queue Sep 19, 2026
Merged via the queue into main with commit 14c54f7 Sep 19, 2026
51 checks passed
@MikeMcQuaid
MikeMcQuaid deleted the sandbox-offline-system-temp branch September 19, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants