macOS arm64 release links non-public liblzma/libbz2 → App Store rejection (2.5.1)

Open 💬 2 comments Opened Jun 15, 2026 by dppeak

Summary

The published codex-aarch64-apple-darwin binary dynamically links the non-public macOS system libraries /usr/lib/liblzma.5.dylib and /usr/lib/libbz2.1.0.dylib, and imports the symbols _lzma_code, _lzma_end, _lzma_stream_decoder, and _BZ2_bzDecompress*. Those dylibs ship on macOS but are not part of the public SDK, so any sandboxed / Mac App Store app that bundles this binary is rejected by App Review under Guideline 2.5.1 – Performance – Software Requirements.

The codex-x86_64-apple-darwin binary is clean — it links neither library and imports none of those symbols.

Real-world impact

This blocked a Mac App Store submission. App Review's response:

The app uses or references the following non-public or deprecated APIs: Contents/Helpers/codex — Symbols: _lzma_code, _lzma_end, _lzma_stream_decoder

Any macOS app that bundles the codex CLI for App Store / sandboxed distribution hits this. Because the x86_64 slice is already clean, the arm64 linkage looks like a build-environment artifact rather than a functional requirement.

Evidence

# arm64 slice of the universal release binary
$ otool -L codex
  ...
  /usr/lib/liblzma.5.dylib (compatibility version 6.0.0, current version 6.3.0)
  /usr/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.8)
  ...

$ nm -u -arch arm64 codex | grep -iE 'lzma|bz2'
  _BZ2_bzDecompress
  _BZ2_bzDecompressEnd
  _BZ2_bzDecompressInit
  _lzma_code
  _lzma_end
  _lzma_stream_decoder

# x86_64 slice — clean
$ nm -u -arch x86_64 codex | grep -iE 'lzma|bz2'
  (no output)
$ otool -L -arch x86_64 codex | grep -iE 'liblzma|libbz2'
  (no output)

Reproduced identically on every release from rust-v0.134.0 through rust-v0.139.0 (arm64 dirty, x86_64 clean).

Root cause

The symbols come transitively from lzma-sys / xz2 and bzip2-sys / bzip2 (pulled in via zip). Those -sys crates dynamically link the system liblzma / libbz2 when they're available in the build environment instead of statically compiling their bundled C sources. The macOS arm64 release build environment has them; the x86_64 build apparently doesn't, which is why the two slices diverge.

Proposed fix

Set the documented static-linking environment variables when building the macOS release (the arm64 build in particular) so the bundled C sources are compiled in statically:

LZMA_API_STATIC=1 BZIP2_STATIC=1 cargo build --release ...

This makes the arm64 build match the already-clean x86_64 build, with no source changes and no behavioral difference. (Alternatively, drop the bzip2 / lzma features from the zip dependency if they aren't actually needed.)

I verified locally that rebuilding the arm64 slice from the rust-v0.137.0 tag with LZMA_API_STATIC=1 BZIP2_STATIC=1 produces a functionally identical binary with zero references to liblzma/libbz2 and none of the flagged symbols — lipo'd together with the stock x86_64 release slice it passes App Review's static scan.

Environment

  • Affected artifact: codex-aarch64-apple-darwin (arm64 slice)
  • Versions checked: rust-v0.134.0 – rust-v0.139.0
  • macOS, Apple Silicon

View original on GitHub ↗

2 Comments

dppeak · 2 months ago

Still reproduces on the latest release (rust-v0.142.3) — and a correction to the BZIP2_STATIC=1 fix I proposed above, which turns out to be a no-op.

Bumping our bundled Codex from 0.137.0 to 0.142.3 hit this again, so the arm64-dirty / x86_64-clean split now spans rust-v0.134.0 → rust-v0.142.3. 0.142.3 also newly links libbz2 in via a new codex-core-plugins → zip 2.4.2 path (zip 2.4.2 enables the bzip2 feature by default).

Correction — BZIP2_STATIC=1 does nothing. Unlike lzma-sys (whose build.rs honors LZMA_API_STATIC), bzip2-sys has no environment variable. Its build.rs only compiles the bundled libbz2 statically when the static cargo feature is enabled; otherwise it pkg-config-probes and dynamically links /usr/lib/libbz2. A from-source build can look clean only because that probe happens to fail in the build environment — which is fragile and environment-dependent, not the env var doing the work. (lzma genuinely is fixed by LZMA_API_STATIC=1.)

Reliable, source-light fix — enable the static feature so bzip2-sys always compiles its bundled C in, independent of the build host:

# at a crate that pulls in zip/bzip2 (e.g. core-plugins or the cli crate):
bzip2 = { version = "0.5", features = ["static"] }   # bzip2 `static` => bzip2-sys/static

(or build zip with default-features = false and omit bzip2/xz if they aren't needed). Combined with LZMA_API_STATIC=1 for lzma, both slices then verify clean:

arm64:  clean (no lzma/bz2, system libs only)
x86_64: clean

Confirmed by rebuilding the 0.142.3 arm64 slice this way and lipo-ing it with the stock clean x86_64 release slice — the result passes App Review's static scan with none of the flagged _lzma_* / _BZ2_* symbols. Happy to open a PR against the macOS release workflow if that'd help.

dppeak · 2 months ago

I prepared the fix as a branch, but openai/codex doesn't accept pull requests from outside forks (org policy), so I can't open a PR. Here's the change so a maintainer can apply it directly — it's small and self-contained.

Branch: https://github.com/dppeak/codex/tree/fix/static-link-bzip2-lzma-macos

codex-rs/Cargo.toml — add to [workspace.dependencies]:

bzip2 = { version = "0.5", features = ["static"] }
xz2   = { version = "0.1", features = ["static"] }

codex-rs/core-plugins/Cargo.toml and codex-rs/core-skills/Cargo.toml (the crates that pull in zip) — reference them so the static feature is in the build graph:

bzip2 = { workspace = true }
xz2   = { workspace = true }

That's it (plus the 4-line Cargo.lock update). bzip2-sys/lzma-sys then compile their bundled C statically instead of linking /usr/lib/libbz2.dylib + /usr/lib/liblzma.dylib. No zip features are removed, so behavior is unchanged.

cargo tree -p codex-cli -e features confirms bzip2-sys feature "static" and lzma-sys feature "static" are both active, and a from-source arm64 build is otool/nm-clean of both libs on both slices. Happy to adjust if you'd prefer dropping the unused zip bzip2/xz features instead.