Dereferenceable Zero

I accept the point - I've been pointing out targets, not the actual incident or blocking problem.

However, here is a target with a problem.

Robin Mueller, a researcher at the University of Stuttgart's Institute of Space Systems, was developing a Rust bootloader for the Vorago VA108xx and VA416xx - radiation-hardened Cortex-M4 MCUs deployed in aerospace - where programme RAM starts at 0x0. He needed to read the running application image from address 0x0 to flash it to non-volatile memory. Skipping the first few bytes was not an option - the image starts there. Standard Rust pointer operations were impossible; the only path was inline ArmĀ® assembly.

    if FLASH_SELF {
        let mut first_four_bytes: [u8; 4] = [0; 4];
        read_four_bytes_at_addr_zero(&mut first_four_bytes);
        let bootloader_data = {
            unsafe {
                &*core::ptr::slice_from_raw_parts(
                    (BOOTLOADER_START_ADDR + 4) as *const u8,
                    (BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 8) as usize,
                )
            }
        };
        let mut digest = CRC_ALGO.digest();
        digest.update(&first_four_bytes);
        digest.update(bootloader_data);
        let bootloader_crc = digest.finalize();

        nvm.write_data(0x0, &first_four_bytes);
        nvm.write_data(0x4, bootloader_data);
        if let Err(e) = nvm.verify_data(0x0, &first_four_bytes) {
            if DEFMT_PRINTOUTS {
                defmt::error!("verification of self-flash to NVM failed: {:?}", e);
            }
        }
        if let Err(e) = nvm.verify_data(0x4, bootloader_data) {
            if DEFMT_PRINTOUTS {
                defmt::error!("verification of self-flash to NVM failed: {:?}", e);
            }
        }

        nvm.write_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes());
        if let Err(e) = nvm.verify_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes()) {
            if DEFMT_PRINTOUTS {
                defmt::error!(
                    "error: CRC verification for bootloader self-flash failed: {:?}",
                    e
                );
            }
        }
    }
// Reading from address 0x0 is problematic in Rust.
// See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/5.
// This solution falls back to assembler to deal with this.
fn read_four_bytes_at_addr_zero(buf: &mut [u8; 4]) {
    unsafe {
        core::arch::asm!(
            "ldr r0, [{0}]",    // Load 4 bytes from src into r0 register
            "str r0, [{1}]",    // Store r0 register into first_four_bytes
            in(reg) BOOTLOADER_START_ADDR as *const u8,         // Input: src pointer (0x0)
            in(reg) buf as *mut [u8; 4],  // Input: destination pointer
        );
    }
}
fn check_own_crc(
    sysconfig: &pac::Sysconfig,
    cp: &cortex_m::Peripherals,
    nvm: &mut NvmWrapper,
    timer: &mut CountdownTimer,
) {
    let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u16).read_unaligned().to_be() };
    // I'd prefer to use [core::slice::from_raw_parts], but that is problematic
    // because the address of the bootloader is 0x0, so the NULL check fails and the functions
    // panics.
    let mut first_four_bytes: [u8; 4] = [0; 4];
    read_four_bytes_at_addr_zero(&mut first_four_bytes);
    let mut digest = CRC_ALGO.digest();
    digest.update(&first_four_bytes);

    ...
}

I circumvented the issue by falling back to assembler, but this feels really hacky to me.. Shouldn't Rust be low level enough to allow me to deal with these issues? I found this pre-RFC: Pre-RFC: Conditionally-supported volatile access to address 0 - libs - Rust Internals.

Source: Reading from physical address 0x0

The project is published as va108xx and va416xx on crates.io and sources available at

2 Likes