GrapheneOS
These are the features of GrapheneOS beyond what’s provided by version 14 of the Android Open Source Project. It only covers our improvements to AOSP and not baseline features. This section doesn’t list features like the standard app sandbox, verified boot, exploit mitigations (ASLR, SSP, Shadow Call Stack, Control Flow Integrity, etc.), permission system (foreground-only and one-time permission grants, scoped file access control, etc.) and so on but rather only our improvements to modern Android. We plan on providing a separate page listing the improvements we’ve contributed to Android since those features aren’t listed here despite being a substantial portion of our overall historical work.
Defending against exploitation of unknown vulnerabilities
GrapheneOS focused on protecting users against attackers exploiting unknown (0 day) vulnerabilities. Patching vulnerabilities doesn’t protect users before the vulnerability is known to the vendor and has a patch developed and shipped.
Unknown (0 day) vulnerabilities are much more widely used than most realize to exploit users not just in targeted attacks but in broad deployments. Project Zero maintains a spreadsheet tracking zero day exploitation detected in the wild. This is only a peek into what’s happening since it only documents cases where the attackers were caught exploiting users, often because the attacks are not targeted but rather deployed on public websites, etc.
The first line of defense is attack surface reduction. Removing unnecessary code or exposed attack surface eliminates many vulnerabilities completely. GrapheneOS avoids removing any useful functionality for end users, but we can still disable lots of functionality by default and require that users opt-in to using it to eliminate it for most of them. An example we landed upstream in Android is disallowing using the kernel’s profiling support by default, since it was and still is a major source of Linux kernel vulnerabilities. Profiling is now only exposed to apps for developers who enable developer tools, enable the Android Debug Bridge (ADB) and then use profiling tools via ADB. It’s also only enabled until the next boot. This isn’t listed below since it’s one of the features we got implemented in Android itself.
The next line of defense is preventing an attacker from exploiting a vulnerability, either by making it impossible, unreliable or at least meaningfully harder to develop. The vast majority of vulnerabilities are well understood classes of bugs and exploitation can be prevented by avoiding the bugs via languages/tooling or preventing exploitation with strong exploit mitigations. In many cases, vulnerability classes can be completely wiped out while in many others they can at least be made meaningfully harder to exploit. Android does a lot of work in this area, and GrapheneOS has helped to advance this in Android and the Linux kernel. It takes an enormous amount of resources to develop fundamental fixes for these problems and there’s often a high performance, memory or compatibility cost to deploying them. Mainstream operating systems usually don’t prioritize security over other areas. GrapheneOS is willing to go further, thus we offer toggles for users to choose the compromises they prefer instead of forcing it on them. In the meantime, weaker less complete exploit mitigations can still provide meaningful barriers against attacks as long as they’re developed with a clear threat model. GrapheneOS is heavily invested in many areas of developing these protections: developing/deploying memory safe languages/libraries, static/dynamic analysis tooling and many kinds of mitigations.
The final line of defense is containment through sandboxing at various levels: fine-grained sandboxes around a specific context like per site browser renderers, sandboxes around a specific component like Android’s media codec sandbox and app/workspace sandboxes like the Android app sandbox used to sandbox each app which is also the basis for user/work profiles. GrapheneOS improves all of these sandboxes through fortifying the kernel and other base OS components along with improving the sandboxing policies.
Preventing an attacker from persisting their control of a component or the OS/firmware through verified boot and avoiding trust in persistent state also helps to mitigate the damage after a compromise has occurred.
Remote code execution vulnerabilities are the most serious and allow an attacker to gain a foothold on the device or even substantial control over it remotely. Local code execution vulnerabilities allow breaking out of a sandbox including the app sandbox or browser renderer sandbox after either compromising an app/browser renderer remotely, compromising an app’s supply chain or getting the user to install a malicious app. Many other kinds of vulnerabilities exist but most of what we’re protecting against falls into these two broad categories.
The vast majority of local and remote code execution vulnerabilities are memory corruption bugs caused by memory unsafe languages or rare low-level unsafe code in an otherwise memory safe language. Most of the remaining issues are caused by dynamic code execution/loading features. Our main focus is on preventing or raising the difficulty of exploiting memory corruption bugs followed by restricting dynamic code execution both to make escalation from a memory corruption bug harder and to directly mitigate bugs caused by dynamic code loading/generation/execution such as a JIT compiler bug or a plugin loading vulnerability.