ITADN

Crashe at scan time on macOS 26 (Tahoe) under Hardened Runtime

#645Opentaher-mosbah 创建于 2026-04-28
T
taher-mosbahcommented
## Summary `yara-x` 1.15.0 crashes with `SIGKILL (Code Signature Invalid)` at **runtime** (not build time) on macOS 26 (Tahoe) when the binary is built with Hardened Runtime and does **not** hold the `com.apple.security.cs.allow-jit` entitlement. The crash occurs the first time a scan is performed, after rules have been compiled successfully. --- ## Environment | Property | Value | |----------|-------| | **yara-x** | 1.15.0 | | **wasmtime** | 43.0.1 | | **Rust** | 1.85.0 (stable) | | **macOS** | 26.0 (Tahoe, Darwin 25.x) | | **Architecture** | arm64 (Apple Silicon) | | **Hardened Runtime** | Enabled (required for macOS app distribution / notarization) | | **`allow-jit` entitlement** | **Not present** | Same crash reproduces on macOS 26 built with Xcode 26. Does **not** crash on macOS 13–15. --- ## Steps to Reproduce 1. Build a macOS app (arm64) with yara-x 1.15.0 and Hardened Runtime enabled — the standard requirement for notarized apps distributed outside the App Store. 2. At runtime, compile any YARA rule: ```rust let mut compiler = yara_x::Compiler::new(); compiler.add_source(r#"rule test { strings: $a = "hello" condition: $a }"#)?; let rules = compiler.build(); ``` 3. Create a scanner and scan any file: ```rust let mut scanner = yara_x::Scanner::new(&rules); scanner.scan_file("/path/to/any/file")?; // ← CRASH HERE ``` 4. Process is killed with `SIGKILL`. --- ## Crash Details **Signal:** `SIGKILL` **Crash reason:** `Code Signature Invalid` **Kernel error code:** `KERN_CODESIGN_ERROR (0x32)` / `EXC_BAD_ACCESS` **macOS Console / crash log excerpt:** ``` Exception Type: EXC_BAD_ACCESS (SIGKILL (Code Signature Invalid)) Exception Codes: KERN_CODESIGN_ERROR at 0x... Termination Reason: Namespace CODESIGNING, Code 0x2 (Invalid Page) ``` **System log:** ``` kernel: proc_enforce: ... code signature validation failed kernel: AMFI: ... denying write+execute permission for page ``` The crash occurs inside wasmtime/Cranelift internals on the first scan. Rule compilation completes normally; the crash is strictly at scan (JIT instantiation) time. --- ## Root Cause `yara-x` compiles YARA rule conditions to WebAssembly (WASM) and uses **wasmtime** with the Cranelift JIT backend to execute them. When a scan is initiated: 1. wasmtime's Cranelift JIT compiles the WASM module to **native ARM64 machine code** 2. This code is written into newly-allocated **RWX memory pages** at runtime 3. These pages are **not code-signed** — they are generated dynamically and cannot be signed ahead of time On macOS 26, the Hardened Runtime policy was tightened. The kernel now kills any process that attempts to execute code in unsigned RWX pages **unless** the process holds the `com.apple.security.cs.allow-jit` entitlement. This is a change from macOS 13–15 behavior, where the enforcement was less strict for processes already under Hardened Runtime. On macOS 26, the enforcement is unconditional: **no `allow-jit` entitlement → any unsigned executable page → SIGKILL**. --- ## Why `allow-jit` Is Not a Viable Solution The `com.apple.security.cs.allow-jit` entitlement: - Requires explicit approval from Apple for App Store apps - Weakens the security posture of the entire process — any code injection attack can now execute arbitrary JIT-compiled code, not just the app's own - Is incompatible with certain app distribution models (e.g., managed enterprise security software distributed via MDM that must not hold elevated entitlements) - Is a binary switch: granting it affects the whole process, not just the yara-x scanner For security-sensitive applications, this entitlement is not acceptable. --- ## Fix wasmtime 28.0+ (included in wasmtime 43.0.1 via the `pulley` cargo feature) ships a **Pulley bytecode interpreter** as an alternative backend. By targeting `"pulley64"` instead of the native host, Cranelift compiles WASM → Pulley bytecode. The Pulley interpreter — which is compiled ahead-of-time, code-signed as part of the binary, and contains no dynamic code generation — executes the bytecode at runtime. **No unsigned RWX pages are created → no `allow-jit` needed → works on macOS 13–26.** ### Changes required in `lib/Cargo.toml` Add `"pulley"` to the wasmtime features: ```toml wasmtime = { workspace = true, default-features = false, features = [ "cranelift", # keep: needed for the compilation API "runtime", "pulley", # ADD: enables the Pulley interpreter backend ] } ``` ### Changes required in `lib/src/wasm/mod.rs` After `config.cranelift_opt_level(...)`, add one line to redirect Cranelift's output from native machine code to Pulley bytecode: ```rust config.cranelift_opt_level(runtime::OptLevel::SpeedAndSize); // Target Pulley instead of the native host so Cranelift compiles WASM to // Pulley bytecode rather than native machine code. The Pulley interpreter // (pre-compiled, code-signed, part of the binary) executes the bytecode at runtime. // This removes the requirement for the com.apple.security.cs.allow-jit entitlement // and makes yara-x compatible with macOS Hardened Runtime on macOS 13–26. let pulley_target = if cfg!(target_pointer_width = "64") { "pulley64" } else { "pulley32" }; config.target(pulley_target).expect("valid Pulley target triple"); ``` ### Complete diff ```diff --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -... @@ wasmtime = { workspace = true, default-features = false, features = [ "cranelift", "runtime", + "pulley", ] } --- a/lib/src/wasm/mod.rs +++ b/lib/src/wasm/mod.rs @@ -... @@ config.cranelift_opt_level(runtime::OptLevel::SpeedAndSize); + // Use Pulley interpreter to avoid unsigned JIT pages (macOS 26 Hardened Runtime). + let pulley_target = if cfg!(target_pointer_width = "64") { "pulley64" } else { "pulley32" }; + config.target(pulley_target).expect("valid Pulley target triple"); config.epoch_interruption(true); ``` --- ## Verification After applying the patch: - ✅ Rule compilation: no change in behavior - ✅ Scanning on macOS 26 (arm64, Hardened Runtime, no `allow-jit`): **no crash** - ✅ Scanning on macOS 13–15: **no crash** (Pulley works on all macOS versions) - ✅ No `com.apple.security.cs.allow-jit` entitlement required - ✅ All existing tests pass --- ## Performance Impact The Pulley interpreter is slower than Cranelift's native JIT. Rough measurement on Apple M-series: | Benchmark | JIT (before) | Pulley (after) | Delta | |-----------|-------------|----------------|-------| | `reverse_single` rule set | ~1500 ops/s (estimated) | **478 ops/s** | ~3× slower | | `reverse_seperate` rule set | ~800 ops/s (estimated) | **271 ops/s** | ~3× slower | For file scanning use cases, the absolute time per file scan remains in the sub-millisecond range on modern hardware. The throughput reduction is acceptable for applications that prioritize compatibility over maximum throughput. For applications where JIT performance is critical, the existing behavior should remain available behind a compile-time feature flag (e.g., `features = ["jit"]`). --- ## Suggested Feature Flag Design Rather than making Pulley unconditional, an ideal fix would introduce a cargo feature: ```toml [features] # Use Pulley interpreter instead of Cranelift JIT. # Required on macOS with Hardened Runtime (no allow-jit entitlement). # Slower (~3x) but compatible with all macOS security configurations. pulley-interpreter = ["wasmtime/pulley"] ``` And in `lib/src/wasm/mod.rs`: ```rust #[cfg(feature = "pulley-interpreter")] { let pulley_target = if cfg!(target_pointer_width = "64") { "pulley64" } else { "pulley32" }; config.target(pulley_target).expect("valid Pulley target triple"); } ``` This lets callers opt in to Pulley on restricted platforms while preserving JIT on platforms where it is permitted. --- ## Related - wasmtime Pulley docs: https://docs.wasmtime.dev/contributing-architecture.html - Pulley RFC: https://github.com/bytecodealliance/wasmtime/issues/9783 - Apple Hardened Runtime entitlements: https://developer.apple.com/documentation/security/hardened_runtime - `com.apple.security.cs.allow-jit`: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-jit
1 条评论