How Android moved from C/C++ to Rust, and the real numbers
TL;DR
Android didn't rewrite its old C/C++; it wrote new code in Rust and bridged the two with bindgen, cxx and AIDL. Memory bugs fell from 76% (2019) to below 20% (2025), with 1000x lower bug density, 4x fewer rollbacks and 25% faster reviews.
In 2019, 76% of the vulnerabilities in Android's security bulletins were memory safety bugs: buffer overflows, use-after-free, reads of uninitialised memory. By 2025 that share had dropped below 20%. Google didn't get there by rewriting tens of millions of lines of C/C++. It changed how new code gets written: new code goes into memory-safe languages, mainly Rust, while old code is left alone, hardened, and allowed to age. This article walks through each technical step they took (build, interop, component choice, firmware, training, measurement), with architecture diagrams and the officially published numbers.
The starting problem
- Memory bugs dominated severe vulnerabilities: in 2021 Google said they consistently made up about 70% of Android's high-severity vulnerabilities. In 2022 they were still 86% of critical-severity and 89% of remotely exploitable vulnerabilities.
- Sandboxing has limits: process isolation costs RAM and adds IPC latency. It doesn't remove bugs, and with high bug density attackers can still chain several bugs to escape.
- Fuzzing and sanitizers only catch symptoms: they see a bug only when the faulty state is actually triggered. Bugs were being found faster than they could be fixed, and some fixes were wrong.
- A full rewrite was impossible: the Android team said rewriting tens of millions of lines of code wasn't feasible, even with the whole team on it.
The core principle: vulnerabilities live in new code
Data from Android and Chromium shows that vulnerabilities decay exponentially, like a half-life. About 50% of memory bugs were in code less than a year old (2021 data). Five-year-old code has 3.4x to 7.4x lower vulnerability density than new code (Google Security blog, 2024).
That leads to the "Safe Coding" strategy: stop adding new unsafe code where it matters, instead of rewriting old code. Google's simulation used these assumptions: the codebase doubles every 6 years, the average vulnerability lifetime is 2.5 years, and moving new code to safe languages takes 10 years. The simulated result: total vulnerabilities still fall sharply, even while the amount of unsafe code keeps growing.
| Generation | Approach | Limitation |
|---|---|---|
| 1. Reactive patching | Wait for bugs to surface, then fix them | Ongoing cost, users stay exposed |
| 2. Exploit mitigation | Stack canaries, CFI, sandboxing | Performance cost, an arms race with attackers |
| 3. Proactive bug finding | Sanitizers, fuzzing | Treats symptoms, low coverage |
| 4. Safe Coding | Memory-safe languages, safe API design | Upfront investment in tooling and training |
Rollout timeline
| When | Milestone |
|---|---|
| ~2019 | Started prioritising memory-safe languages (Java/Kotlin, Rust) for new development |
| 2021 (Android 12) | AOSP officially supports Rust after about 18 months of groundwork; AIDL gains a Rust backend |
| 2022 (Android 13) | For the first time most new code is in memory-safe languages; about 21% of new native code is Rust; about 1.5 million lines of Rust in AOSP |
| 2023 (Android 14) | Protected VM firmware (pvmfw) rewritten in Rust, replacing U-Boot; Trusty TEE supports Trusted Apps written in Rust |
| 2024 | Memory bugs down to 24% of all vulnerabilities; Rust changes have less than half the rollback rate of C++ |
| 2025 | About 5 million lines of Rust; net Rust additions slightly exceed C++ in the first three quarters; Android's 6.12 kernel ships the first production Rust driver |
Architecture: where does Rust live in Android?
Everything from the Framework up is already memory-safe thanks to Java/Kotlin. The risk is concentrated in the native layers: system services, HALs, the kernel and firmware. That is exactly where Rust went. Components talk to each other over Binder/AIDL, so a Rust service can replace a C++ one without changing its callers.
How they did it, step by step
Step 1: Bring Rust into the build system (about 18 months)
- Soong invokes
rustcdirectly: Android needs hermetic, reproducible builds and tight control over compiler versions, flags and dependency locations. Cargo couldn't guarantee that at the time, and it rebuilds the whole dependency graph on every run. - No
build.rs: these scripts run arbitrary code on the build host and often read files outside the source tree. C dependencies are declared again ascc_librarymodules; code-generating scripts are replaced by arust_binaryused as a source generator.proc_macrois still allowed, because it's easier to sandbox and well scrutinised upstream. - Third-party crates are vendored into AOSP (under
external/rust/crates), with patches sent upstream where possible. - Dynamic linking by default on device: almost every binary shares the same crates, so shared dylibs save RAM and disk.
- Quality tooling on by default:
clippyand lints run by default, with fuzzers (with HWASAN/ASAN), benchmarks and code coverage. Android turns on integer overflow checks by default for Rust.
A Rust module in AOSP looks like this (condensed from the AOSP documentation example):
// external/rust/hello_rust/Android.bp
rust_binary {
name: "hello_rust",
srcs: ["src/hello_rust.rs"],
rustlibs: ["libsimple_printer"], // Soong picks rlib or dylib
}
// external/rust/libsimple_printer/Android.bp
rust_library {
name: "libsimple_printer", // must be lib<crate_name>
crate_name: "simple_printer",
srcs: ["src/lib.rs"],
}
source build/envsetup.sh
lunch aosp_arm64-eng
m hello_rust
A Rust module that links C/C++ libraries adds shared_libs or static_libs, just like a normal C++ module.
Step 2: Survey interop before writing the first line of Rust
New Rust must be able to call existing C/C++ libraries. The Android team classified the types exported by the most widely used C++ libraries (liblog, libbase, libutils, libcutils, libbinder…). The result: 81% of types were fully supported immediately. For Mainline modules the figure was 88%. Most of the rest were types they deliberately chose not to support.
(a) bindgen for C libraries: the build system generates bindings automatically from a wrapper header.
rust_bindgen {
name: "libbuzz_bindgen",
crate_name: "buzz_bindgen",
wrapper_src: "libbuzz_wrapper.h", // header that includes the C headers you need
source_stem: "bindings",
shared_libs: ["libbuzz"],
}
(b) cxx for C++: declare the C++ functions in a bridge module; cxx checks types on both sides. An illustrative example:
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("legacy/checksum.h");
fn legacy_checksum(data: &[u8]) -> u32; // receives rust::Slice on the C++ side
}
}
pub fn checksum(data: &[u8]) -> u32 {
ffi::legacy_checksum(data) // callers only see a safe API
}
(c) AIDL for services in other processes: aidl_interface generates a Rust crate, and the service just implements a trait. An illustrative example following the API in the AOSP docs:
use my_iface::aidl::my::package::IFoo::{BnFoo, IFoo};
use binder::{BinderFeatures, Interface};
struct MyFoo;
impl Interface for MyFoo {}
impl IFoo for MyFoo {
fn doFoo(&self, input: &str) -> binder::Result<String> {
Ok(input.to_uppercase())
}
}
fn main() {
binder::ProcessState::start_thread_pool();
let service = BnFoo::new_binder(MyFoo, BinderFeatures::default());
binder::add_service("myservice", service.as_binder()).expect("register");
binder::ProcessState::join_thread_pool();
}
About a year later, with more than 100,000 lines of Rust, the Android team confirmed that reality matched the survey: bindgen handled most interop, cxx was used in Bluetooth, AIDL in Keystore2, plus a few hand-written C wrappers (for example in profcollectd).
Step 3: Pick the right components to write in Rust
The priority is new code, or code that needs rewriting anyway, in high-risk places: handling untrusted data, running with high privilege, talking to the network. Android uses the "Rule of 2" to decide when extra isolation is needed, and Rust reduces the need for sandboxing.
| Component | Why it was chosen | Reported outcome |
|---|---|---|
| Keystore2 | Cryptographic key management, highly sensitive | Uses the AIDL Rust backend to serve other processes |
| New UWB stack | Radio protocol, receives external data | Only 2 unsafe blocks; runs in an existing process, saving several MB of RAM and some IPC latency |
| DNS-over-HTTP/3 | Handles network data | Uses async/await, needs fewer threads for the same work |
| Nearby Presence, MLS (RCS) | New protocols for device discovery and secure messaging | Written in Rust from the start; Nearby Presence runs in Google Play Services, MLS is coming to Google Messages |
Step 4: Go down to firmware, the TEE and the kernel
- pVM firmware (pvmfw): the old version was based on U-Boot, which wasn't designed for a hostile environment. It had many out-of-bounds accesses, integer underflows and memory corruption bugs in its VirtIO drivers. Google rewrote pvmfw in bare-metal Rust (shipped in Android 14), using the
aarch64-paging(page tables),smccc(PSCI/SMCCC calls) andvirtio-driverscrates. - Size: the Rust version is about 460 kB, versus 220 kB for the old C version. Google notes the comparison isn't fair: the new version adds features that allowed other components to be removed, so the total boot chain is a similar size.
- Trusty TEE: supports Trusted Apps written in Rust; the reference KeyMint TA is written in Rust.
- Kernel: Android's 6.12 kernel is the first with Rust support enabled and a production Rust driver. A kernel-mode GPU driver is being developed with Arm and Collabora.
- Low-level challenges: MMIO, shared memory and interrupt handlers break safe Rust's assumptions, so more
unsafeis required. Some niche cores (such as the Hexagon DSP) have weaker architecture support.
Step 5: Control unsafe and keep the old defences
- About 4% of Android's Rust code is inside
unsafeblocks. Each block is encapsulated behind a safe API and reviewed more carefully. In the UWB stack, scrutinising its 2 unsafe blocks helped uncover a potential race condition. - The remaining C/C++ is still hardened with the Scudo allocator, HWASAN, GWP-ASAN and KFENCE. Scudo's guard pages made the first near-miss Rust buffer overflow (CVE-2025-48530, in the CrabbyAVIF library) non-exploitable. It was fixed before release.
Step 6: Train engineers
The Android team wrote its own Comprehensive Rust course (public and free, taught since late 2022) for engineers with 2–3 years of C, C++ or Java experience:
- A 3-day fundamentals course, 5 hours a day.
- Deep dives: a half day on "Rust in Android" (interop with C, C++, Java), a full day on bare metal, a full day on concurrency.
- By September 2023, more than 500 Google engineers had taken it. 96% said it was worth their time. Three months after the course, participants confirmed they could write and review Rust code.
- In 2025 the course is adding an unsafe deep dive: soundness, undefined behaviour, safety comments and building safe abstractions.
Step 7: Measure continuously
Google tracks vulnerabilities through the monthly Android security bulletins and compares engineering metrics between Rust and C++ changes: rollback rate, review time, number of revisions. These numbers are what turned Rust from a "security cost" into a productivity advantage.
Results: the real numbers
| Metric | Result |
|---|---|
| Memory bugs as a share of all vulnerabilities | 76% (2019) → 35% (2022) → 24% (2024) → below 20% (2025) |
| Memory safety vulnerabilities per year | 223 (2019) → 85 (2022) |
| Memory safety vulnerability density | About 0.2 per million lines for Rust, versus close to 1,000 per million lines for C/C++; more than a 1000x reduction |
| Rollback rate (medium and large changes) | About 4x lower for Rust than for C++ |
| Code review time | About 25% less |
| Revisions before approval | About 20% fewer for changes of similar size |
| Performance | The UWB stack saved several MB of RAM; DNS-over-HTTP/3 needs fewer threads. In Chromium, the Rust QR code generator got 20x faster once its sandbox could be removed |
The most striking point: on Android, Rust is not only safer but also faster in productivity terms. Reviews go faster and there are fewer rollbacks. This is the strongest argument for convincing management.
Risks and lessons
- Rust is not fully immune: the near-miss CVE-2025-48530 sat in exactly the kind of code that's unsafe. It was caught thanks to a hardened allocator, and Google also found and fixed a gap in its crash reporting.
- The upfront cost is real: about 18 months of build integration, plus training and interop tooling. The rollout spans several years.
- Low levels are harder: firmware and drivers need more unsafe code, so review has to be strict.
- Logic bugs remain: Rust removes one class of bugs; it doesn't replace review, testing and threat modelling.
How to apply it
- Dev: Start with a new, small module with clear boundaries (a parser, a codec, a service taking external input). Call existing code through bindgen/cxx, wrap every
unsafebehind a safe API, and write a safety comment for each block. - Tech Lead: Follow Android's order. Integrate Rust into your existing build system first. Survey the types at your interop boundaries. Set a policy that "new code in high-risk components uses a memory-safe language". Keep the C/C++ hardening in place.
- PM: Measure before and after (memory bug rate, rollbacks, review time). Budget for training (about 3–4 days per engineer) and a multi-year roadmap. Pitch with Android's numbers: memory vulnerabilities down from 76% to below 20%, 4x fewer rollbacks, 25% faster reviews.
Sources & references
- Rust in Android: move fast and fix things — Jeff Vander Stoep, Google (11/2025)
- Eliminating Memory Safety Vulnerabilities at the Source — Google Online Security Blog (09/2024)
- Memory Safe Languages in Android 13 — Google Online Security Blog (12/2022)
- Rust/C++ interop in the Android Platform — Google Online Security Blog (06/2021)
- Integrating Rust Into the Android Open Source Project — Google Online Security Blog (05/2021)
- Rust in the Android platform — Google Online Security Blog (04/2021)
- Comprehensive Rust — Google
- Bare-metal Rust in Android — Google Online Security Blog (10/2023)
- Scaling Rust Adoption Through Training — Google Online Security Blog (09/2023)
- Build Rust modules: Hello Rust example — Android Open Source Project
- Bindgen binding modules — Android Open Source Project
- AIDL backends — Android Open Source Project