[PATCH v19 00/14] arm64: entry: Convert to Generic Entry

Kees Cook kees at kernel.org
Tue Sep 22 11:29:48 PDT 2026


On Tue, Sep 22, 2026 at 11:54:56AM +0800, Jinjie Ruan wrote:
> This series converts arm64 to the generic entry infrastructure.

With my trusty LLM driving a bunch of orchestration, I gave this
a fairly wide before/after run under qemu-system-aarch64, trying
to cover things beyond the ptrace/breakpoints/abi/fp/vDSO list in
the cover letter. But since this is all emulated, it's mainly basic
correctness coverage, without any meaningful concurrency coverage. I
just wanted to find stuff that maybe hadn't been exercised yet.

tl;dr: the only behavioral difference I could find anywhere is what
patch 1 fixed, and I found no meaningful regressions.

Setup: the series applies cleanly to v7.3-rc2 (I only noticed later
the series was actually based on -rc4), so "base" is v7.3-rc2 and
"patched" is the same tree plus the 14 patches, with each pair built
from the same config with GCC 16.1.0. The guest was QEMU 11.1.0:

  -M virt,gic-version=max,mte=on -cpu max,pauth-impdef=on -smp 4

I used four kernel configs, each built for both base and patched:

  plain    defconfig + SECCOMP, USER_NS, AUDITSYSCALL, KUNIT_ALL_TESTS
  lockdep  plain + PROVE_LOCKING, TRACE_IRQFLAGS, PROVE_RCU,
             DEBUG_ATOMIC_SLEEP, DEBUG_PREEMPT
  hooks    lockdep + the options that add work to syscall entry/exit:
             FTRACE_SYSCALLS, KSTACK_ERASE, LKDTM, NO_HZ_FULL,
             CONTEXT_TRACKING_USER (booted nohz_full=2-3)
  rt       hooks + PREEMPT_RT

and ran userspace tests from both an AArch64 and an AArch32 (armhf)
userspace, since the latter takes the is_compat_task() side of
ptrace_save_reg() and the cover letter didn't mentioned COMPAT.

Results were identical on base and patched (pass/fail/skip):

  seccomp_bpf                   98/1/12 (AArch64), 94/2/15 (AArch32)
  ptrace selftests              get/set_syscall_info, peeksiginfo,
                                vmaccess: no change
  breakpoint_test_arm64         213/0/0
  rseq basic, percpu_ops        pass
  KUnit (KUNIT_ALL_TESTS)       ~1500 results, no change
  MTE selftests                 117/0/0 (but see below)
  strace 6.18 test suite        81/6/0 (ptrace/seccomp tests)
  audit (a0 filter)             pass
  syscall tracepoints           pass (argument recorded correctly)
  lkdtm KSTACK_ERASE            pass
  lkdtm stack-entropy.sh        7 bits
  lockdep/RCU                   0 splats in lockdep, hooks, and rt

The only delta I could find was the expected one:

  sysemu_singlestep (new test)  FAIL on base, pass on patched

So patch 1 fixes the described bug, but it had no in-tree test, so I
wrote one. I'll send that separately.

Various things I noticed along the way:

- The set of traceable symbols on the syscall path changes:

    removed: syscall_trace_enter, syscall_trace_exit,
             el0_svc_common.constprop.0
    added:   trace_syscall_enter, trace_syscall_exit,
             syscall_enter_audit

  These aren't ABI, and the new names are the same ones x86,
  riscv, loongarch, and s390 already expose, so this is really an
  improvement. But it might be worth a sentence in the cover letter,
  since patch 13's "[Compatibility]" note could be read as "nothing
  observable changes", which isn't _strictly_ true. :)

- Syscall-path stack use grows by 48 bytes. Base do_el0_svc() has a
  16-byte frame and calls el0_svc_common() with a 48-byte frame; with
  patch 14 inlining it (plus the __always_inline generic enter/exit
  helpers), do_el0_svc() grows from 48 to 808 bytes and 1 to 13 calls,
  with a single 112-byte frame: 112 - 64 = 48. lkdtm KSTACK_ERASE with
  randomize_kstack_offset=off confirms exactly that at runtime (2288 vs
  2336 bytes, ten samples, no variance), on both userspaces and in
  both the hooks and rt configs. It's small, but it's not mentioned as
  a (minor) trade-off for patch 14's ~1% speedup. Other size deltas:
  .text +12K, ptrace.o -3.6K, and +3.1K of shared
  kernel/entry/syscall-common.o (all seems expected/unremarkable).

- Patch 14 also moves that inlined code into .noinstr.text (+896
  bytes). Since arm64 doesn't have objtool noinstr validation like x86,
  I checked the images directly: no calls from .noinstr.text to
  instrumentation outside the noinstr range, and no __mcount_loc entry
  falls inside it, on either kernel. But we don't seem to have anything
  that will continue to enforce this?

- CONFIG_DEBUG_RSEQ "depends on ... && !GENERIC_ENTRY", so patch 13
  makes it unselectable on arm64 and rseq_syscall() becomes the no-op
  stub. Patches 5 and 9 carefully reposition that call, and then patch
  13 removes its effect. Not a bug (generic entry does the equivalent
  via rseq_debug_enabled, and I can see __rseq_debug_syscall_return()
  called from the new do_el0_svc()), but patch 5 reads like a
  standalone fix that disappears eight patches later, so a note in the
  cover letter might help make sense of this?

- The cover letter includes the gvisor SUD numbers, but SUD needs
  ARCH_SUPPORTS_SYSCALL_USER_DISPATCH (as well as GENERIC_ENTRY),
  and with the SUD patch dropped in v18, arm64 doesn't select it,
  so CONFIG_SYSCALL_USER_DISPATCH can't be enabled yet. (rseq slice
  extension is similarly blocked on HAVE_GENERIC_TIF_BITS.) A reader
  could take those numbers as something this series delivers.

- arm64 defconfig has FTRACE_SYSCALLS=n, which means
  SYSCALL_WORK_SYSCALL_TRACEPOINT can never be set and the tracepoint
  paths touched by patches 2, 3, and 9 aren't even built. So anyone
  testing with defconfig isn't exercising those; you may want to test
  with it enabled. (Also, compat tasks never hit syscall tracepoints
  on arm64 at all, due to ARCH_TRACE_IGNORE_COMPAT_SYSCALLS, so that
  path can only be covered by native tasks, but that's unchanged by
  the series.)

- I verified that the entry work ordering is unchanged. It looks
  correct to me: arm64 did ptrace, seccomp, tracepoint, audit; generic
  entry does the same. (It also checks SUD and rseq-slice before those,
  but neither can be enabled on arm64 yet.)

Suggestions:

- rr: Since you have real hardware, could you also run the rr test
  suite? rr has been the most sensitive ptrace user I've encountered,
  and it depends on exactly the syscall-stop and single-step behavior
  this series touches. I couldn't run it: QEMU's TCG PMU exposes PMUv3
  but only counts CPU_CYCLES (BR_RETIRED always reads 0, on every
  CPU model I tried), so rr aborts in check_working_counters(). If
  you do, note that you'll need "proc_mem.force_override=always" (or
  CONFIG_PROC_MEM_ALWAYS_FORCE=y), or three rr tests fail for unrelated
  reasons (rr-debugger/rr#4093). Build and test instructions are here:
  https://github.com/rr-debugger/rr/wiki/Building-And-Installing#tests

- MTE: I didn't see MTE in the cover letter's test list, and it seems
  relevant since patch 13 puts _TIF_MTE_ASYNC_FAULT into
  ARCH_EXIT_TO_USER_MODE_WORK, so an async tag check fault lands
  on the reworked exit path. The MTE selftests showed no differences,
  but a third of them don't actually run for me under QEMU, so they'd
  be worth running on your hardware. (Testing MTE under QEMU saw
  check_mmap_options hang at test 5, the first test with tag checking
  on, and check_child_memory never produces output, on both base and
  patched, so only 117 of the 183 planned MTE tests ran. But this is,
  of course, a QEMU/selftest issue unrelated to the generic entry.)

So, for the series:

Tested-by: Kees Cook <kees at kernel.org>

-Kees

-- 
Kees Cook



More information about the linux-arm-kernel mailing list