rust-osdev/uefi-rs

Book tutorial results in panic: unreachable_unchecked must never be reached

Closed this issue · 9 comments

I tried this code:

I followed the instructions of the Rust UEFI book precisely in the Tutorial chapter (which means I am using the current stable crates.io version):

#![no_main]
#![no_std]

use log::info;
use uefi::prelude::*;

#[entry]
fn main() -> Status {
    uefi::helpers::init().unwrap();
    info!("Hello world!");
    boot::stall(10_000_000);
    Status::SUCCESS
}

With the sole exception that the OVMF version I'm using renamed its files for x64 to OVMF_CODE.4m.fd and OVMF_VARS.4m.fd respectively. But everything seemed to work on the QEMU side.

I expected this to happen: the example application to run as shown in the book.

Instead, this happened: I got the following output (manually retyped so whitespace may not be accuate) and after several seconds of sleep, the VM turned itself off as expected.

[ INFO]:            [PANIC]: panicked at library/core/src/panicking.rs:218:5
unsafe precondition(s) violated: hint:unreachable_unchecked must never be reached

This indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety.

Software and Hardware

QEMU emulator version 10.0.0
Copyright (c) 2003-2025 Fabrice Bellard and the QEMU Project developers

Arch Linux:
edk2-ovmf 202411-1
linux 6.12.4-arch1-1
extra/qemu-system-x86 10.0.0-5

CPU:
vendor_id : AuthenticAMD
cpu family : 25
model : 33
model name : AMD Ryzen 9 5950X 16-Core Processor

Not sure what's causing that, I'm not able to repro your issue. What channel and version of Rust are you using?

Could you try cloning the uefi-rs repo and running cargo xtask run --example hello_world? That will download the same build of OVMF that we use in CI, so it might help narrow down where the problem is.

Neat packaging of the QEMU command. Thanks!

Unfortunately with main it does indeed have the same result (ignore the VNC server, that's my config and both then and now it displays the same thing on the VGA console):

[INFO] downloading https://github.com/rust-osdev/ovmf-prebuilt/releases/download/edk2-stable202502-r2/edk2-stable202502-r2-bin.tar.xz
[INFO] received 6431288 bytes
[INFO] decompressing tarball
[INFO] unpacking to target/ovmf/x64/shell.efi
[INFO] unpacking to target/ovmf/x64/vars.fd
[INFO] unpacking to target/ovmf/x64/code.fd
[INFO] unpacking to target/ovmf/ia32/shell.efi
[INFO] unpacking to target/ovmf/ia32/vars.fd
[INFO] unpacking to target/ovmf/ia32/code.fd
[INFO] unpacking to target/ovmf/riscv64/vars.fd
[INFO] unpacking to target/ovmf/riscv64/code.fd
[INFO] unpacking to target/ovmf/riscv64/shell.efi
[INFO] unpacking to target/ovmf/aarch64/vars.fd
[INFO] unpacking to target/ovmf/aarch64/code.fd
[INFO] unpacking to target/ovmf/aarch64/shell.efi
qemu-system-x86_64 -nodefaults -device virtio-rng-pci -boot menu=on,splash-time=0 -fw_cfg name=opt/org.tianocore/X-Cpuhp-Bugcheck-Override,string=yes -machine q35 -smp 4 -m 256M -vga std --enable-kvm -device isa-debug-exit,iobase=0xf4,iosize=0x04 -debugcon file:./integration-test-debugcon.log -chardev file,id=fw,path=./ovmf-firmware-debugcon.log -device isa-debugcon,chardev=fw,iobase=0x402 -drive if=pflash,format=raw,readonly=on,file=target/ovmf/x64/code.fd -drive if=pflash,format=raw,readonly=off,file=/tmp/.tmpUG04nV/ovmf_vars -device virtio-scsi-pci -drive format=raw,file=fat:rw:target/x86_64-unknown-uefi/debug/esp -device qemu-xhci -device usb-net -drive format=raw,file=/tmp/.tmpUG04nV/test_disk.fat.img -drive if=none,id=scsidisk0,format=raw,file=/tmp/.tmpUG04nV/test_disk2.empty.img -device scsi-hd,drive=scsidisk0,vendor=uefi-rs,product=ExtScsiPassThru -drive if=none,id=nvmedisk0,format=raw,file=/tmp/.tmpUG04nV/test_disk3.empty.img -device nvme,drive=nvmedisk0,serial=uefi-rsNvmePassThru -drive if=none,format=raw,id=satadisk0,file=/tmp/.tmpUG04nV/test_disk4.empty.img -device ide-hd,drive=satadisk0,bus=ide.2,serial=AtaPassThru,model=AtaPassThru -serial pipe:/tmp/.tmpUG04nV/serial -qmp pipe:/tmp/.tmpUG04nV/qemu-monitor -fw_cfg name=etc/edk2/https/cacerts,file=uefi-test-runner/https/cacerts.bin
qemu-system-x86_64: -fw_cfg name=etc/edk2/https/cacerts,file=uefi-test-runner/https/cacerts.bin: warning: externally provided fw_cfg item names should be prefixed with "opt/"
qemu-system-x86_64: warning: nic usb-net.0 has no peer
VNC server running on ::1:5900
BdsDxe: failed to load Boot0001 "UEFI uefi-rs ExtScsiPassThru " from PciRoot(0x0)/Pci(0x3,0x0)/Scsi(0x0,0x0): Not Found
BdsDxe: failed to load Boot0002 "UEFI QEMU NVMe Ctrl uefi-rsNvmePassThru 1" from PciRoot(0x0)/Pci(0x5,0x0)/NVMe(0x1,00-00-00-00-00-00-00-00): Not Found
BdsDxe: loading Boot0003 "UEFI QEMU HARDDISK QM00001 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)
BdsDxe: starting Boot0003 "UEFI QEMU HARDDISK QM00001 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)
[ INFO]:             [PANIC]: panicked at library/core/src/panicking.rs:226:5:
unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached

This indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety.
Error: tests did not complete successfully

The two failed to load lines were not in the original output, but could have been clear screened away for all I know.

The most likely cause of the difference in behavior is the rust toolchain itself. Does your rust come from the regular rustup method or is it provided by the distro? Also, what channel and version of Rust are you on?

The other possible difference is the version of qemu; my distro is on 9.2.3. Not that the version of qemu should make a difference, but I can't think of any other likely possibilities.

As one more experiment, I tried building with RUSTFLAGS="-Cdebug-assertions=on -Zextra-const-ub-checks -Zstrict-init-checks" cargo +nightly build -Zbuild-std --target x86_64-unknown-uefi (using rustc 1.90.0-nightly (b03b3a7ec 2025-06-26)). Still didn't hit any panics.

I have been using stable but I just tried nightly-2025-06-26. Same result.

I tried without KVM enabled on a Westmere soft CPU. Same result.

Looking back at Arch Linux package history, it seems the last pre-10.0 version was 7.0 back in 2022. So I used Alpine in a docker container to launch QEMU 9.1.2 with the same binary and a Westmere soft CPU in that. Same result.

But I did notice when I ran on nightly, the traceback did change thanks to one of the nightly compiler features:

[ INFO]:             [PANIC]: panicked at [...]/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ucs2-0.3.3/src/lib.rs:90:18:
unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached

Which is in function ucs2_from_utf8_at_offset:

    if bytes[offset] & 0b1000_0000 == 0b0000_0000 {
        ch = bytes[offset] as u16;
        ch_len = 1;
    } else if bytes[offset] & 0b1110_0000 == 0b1100_0000 {
        // ...
    } else if bytes[offset] & 0b1111_0000 == 0b1110_0000 {
        // ...
    } else if bytes[offset] & 0b1111_0000 == 0b1111_0000 {
        return Err(Error::MultiByte); // UTF-16
    } else {
        // safe: impossible utf-8 string.
        unsafe { core::hint::unreachable_unchecked() }
    }

So my guess is it's reading garbage data from improperly laid out memory somehow.

How would one even debug this...

After realizing this had to be stack corruption, I had a hunch. Sure enough, this makes the panic go away:

export RUSTFLAGS="-Cno-redzone=yes" 

Makes sense to me.

(I also had to do cargo clean because apparently the xtask approach makes it cache previous builds without realizing things like this change, but that's a side note.)

Now the question is why am I the only one to ever hit this, and is there a way to ship this in the toolchain file or something...

Can you reproduce this after cargo clean?

The red zone should already be disabled in the UEFI targets:

$ rustc +nightly --target x86_64-unknown-uefi -Z unstable-options --print target-spec-json | grep redzone
  "disable-redzone": true,

Can you try running that and see if you get the same output?

I do indeed. Originally however I was using stable -- since that is my default and the documentation said it should work

Specifically: rustc 1.88.0 (6b00bc388 2025-06-23)

While it's not possible to print a JSON file, I decided to go digging through the source code for the target definition and... it's correct

https://github.com/rust-lang/rust/blob/stable/compiler/rustc_target/src/spec/base/uefi_msvc.rs#L40

So I tried a docker container from scratch. It did indeed work on stable.

I am extremely confused, but I must have local configuration that overrides this somehow?

Hopefully this bug will be helpful to anyone else who trips over this.