6.7 C
Canberra
Saturday, April 11, 2026

Bringing Rust to the Pixel Baseband


Google is repeatedly advancing the safety of Pixel gadgets. We’ve got been specializing in hardening the mobile baseband modem in opposition to exploitation. Recognizing the dangers related inside the complicated modem firmware, Pixel 9 shipped with mitigations in opposition to a spread of memory-safety vulnerabilities. For Pixel 10, Google is advancing its proactive safety measures additional. Following our earlier dialogue on “Deploying Rust in Present Firmware Codebases”, this submit shares a concrete utility: integrating a memory-safe Rust DNS(Area Title System) parser into the modem firmware. The brand new Rust-based DNS parser considerably reduces our safety danger by mitigating a whole class of vulnerabilities in a dangerous space, whereas additionally laying the inspiration for broader adoption of memory-safe code in different areas.

Right here we share our expertise of engaged on it, and hope it will possibly encourage using extra reminiscence protected languages in low-level environments.

Why Modem Reminiscence Security Can’t Wait

Lately, we’ve got seen rising curiosity within the mobile modem from attackers and safety researchers. For instance, Google’s Venture Zero gained distant code execution on Pixel modems over the Web. Pixel modem has tens of Megabytes of executable code. Given the complexity and distant assault floor of the modem, different vital reminiscence security vulnerabilities might stay within the predominantly memory-unsafe firmware code.

Why DNS?

The DNS protocol is mostly recognized within the context of browsers discovering web sites. With the evolution of mobile know-how, trendy mobile communications have migrated to digital information networks; consequently, even primary operations similar to name forwarding depend on DNS providers.

DNS is a fancy protocol and requires parsing of untrusted information, which might result in vulnerabilities, notably when applied in a memory-unsafe language (instance: CVE-2024-27227). Implementing the DNS parser in Rust affords worth by reducing the assault surfaces related to reminiscence unsafety.

Selecting a DNS library

DNS already has a degree of assist within the open-source Rust group. We evaluated a number of open supply crates that implement DNS. Primarily based on standards shared in earlier posts, we recognized hickory-proto as the very best candidate. It has wonderful upkeep, over 75% check protection, and widespread adoption within the Rust group. Its pervasiveness reveals its potential because the de-facto DNS alternative and long run assist. Though hickory-proto initially lacked no_std assist, which is required for Naked-metal environments (see our earlier submit on this matter), we had been ready so as to add assist to it and its dependencies.

Including no_std assist

The work to allow no_std for hickory-proto is usually mechanical. We shared the method in a earlier submit. We undertook modifications to hickory_proto and its dependencies to allow no_std assist. The upstream no_std work additionally leads to a no_std URL parser, useful to different initiatives.

The above PRs are nice examples of find out how to lengthen no_std assist to present std-only crates.

Code dimension examine

Code dimension is the one of many elements that we evaluated when selecting the DNS library to make use of.

Code dimension
by class
Rust applied Shim that calls Hickory-proto on receiving a DNS response 4KB
core, alloc, compiler_builtins
(reusable, one-time value)
17KB
Hickory-proto library and dependencies 350KB


We constructed prototypes and measured dimension with size-optimized settings. Expectedly, hickory_proto shouldn’t be designed with embedded use in thoughts, and isn’t optimized for dimension. Because the Pixel modem shouldn’t be tightly reminiscence constrained, we prioritized group assist and code high quality, leaving code dimension optimizations as future work.

Nonetheless, the extra code dimension could also be a blocker for different embedded methods. This might be addressed sooner or later by including further characteristic flags to conditionally compile solely required performance. Implementing this modularity can be a precious future work.

Hook-up Rust to modem firmware

Earlier than constructing the Rust DNS library, we outlined a number of Rust unit assessments to cowl primary arithmetic, dynamic allocations, and FFI to confirm the mixing of Rust with the prevailing modem firmware code base.

Compile Rust code to staticlib

Whereas utilizing cargo is the default alternative for compilation within the Rust ecosystem, it presents challenges when integrating it into present construct methods. We evaluated two choices:

  1. Utilizing cargo to construct a staticlib earlier than the modem builds. Then add the produced staticlib into the linking step.
  2. Immediately work with rustc and combine the Rust compilation steps into the prevailing modem construct system.

Possibility #1 doesn’t scale if we’re going to add extra Rust elements sooner or later, as linking a number of staticlibs might trigger duplicated image errors. We selected choice #2 because it scales extra simply and permits tighter integration into our present construct system. Our present C/C++ codebase makes use of Pigweed to drive the first construct system. Pigweed helps Rust targets (instance) with direct calls to rustc by way of rust instruments outlined in GN.

We compiled all of the Rust crates, together with hickory-proto, its dependencies, and core, compiler_builtin, alloc, to rlib. Then, we created a staticlib goal with a single lib.rs file which references all of the rlib crates utilizing extern crate key phrases.

Construct core, alloc, and compiler_builtins

Android’s Rust Toolchain distributes supply code of core, alloc, and compiler_builtins, and we leveraged this for the modem. They are often included to the construct graph by including a GN goal with crate_root pointing to the basis lib.rs of every crate.

Pixel modem firmware already has a well-tested and specialised world reminiscence allocation system to assist some dynamic reminiscence allocations. alloc assist was added by implementing the GlobalAlloc with FFI calls to the allocators C APIs:

use core::alloc::{GlobalAlloc, Format};

extern "C" {
    fn mem_malloc(dimension: usize, alignment: usize) -> *mut u8;
    fn mem_free(ptr: *mut u8, alignment: usize);
}

struct MemAllocator;

unsafe impl GlobalAlloc for MemAllocator {
    unsafe fn alloc(&self, format: Format) -> *mut u8 {
        mem_malloc(format.dimension(), format.align())
    }

    unsafe fn dealloc(&self, ptr: *mut u8, format: Format) {
        mem_free(ptr, format.align());
    }
}

#[global_allocator]
static ALLOCATOR: MemAllocator = MemAllocator;

Pixel modem firmware already implements a backend for the Pigweed crash facade as the worldwide crash handler. Exposing it into Rust panic_handler by way of FFI unifies the crash dealing with for each Rust and C/C++ code.

#![no_std]
use core::panic::PanicInfo;

extern "C" {
    pub fn PwCrashBackend(sigature: *const i8, file_name: *const i8, line: u32);
}

#[panic_handler]
fn panic(panic_info: &PanicInfo) -> ! {
    let mut filename = "";
    let mut line_number: u32 = 0;

    if let Some(location) = panic_info.location() {
        filename = location.file();
        line_number = location.line();
    }

    let mut cstr_buffer = [0u8; 128];
    // By no means writes to the final byte to ensure `cstr_buffer` is at all times zero
    // terminated.
    let (_, author) = cstr_buffer.split_last_mut().unwrap();
    for (place, ch) in author.iter_mut().zip(filename.bytes()) {
        *place = ch;
    }

    unsafe {
        PwCrashBackend(
            "Rust panic".as_ptr() as *const i8,
            cstr_buffer.as_ptr() as *const i8,
            line_number,
        );
    }

    loop {}
}

Hyperlink Rust staticlib

The Pixel modem firmware linking has a step that calls the linker to hyperlink all of the objects generated from C/C++ code. Through the use of llvm-ar -x to extract object recordsdata from the Rust mixed staticlib and supplying them to the linker, the Rust code seems within the closing modem picture.

There was a efficiency subject we skilled attributable to weak symbols throughout linking. The inclusion of Rust core and compiler-builtin prompted surprising energy and efficiency regressions on varied assessments. Upon evaluation, we realized that modem optimized implementations of memset and memcpy offered by the modem firmware are by accident changed by these outlined in compiler_builtin. It appears to occur as a result of each compiler_builtin crate and the prevailing codebase defines symbols as weak, linker has no means to determine which one is weaker. We mounted the regression by stripping the compiler_builtin crate earlier than linking utilizing a one line shell script.

llvm-ar -t  | grep compiler_builtins | xargs llvm-ar -d 

Integrating hickory-proto

Expose Rust API and calling again to C++

For the DNS parser, we declared the DNS response parsing API in C after which applied the identical API in Rust.

int32_t process_dns_response(uint8_t*, int32_t);

The Rust operate returns an integer standing for the error code. The acquired DNS solutions within the DNS response are required to be up to date to in-memory information constructions which are coupled with the unique C implementation, subsequently, we use present C features to do it. The prevailing C features are dispatched from the Rust implementation.

pub unsafe extern "C" fn process_dns_response(
    dns_response: *const u8,
    response_len: i32,
) -> i32 {
    //... validate inputs `dns_response` and `response_len`.


    // SAFETY:
    // It's protected as a result of `dns_response` is null checked above. `response_len`
    // is handed in, protected so long as it's set accurately by vendor code.
    match process_response(unsafe {
        slice::from_raw_parts(dns_response, response_len)
    }) {
         Okay(()) => 0,
         Err(err) => err.into(),
    }
}

fn process_response(response: &[u8]) -> End result<()> {
    let response = hickory_proto::op::Message::from_bytes(response)?;
    let response = hickory_proto::xfer::DnsResponse::from_message(response)?;

   
    for reply in response.solutions() {  
        match reply.record_type() {
            hickory_proto::RecordType:... => {
                // SAFETY:
                // It's protected as a result of the callback operate doesn't retailer
                // reference of the inputs or their members.
                unsafe {
                    callback_to_c_function(...)?;
                }
            }
            
            // ... extra match arms omitted.
        }    
    }

    Okay(())
}

In our case, the DNS responding parsing operate API is easy sufficient for us handy write, whereas the callbacks again to C features for dealing with the response have complicated information kind conversions. Subsequently, we leveraged bindgen to generate FFI code for the callbacks.

Construct third-party crates

Even with all options disabled, hickory-proto introduces greater than 30 dependent crates. Manually written construct guidelines are troublesome to make sure correctness and scale poorly when upgrading dependencies into new variations.

Fuchsia has developed cargo-gnaw to assist constructing their third celebration Rust crates. Cargo-gnaw works by invoking cargo metadata to resolve dependencies, then parse and generate GN construct guidelines. This ensures correctness and ease of upkeep.

Conclusion

The Pixel 10 collection of telephones marks a pivotal second, being the primary Pixel machine to combine a memory-safe language into its modem.

Whereas changing one piece of dangerous assault floor is itself precious, this venture lays the inspiration for future integration of memory-safe parsers and code into the mobile baseband, guaranteeing the baseband’s safety posture will proceed to enhance as growth continues.

Particular because of Armando Montanez, Bjorn Mellem, Boky Chen, Cheng-Yu Tsai, Dominik Maier, Erik Gilling, Ever Rosales, Hungyen Weng, Ivan Lozano, James Farrell, Jeffrey Vander Stoep, Jiacheng Lu, Jingjing Bu, Min Xu, Murphy Stein, Ray Weng, Shawn Yang, Sherk Chung, Stephan Chen, Stephen Hines.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

[td_block_social_counter facebook="tagdiv" twitter="tagdivofficial" youtube="tagdiv" style="style8 td-social-boxed td-social-font-icons" tdc_css="eyJhbGwiOnsibWFyZ2luLWJvdHRvbSI6IjM4IiwiZGlzcGxheSI6IiJ9LCJwb3J0cmFpdCI6eyJtYXJnaW4tYm90dG9tIjoiMzAiLCJkaXNwbGF5IjoiIn0sInBvcnRyYWl0X21heF93aWR0aCI6MTAxOCwicG9ydHJhaXRfbWluX3dpZHRoIjo3Njh9" custom_title="Stay Connected" block_template_id="td_block_template_8" f_header_font_family="712" f_header_font_transform="uppercase" f_header_font_weight="500" f_header_font_size="17" border_color="#dd3333"]
- Advertisement -spot_img

Latest Articles