Skip to content

Fix #216: iOS SIGILL on pre-A13 devices (deviceCpu=apple-a12)#218

Open
jappeace-sloth wants to merge 10 commits intojappeace:developfrom
jappeace-sloth:test/ios-sigill-repro-216
Open

Fix #216: iOS SIGILL on pre-A13 devices (deviceCpu=apple-a12)#218
jappeace-sloth wants to merge 10 commits intojappeace:developfrom
jappeace-sloth:test/ios-sigill-repro-216

Conversation

@jappeace-sloth
Copy link
Copy Markdown

@jappeace-sloth jappeace-sloth commented Apr 26, 2026

Summary

  • Adds deviceCpu parameter (default "apple-a12") to lib.nix module level
  • Constrains all iOS/watchOS C compilation to avoid ARMv8.4+ ops (UDOT/SDOT) that crash on A12/A12X
  • Three layers covered:
    • Hatter cbits: -optc -mcpu flag on ghc -staticlib (device builds only)
    • Haskell dependency C sources: mkDerivation overlay in ios-deps.nix injects --ghc-option=-optc-mcpu
    • C library deps (gmp, libffi): NIX_CFLAGS_COMPILE override
  • Rewrites ios-sigill-check to compile canary through lib.compileIOSDeviceC — same GHC + flags as mkAppleStaticLib
  • Test exercises the actual build system instead of hardcoded -mcpu flags

Closes #216

Test plan

  • ios-sigill-check passes (canary compiled through build system has no UDOT)
  • iOS and watchOS CI jobs still pass
  • Linux CI (nix-build, android) unaffected

🤖 Generated with Claude Code

jappeace-sloth and others added 6 commits April 26, 2026 11:10
)

On Apple Silicon, GHC invokes clang without -mcpu, so clang defaults
to the host CPU and emits ARMv8.4+ instructions (UDOT/SDOT) that
crash on pre-A13 iOS devices (A12/A12X).

This adds a canary C function (uint8 dot-product loop) that
auto-vectorizes into UDOT at -O2, compiled with the same toolchain
as the iOS build. The test disassembles the output and checks for
UDOT/SDOT — expected to FAIL on Apple Silicon CI, proving the
vulnerability exists.

Prompt: reproduce issue jappeace#216 (SIGILL on pre-A13 ios devices) with a CI test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
runCommand doesn't provide a C compiler by default in the nix sandbox.

Prompt: fix reproduction test, cc was not found in CI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The nix CC wrapper targets generic aarch64, not the host CPU, so the
previous test did not produce UDOT.  Now explicitly compiles with
-mcpu=apple-m1 (simulating host-CPU targeting) and -mcpu=apple-a12
(the proposed fix), comparing the outputs to prove:
1. M1-targeting produces UDOT (the bug)
2. A12-targeting suppresses it (the fix)

Prompt: fix reproduction, nix cc wrapper uses generic aarch64 target

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CI showed otool printing UDOT as '.long 0x6e829480' instead of the
mnemonic. The UDOT IS being emitted by -mcpu=apple-m1, but otool
can't disassemble it. Match UDOT/SDOT vector encodings (0x[06]e8x94xx)
in addition to mnemonics.

Prompt: fix detection, otool shows .long for UDOT encoding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The reproduction test should visibly fail CI so the issue is
apparent from the commit status.

Prompt: remove continue-on-error so CI fails red

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add deviceCpu parameter (default "apple-a12") to iOS/watchOS build
pipeline. This constrains the instruction set to avoid ARMv8.4+ ops
(UDOT/SDOT) that crash on A12/A12X devices.

Three layers of C code are covered:
- Hatter's own cbits: -optc -mcpu flag on ghc -staticlib
- Haskell dependency C sources: mkDerivation overlay in ios-deps.nix
  injects --ghc-option=-optc-mcpu into configureFlags
- C library deps (gmp, libffi): NIX_CFLAGS_COMPILE override

The ios-sigill-check test is now a regression guard that passes when
-mcpu=apple-a12 suppresses UDOT (confirming the fix works).

Closes jappeace#216

Prompt: Implement the plan for fixing iOS SIGILL on pre-A13 devices (hatter#216)
Tokens: ~50k input, ~5k output

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jappeace-sloth jappeace-sloth changed the title Repro #216: CI test for iOS SIGILL on pre-A13 devices Fix #216: iOS SIGILL on pre-A13 devices (default -mcpu=apple-a12) Apr 26, 2026
jappeace-sloth and others added 2 commits April 27, 2026 08:02
Add deviceCpu parameter (default "apple-a12") to lib.nix module level.
This constrains all iOS/watchOS C compilation to avoid ARMv8.4+ ops
(UDOT/SDOT) that crash on A12/A12X devices.

Three layers of C code are covered:
- Hatter cbits: -optc -mcpu flag on ghc -staticlib (device only)
- Haskell dependency C sources: mkDerivation overlay in ios-deps.nix
  injects --ghc-option=-optc-mcpu into configureFlags
- C library deps (gmp, libffi): NIX_CFLAGS_COMPILE override

Rewrites ios-sigill-check to compile the canary through the same
GHC + flags as mkAppleStaticLib (via lib.compileIOSDeviceC), so the
test exercises the actual build system instead of hardcoded -mcpu flags.

Closes jappeace#216

Prompt: revert previous fix, rewrite reproduction test to use
hatter build system so the fix flows through naturally
Tokens: ~120k input, ~15k output

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jappeace-sloth jappeace-sloth changed the title Fix #216: iOS SIGILL on pre-A13 devices (default -mcpu=apple-a12) Fix #216: iOS SIGILL on pre-A13 devices (deviceCpu=apple-a12) Apr 27, 2026
jappeace-sloth and others added 2 commits April 27, 2026 20:28
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The deviceCpu=apple-a12 default was changing derivation hashes for all
builds (including simulator), causing nix cache misses and slower
watchOS simulator tests that timed out CI.

Now deviceCpu defaults to null everywhere (no flag applied). Only the
ios-sigill-check test in ci.nix explicitly passes deviceCpu=apple-a12
via libWithCpuFlag to prove the fix works. Consumers who need the
pre-A13 protection can pass deviceCpu="apple-a12" explicitly.

Prompt: make deviceCpu opt-in, only used by reproducer test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant