Skip to content

Fix callback function tests broken by modern GCC and Linux kernel NX protection#541

Open
mqcmd196 wants to merge 3 commits into
euslisp:masterfrom
mqcmd196:mqcmd196/fix-foreign
Open

Fix callback function tests broken by modern GCC and Linux kernel NX protection#541
mqcmd196 wants to merge 3 commits into
euslisp:masterfrom
mqcmd196:mqcmd196/fix-foreign

Conversation

@mqcmd196

Copy link
Copy Markdown
Contributor

Summary

  • test/test_foreign.c: set_ifunc and set_ffunc lacked return statements. GCC -O2 generated no retq, causing fall-through from set_ifuncset_ffunccall_ifuncjmp *g where g held the integer argument (e.g. 12345), crashing at address 0x3039.
  • lisp/l/eusforeign.l: The trampoline (podcode) lives in GC heap, which is non-executable on Linux kernel ≥ 5.8 (NX protection), even with -z execstack. Added defforeign c-mprotect via sys::sysmod and call it in foreign-pod :init to mark the trampoline page PROT_READ|PROT_WRITE|PROT_EXEC. Page address is computed with (* (floor (/ addr 4096)) 4096) to avoid a 32-bit truncation bug in (logand addr (lognot 4095)).
  • test/test-foreign.l: Uncommented and restructured callback tests as proper deftest forms. defun-c-callable must be at top level (not inside deftest) because it uninterns and re-interns the symbol; use (intern "LISP-IFUNC") at runtime instead of 'LISP-IFUNC to look up the re-interned foreign-pod.

Test plan

  • Run test/test-foreign.l with eusg on x86_64 Ubuntu 24.04 (GCC 13, kernel 6.17): all 19 tests pass including test-callback-integer and test-callback-float

🤖 Generated with Claude Code

mqcmd196 and others added 3 commits June 11, 2026 16:57
…protection

Three bugs prevented defun-c-callable / callback tests from working:

1. test/test_foreign.c: set_ifunc and set_ffunc lacked return statements.
   GCC -O2 generated no retq, causing fall-through from set_ifunc -> set_ffunc
   -> call_ifunc -> jmp *g where g was the integer argument (e.g. 12345),
   crashing at address 0x3039. Fixed by adding `return 0;` to both.

2. lisp/l/eusforeign.l: trampoline (podcode) lives in GC heap which is
   non-executable on Linux kernel >= 5.8 even with -z execstack. Fixed by
   adding defforeign for mprotect via sys::sysmod and calling it in
   foreign-pod :init to mark the trampoline page PROT_READ|PROT_WRITE|PROT_EXEC.
   Page address is computed with (* (floor (/ addr 4096)) 4096) to avoid
   a 32-bit truncation bug in (logand addr (lognot 4095)).

3. test/test-foreign.l: uncommented callback tests and restructured them
   as proper deftest forms. defun-c-callable must be at top level (not inside
   deftest) because it uninters and re-interns the symbol; quote-captured
   'LISP-IFUNC inside a deftest body would hold the pre-unintern symbol.
   Use (intern "LISP-IFUNC") at runtime to look up the re-interned foreign-pod.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
macOS (Intel x86_64) CI failed because euscomp reads eusforeign.l at
startup and the #+:x86_64 guard is true on macOS, but mprotect is not
findable via (system::find-entry "mprotect" (sys::sysmod)) on Darwin.
Fix: wrap defforeign in (when (system::find-entry ...)) so it is only
defined when mprotect is actually available in sysmod.
Similarly guard the mprotect call in foreign-pod :init with (fboundp
'c-mprotect) to skip it when the defforeign was not executed.

ARM/i386/RISC-V CI failed because the new callback tests run on all
platforms, but defun-c-callable trampolines are x86_64-specific
machine code. Fix: add #+:x86_64 reader guards to both defun-c-callable
and deftest forms for the callback tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#+:x86_64 is also true on macOS Intel, but there mprotect is not
available in sysmod so the GC heap cannot be made executable.
The callback tests crash on macOS even though the build no longer
fails. Use #+(and :x86_64 :linux) to limit them to Linux x86_64 only.

Co-Authored-By: Claude Sonnet 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