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.
- 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.
- 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.
- 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.
- 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.
- 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.