mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
fix(release): add missing Intel V8 signing entitlement (#30953)
## Why Intel macOS release binaries crash on the first Code Mode tool call while V8 creates its code range. The x86_64 V8 allocator later makes a non-`MAP_JIT` reservation executable, which Hardened Runtime rejects when the signature contains only `com.apple.security.cs.allow-jit`. Tracks [SE-8006](https://linear.app/openai/issue/SE-8006/intel-macos-codex-cli-crashes-in-v8-startup-on-gpt-56-sol-tool-calls). Fixes #28390. ## What - add an expanded entitlement profile only for x86_64 `codex` and `codex-app-server`, the release binaries that link V8 - keep arm64 and `codex-responses-api-proxy` on the existing narrower profile - share one fail-closed target/binary selector between signing and final verification - verify the expected Mach-O architecture and exact entitlement dictionary for the signed binary, tar.gz, zstd, package, and DMG copies ## Verification - `just test-github-scripts` (34 tests) - `UV_CACHE_DIR=/private/tmp/codex-uv-cache just fmt-check` - `bash -n .github/scripts/macos-signing/select_codex_entitlements.sh` - `plutil -lint` on both entitlement profiles - parsed `rust-release.yml` as YAML - `git diff --check` - ad-hoc Hardened Runtime signing smoke on an x86_64 Mach-O slice: strict `codesign` verification passed; the Codex profile contained exactly both keys and the proxy profile retained exactly `allow-jit` ## Release validation Run a native Intel smoke of the final Developer ID-signed x86_64 Codex binary through V8 isolate creation before shipping. PR #30849 is diagnostic scaffolding, but its non-sandbox release job currently fails in the harness before V8 starts, so it is not counted as coverage here.
This commit is contained in:
10
.github/scripts/macos-signing/codex-app-server.entitlements.plist
vendored
Normal file
10
.github/scripts/macos-signing/codex-app-server.entitlements.plist
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
10
.github/scripts/macos-signing/codex-code-mode-host.entitlements.plist
vendored
Normal file
10
.github/scripts/macos-signing/codex-code-mode-host.entitlements.plist
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
8
.github/scripts/macos-signing/codex-responses-api-proxy.entitlements.plist
vendored
Normal file
8
.github/scripts/macos-signing/codex-responses-api-proxy.entitlements.plist
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -4,5 +4,7 @@
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
37
.github/scripts/test_macos_signing_entitlements.py
vendored
Normal file
37
.github/scripts/test_macos_signing_entitlements.py
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import plistlib
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SIGNING_DIR = ROOT / ".github" / "scripts" / "macos-signing"
|
||||
ALLOW_JIT = "com.apple.security.cs.allow-jit"
|
||||
ALLOW_UNSIGNED_EXECUTABLE_MEMORY = (
|
||||
"com.apple.security.cs.allow-unsigned-executable-memory"
|
||||
)
|
||||
|
||||
|
||||
class MacosSigningEntitlementsTest(unittest.TestCase):
|
||||
def load(self, binary: str) -> dict[str, bool]:
|
||||
path = SIGNING_DIR / f"{binary}.entitlements.plist"
|
||||
with path.open("rb") as file:
|
||||
return plistlib.load(file)
|
||||
|
||||
def test_v8_binaries_allow_unsigned_executable_memory(self) -> None:
|
||||
expected = {
|
||||
ALLOW_JIT: True,
|
||||
ALLOW_UNSIGNED_EXECUTABLE_MEMORY: True,
|
||||
}
|
||||
for binary in ["codex", "codex-app-server", "codex-code-mode-host"]:
|
||||
with self.subTest(binary=binary):
|
||||
self.assertEqual(self.load(binary), expected)
|
||||
|
||||
def test_responses_proxy_keeps_existing_entitlements(self) -> None:
|
||||
self.assertEqual(
|
||||
self.load("codex-responses-api-proxy"),
|
||||
{ALLOW_JIT: True},
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
38
.github/workflows/rust-release.yml
vendored
38
.github/workflows/rust-release.yml
vendored
@@ -573,6 +573,12 @@ jobs:
|
||||
zstd -d --stdout "$unsigned_path" >"$signed_path"
|
||||
chmod 0755 "$signed_path"
|
||||
|
||||
entitlements="${GITHUB_WORKSPACE}/.github/scripts/macos-signing/${binary}.entitlements.plist"
|
||||
if [[ ! -f "$entitlements" ]]; then
|
||||
echo "Entitlements file $entitlements not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
.github/scripts/macos-signing/sign_macos_code.sh \
|
||||
--target "$signed_path" \
|
||||
--identity unused \
|
||||
@@ -580,7 +586,7 @@ jobs:
|
||||
--identifier "$binary" \
|
||||
--options runtime \
|
||||
--timestamp true \
|
||||
--entitlements .github/scripts/macos-signing/codex.entitlements.plist
|
||||
--entitlements "$entitlements"
|
||||
|
||||
mkdir -p "${report_dir}/${binary}"
|
||||
rcodesign print-signature-info "$signed_path" \
|
||||
@@ -974,15 +980,29 @@ jobs:
|
||||
|
||||
target="${{ matrix.target }}"
|
||||
packaged_dir="dist/${target}"
|
||||
expected_entitlements="${GITHUB_WORKSPACE}/.github/scripts/macos-signing/codex.entitlements.plist"
|
||||
case "$target" in
|
||||
aarch64-apple-darwin) expected_arch="arm64" ;;
|
||||
x86_64-apple-darwin) expected_arch="x86_64" ;;
|
||||
*)
|
||||
echo "Unexpected macOS target: $target"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
verify_signed_binary() {
|
||||
local path="$1"
|
||||
local actual_entitlements normalized_actual normalized_expected
|
||||
local binary="$2"
|
||||
local actual_entitlements expected_entitlements normalized_actual normalized_expected
|
||||
|
||||
chmod 0755 "$path"
|
||||
lipo "$path" -verify_arch "$expected_arch"
|
||||
codesign --verify --strict --verbose=2 "$path"
|
||||
|
||||
expected_entitlements="${GITHUB_WORKSPACE}/.github/scripts/macos-signing/${binary}.entitlements.plist"
|
||||
if [[ ! -f "$expected_entitlements" ]]; then
|
||||
echo "Expected entitlements file $expected_entitlements not found"
|
||||
exit 1
|
||||
fi
|
||||
actual_entitlements="$(mktemp)"
|
||||
normalized_actual="$(mktemp)"
|
||||
normalized_expected="$(mktemp)"
|
||||
@@ -995,7 +1015,7 @@ jobs:
|
||||
|
||||
for binary in ${{ matrix.binaries }}; do
|
||||
binary_path="${RUNNER_TEMP}/signed-binaries/${binary}"
|
||||
verify_signed_binary "$binary_path"
|
||||
verify_signed_binary "$binary_path" "$binary"
|
||||
|
||||
# The app-server package contains the host, but its standalone archives
|
||||
# are omitted above to avoid duplicate release asset names.
|
||||
@@ -1007,11 +1027,11 @@ jobs:
|
||||
rm -rf "$direct_archive_dir"
|
||||
mkdir -p "$direct_archive_dir"
|
||||
tar -xzf "${packaged_dir}/${binary}-${target}.tar.gz" -C "$direct_archive_dir"
|
||||
verify_signed_binary "${direct_archive_dir}/${binary}-${target}"
|
||||
verify_signed_binary "${direct_archive_dir}/${binary}-${target}" "$binary"
|
||||
|
||||
direct_zstd_path="${RUNNER_TEMP}/${binary}-${target}-from-zstd"
|
||||
zstd -d --stdout "${packaged_dir}/${binary}-${target}.zst" >"$direct_zstd_path"
|
||||
verify_signed_binary "$direct_zstd_path"
|
||||
verify_signed_binary "$direct_zstd_path" "$binary"
|
||||
done
|
||||
|
||||
case "${{ matrix.bundle }}" in
|
||||
@@ -1033,8 +1053,8 @@ jobs:
|
||||
rm -rf "$package_dir"
|
||||
mkdir -p "$package_dir"
|
||||
tar -xzf "${packaged_dir}/${package_stem}-${target}.tar.gz" -C "$package_dir"
|
||||
verify_signed_binary "${package_dir}/bin/${package_entrypoint}"
|
||||
verify_signed_binary "${package_dir}/bin/codex-code-mode-host"
|
||||
verify_signed_binary "${package_dir}/bin/${package_entrypoint}" "$package_entrypoint"
|
||||
verify_signed_binary "${package_dir}/bin/codex-code-mode-host" "codex-code-mode-host"
|
||||
|
||||
if [[ "${{ matrix.verify_dmg }}" != "true" ]]; then
|
||||
exit 0
|
||||
@@ -1060,7 +1080,7 @@ jobs:
|
||||
trap cleanup_mount EXIT
|
||||
|
||||
for binary in ${{ matrix.binaries }}; do
|
||||
verify_signed_binary "${mount_dir}/${binary}"
|
||||
verify_signed_binary "${mount_dir}/${binary}" "$binary"
|
||||
done
|
||||
|
||||
cleanup_mount
|
||||
|
||||
Reference in New Issue
Block a user