Skip to content

Fix Apple Silicon SIGBUS writing native-module literals (JIT W^X)#1781

Closed
dg1sbg wants to merge 1 commit into
clasp-developers:mainfrom
dg1sbg:fix/apple-silicon-jit-wx-literals
Closed

Fix Apple Silicon SIGBUS writing native-module literals (JIT W^X)#1781
dg1sbg wants to merge 1 commit into
clasp-developers:mainfrom
dg1sbg:fix/apple-silicon-jit-wx-literals

Conversation

@dg1sbg

@dg1sbg dg1sbg commented May 31, 2026

Copy link
Copy Markdown
Contributor

Problem

On Apple Silicon the LLVM ORC JITLink code/data slab is mapped MAP_JIT and is write-protected (execute mode) per-thread by default; a thread must call pthread_jit_write_protect_np(false) before it may write into that memory. clasp stores Lisp object pointers into each native module's literals vector, which lives in this JIT memory. Several of those stores were not bracketed by a switch to write mode, so on Apple Silicon they fault with SIGBUS (EXC_BAD_ACCESS code=2, KERN_PROTECTION_FAILURE).

This manifests as a crash in loadltv::attr_clasp_module_native while loading freshly compiled native FASLs during the "Compiling Clasp native image" bootstrap step, and at core::core__literals_vset more generally. On x86-64 and Linux the page is genuinely RWX, so the bug is latent there.

Fix

The helpers JITDataReadWriteMaybeExecute() / JITDataReadExecute() already exist for exactly this purpose. This wraps the three native-module literal write sites with them:

  • src/core/compiler.cc core__literals_vset — the store into the literals vector.
  • src/core/loadltv.cc op_setf_literals (lits[i] = scf) — replacing a bytecode function with its native simple-fun.
  • src/core/loadltv.cc attr_clasp_module_native (lits[i] = value loop) — filling a freshly loaded native module's literals; the get_ltv read stays in execute mode and only the store switches to write mode.

Verification

On Apple M5 / macOS 15 / LLVM 22.1.5: before the fix, (require :asdf) against the bytecode image SIGBUS'd at compiler.cc:213; with the first wrap the fault moved to loadltv.cc; with all three, (require :asdf) loads cleanly and the native image (base.nfasl) builds and boots — which previously aborted at the native-image compile step.

🤖 Generated with Claude Code

On Apple Silicon the LLVM ORC JITLink code/data slab is mapped MAP_JIT and
is write-protected (execute mode) per-thread by default; a thread must call
pthread_jit_write_protect_np(false) before it may write into that memory.
clasp stores Lisp object pointers into each native module's literals vector,
which lives in this JIT memory. Several of those stores were not bracketed by
a switch to write mode, so on Apple Silicon they fault with SIGBUS
(EXC_BAD_ACCESS code=2, KERN_PROTECTION_FAILURE). This manifests as a crash
in loadltv::attr_clasp_module_native while loading freshly compiled native
FASLs during the "Compiling Clasp native image" bootstrap step, and at
core::core__literals_vset more generally. (On x86-64 and Linux the page is
genuinely RWX, so the bug is latent there.)

The helpers JITDataReadWriteMaybeExecute()/JITDataReadExecute() already exist
for exactly this purpose. Bracket the three native-module literal write sites
with them:

- src/core/compiler.cc core__literals_vset: the store into the literals vector.
- src/core/loadltv.cc op_setf_literals (lits[i] = scf): replacing a bytecode
  function with its native simple-fun.
- src/core/loadltv.cc attr_clasp_module_native (lits[i] = value loop): filling
  a freshly loaded native module's literals; the get_ltv read stays in execute
  mode and only the store switches to write mode.

Verified on Apple M5 / macOS 15 / LLVM 22.1.5: before, (require :asdf) against
the bytecode image SIGBUS'd at compiler.cc:213; with the first wrap the fault
moved to loadltv.cc, and with all three (require :asdf) loads cleanly and the
native image (base.nfasl) builds and boots, which previously aborted at the
native-image compile step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Bike

Bike commented Jun 11, 2026

Copy link
Copy Markdown
Member

Everything here appears to duplicate parts of #1768. I do appreciate all these changes but they would be much easier to merge if better organized.

@Bike Bike closed this Jun 11, 2026
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.

2 participants