Skip to content

UEFI: locate_handles uses stale size for set_len, potentially exposing uninitialized handles #164945

Closed

Description

@cueballcipher

locate_handles sets the result vector's length from the size returned by the
first (probe) call rather than the size updated by the second call. If the
firmware handle database shrinks between the two calls, the Vec is left
containing uninitialized Handle values — reading them is UB — on routine
path/env/process code paths for UEFI targets.

I tried this code:

// locate_handles is called internally by device_path_to_text, open_shell,
// OwnedDevicePath::from_text, and ServiceProtocol::open on UEFI.
use std::process::{Command, Stdio};
let _ = Command::new("init").stdout(Stdio::null()).output();

I expected to see this happen: handle enumeration reflects the count the second
call actually returned, so only initialized handles are used.

Instead, this happened: the length passed to set_len is derived from the
first probe, ignoring the buf_len the second call updates
(library/std/src/sys/pal/uefi/helpers.rs:64-88):

let mut buf_len = 0usize;
match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { ... } // probe
let num_of_handles = buf_len / size_of::<r_efi::efi::Handle>();
let mut buf: Vec<r_efi::efi::Handle> = Vec::with_capacity(num_of_handles);
match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) {
    Ok(()) => {
        unsafe { buf.set_len(num_of_handles) }; // stale count; updated buf_len ignored
        Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect())
    }
    ...
}

Per the UEFI spec, LocateHandle updates BufferSize on output to the size of
data actually returned. If a device is uninstalled or hot-unplugged between the
probe and the second call, the second call succeeds with fewer handles and a
smaller buf_len, but set_len(num_of_handles) uses the larger first count. The
tail of the vector is uninitialized; any garbage non-null value passes the
NonNull::new filter into open_protocol. The fix is to set the length from the
second call's value:

unsafe { buf.set_len(buf_len / size_of::<r_efi::efi::Handle>()) };

Meta

Found by static analysis of main @ dc44e7f. Conformant firmware rejects
garbage handles with EFI_INVALID_PARAMETER, limiting practical impact, but the
uninitialized read is UB regardless. Related: #100499.

rustc --version --verbose:

rustc 1.101.0-dev
binary: rustc
commit-hash: dc44e7f2c436b6041742e138b9c2176f093196d5
commit-date: 2026-10-24
host: x86_64-unknown-linux-gnu
release: 1.101.0-dev
Backtrace

(static analysis; no runtime backtrace — requires the handle database to change between probe and retrieval)

@rustbot label +I-unsound +T-libs +O-uefi +C-bug

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Oct 26, 2026
  2. yashaggarwal85d commented on Oct 26, 2026

    @yashaggarwal85d
  3. yashaggarwal85d commented on Oct 26, 2026

    @yashaggarwal85d
  4. added 2 commits that reference this issue on Oct 26, 2026
    5680261
    493f336
  5. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 26, 2026
  6. added a commit that references this issue on Oct 26, 2026
    50a8fee
  7. removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessO-UEFIUEFIT-libsRelevant to the library team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions