GrapheneOS 2024083100 came with an interesting change:
kernel (6.1, 6.6): enable struct randomization in the full mode with a deterministic seed based on kernel commit timestamp (we plan to also incorporate the device family and eventually make the seed specific to each device model, but it will increase our build/testing workload)
Structure randomization was originally created by grsecurity, later partially mainlined by Kees Cook, and ported to clang 15 in 2022. As detailed in the documentation entry of GRKERNSEC_RANDSTRUCT:
If you say Y here, the layouts of a number of sensitive kernel structures (task, fs, cred, etc) and all structures composed entirely of function pointers (aka "ops" structs) will be randomized at compile-time. This can introduce the requirement of an additional infoleak vulnerability for exploits targeting these structure types.
I had reservations about the usefulness of this mitigations, as an attacker
could simply download all the GrapheneOS releases and gather the various
structure layouts, for every release which happens every week or so, for every
supported phone model. In theory, GrapheneOS could ship multiple kernel builds
and kexec a random one at boot time, or ship several ROM per release and not
make them public (like Tor is doing to distribute bridges), but
this would result in a complexity nightmare.
After discussing with Daniel Micay, I now see at least 3 different threat models for this feature, with various degrees of usefulness:
- a remote attacker with a kernel exploit: obviously, this might make their life harder, as it's likely non-trivial to fingerprint the exact phone model (Pixel 8 vs. Pixel 8 Pro) and kernel version remotely. Albeit remote code execution in the Linux kernel aren't that common, they're not unheard of. This likely adds complexity to some exploits, but not all, as an attacker able to remotely defeat KASLR is likely in a position to dump various interesting structures and infer their layout.
- a local attacker with a non-privileged foothold on the device, either via the web browser or the WiFi/Bluetooth/Cellular SoC: fingerprinting the device is likely not that hard, even though the low-hanging fruits like user-agents and friends are already obfuscated, so it doesn't mean it's necessarily easy to do so.
- a local attacker without a foothold on the device, doing non-consensual forensic extraction: identifying the exact device model is of course trivial, but identifying the exact kernel build less so, especially with the USB-C port handling hardening, the automatic turn-off of Bluetooth, … Moreover, GrapheneOS only has to keep the attacker out until the auto-reboot kicks in.
The performance impact is tiny, and enabling this feature was straightforward with only a couple of casts to fix in the kernel to make things fly, which are in the process of being upstreamed. Even if it's not a silver-bullet, there is no reason not to use it, especially as Micay puts it: "[attackers] will increasingly target the kernel as userspace security continues to rapidly improve while kernel security barely does."