The NX bit is not just about security

A developer debugging a bare-metal hypervisor on ARM64 for postmarketOS encounters a mysterious bug: enabling the CTR_EL0 intercept causes random system lockups. After ruling out emulation errors and out-of-spec hardware, they discover that the issue is caused by speculative execution of a dynamic branch to a function pointer, which can mispredict to address 0x0 and trigger a fault. The fix involves using a static branch instead, highlighting that the NX bit and speculative execution have implications beyond security.

How do the two instructions differ? `blr x0` is dynamic, `bl get_ctr_el0` is static. Dynamic dispatches employ branch prediction, static branches know where they lead ahead of time and cannot mispredict.
  1. repiret

    "Speculative instruction fetch to device memory crashes device" is in my list of favorite bugs ever found. I had ran into it on a Cortex-M4 without an MPU, so I couldn't even fix the memory attributes, the only solution was to gerrymander the compiled code to prevent the speculative fetch.

    It's crazy that Armv8/9-A permits speculative instruction fetches to device memory. Crazy enough that I had to look it up to believe it:

    "Hardware does not prevent speculative instruction fetches from a memory location with any of the Device memory attributes unless the memory location is also marked as execute-never for all Exception levels." - ARM DDI 0487K.a § B2.15.2

    The Armv7-M spec is less clear. It does say: "The architecture does not permit speculative accesses to memory marked as Device," in contrast to Armv8/9-A which qualifies a similar statement with "data accesses". But then it later says "To ensure correctness, read-sensitive locations must be marked as non-executable". (This is all from ARM DDI 0403E.e § A3.5.7)

    I don't have v7-A older handy to compare what they say.

  2. asveikau

    On this theme of it not being just about security: if you have a bug like a use after free and it happens to cover a function pointer, the nx bit can ensure that when you follow that pointer through a call, you get a clean trap as close to the failure point as possible. If it blindly executed stale bytes as code, maybe the crash and stack trace doesn't look as nice.

    But then, a lot of correctness bugs like that are also security problems.

  3. anyfoo

    Nowadays, it's considered bad form to map anything at 0... you now know one of the reasons why!

    In the case that I know that has a 1:1 identity mapping of the address space, the physical address space doesn't actually have anything at 0 either, so it still doesn't need to be mapped.

  4. mubbicles

    I appreciated this article. I've asked our CSP guy the difference, but this helps me better understand device vs ns. I also appreciate that this doesn't appear AI written.

  5. tripdout

    I really wish I understood this, it hits a bunch of topics that I've heard of and are/sound interesting, but I don't know enough to follow it. I don't get the link between the NX bit (which I get) and speculative access.

More from this day

2026-09-07