Skip to content

std: sys: random: uefi: Provide rdrand based fallback #141324

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Ayush1325
Copy link
Contributor

Some UEFI systems based on American Megatrends Inc. v3.3 do not provide RNG support 1. So fallback to rdrand in such cases.

Fixes #138252

cc @seijikun

@rustbot
Copy link
Collaborator

rustbot commented May 21, 2025

r? @workingjubilee

rustbot has assigned @workingjubilee.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels May 21, 2025
@joboet
Copy link
Member

joboet commented May 23, 2025

Unfortunately, the rdrand and rdseed instructions are not always safe or secure to use and thus require some checks (see the getrandom crate's backend for some of the issues). So std should either perform those checks or alternatively only use rdrand for generating hashmap keys (which would resolve the reported issue).

@Ayush1325
Copy link
Contributor Author

Unfortunately, the rdrand and rdseed instructions are not always safe or secure to use and thus require some checks (see the getrandom crate's backend for some of the issues). So std should either perform those checks or alternatively only use rdrand for generating hashmap keys (which would resolve the reported issue).

@seijikun Can you check if your hardware passes the checks in getrandom rand backend?

@seijikun
Copy link

Can you check if your hardware passes the checks in getrandom rand backend?

@Ayush1325 Sure! I don't have access to it at the moment, unfortunately - but I will do it monday morning.
You probably mean is_rdrand_good() and self_test(), right?

@Ayush1325
Copy link
Contributor Author

Can you check if your hardware passes the checks in getrandom rand backend?

@Ayush1325 Sure! I don't have access to it at the moment, unfortunately - but I will do it monday morning. You probably mean is_rdrand_good() and self_test(), right?

is_rdrand_good calls self_test, so yup.

@seijikun
Copy link

@Ayush1325 Looks good:
image

@Ayush1325
Copy link
Contributor Author

@Ayush1325 Looks good: image

Great, I will update the PR soon

@Ayush1325 Ayush1325 force-pushed the uefi-rand-fallback branch from d4710c0 to 1c3abfb Compare May 26, 2025 13:43
@Ayush1325
Copy link
Contributor Author

@seijikun Can you take a look now?

@seijikun
Copy link

seijikun commented May 27, 2025

Sorry, took me a while to find out how this custom-std stuff works. This is how I did it:

git clone --filter="blob:none" https://github.com/Ayush1325/rust.git
cd rust
git checkout uefi-rand-fallback

./x setup dist
echo "[target.x86_64-unknown-linux-gnu]" >> ./bootstrap.toml
echo "no-std = false" >> ./bootstrap.toml
echo "[target.x86_64-unknown-uefi]" >> ./bootstrap.toml
echo "no-std = false" >> ./bootstrap.toml
echo "[build]" >> ./bootstrap.toml
echo "target=[\"x86_64-unknown-linux-gnu\", \"x86_64-unknown-uefi\"]" >> ./bootstrap.toml

./x build --stage 0
rustup toolchain link stage0 build/host/stage0-sysroot

And then, I built the project (consisting of ~3 code lines that just use a HashMap) using:

cargo +stage0 build --target x86_64-unknown-uefi --release

@seijikun
Copy link

@Ayush1325 There is currently still a bug in the implementation.
UEFI's locate_handles function has one very weird quirk. When the resulting list is empty (as in my case), it doesn't return an empty list but instead fails with EFI_NOT_FOUND. That's an error I have stumbled over more than I'd like to admit...

So the normal case has to be edited to look like this:

--- a/library/std/src/sys/random/uefi.rs
+++ b/library/std/src/sys/random/uefi.rs
@@ -3,22 +3,22 @@
 use crate::sys::pal::helpers;
 
 pub fn fill_bytes(bytes: &mut [u8]) {
-    let handles =
-        helpers::locate_handles(rng::PROTOCOL_GUID).expect("failed to generate random data");
-    for handle in handles {
-        if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
-            let r = unsafe {
-                ((*protocol.as_ptr()).get_rng)(
-                    protocol.as_ptr(),
-                    crate::ptr::null_mut(),
-                    bytes.len(),
-                    bytes.as_mut_ptr(),
-                )
-            };
-            if r.is_error() {
-                continue;
-            } else {
-                return;
+    if let Ok(handles) = helpers::locate_handles(rng::PROTOCOL_GUID) {
+        for handle in handles {
+            if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
+                let r = unsafe {
+                    ((*protocol.as_ptr()).get_rng)(
+                        protocol.as_ptr(),
+                        crate::ptr::null_mut(),
+                        bytes.len(),
+                        bytes.as_mut_ptr(),
+                    )
+                };
+                if r.is_error() {
+                    continue;
+                } else {
+                    return;
+                }
             }
         }
     }

@seijikun
Copy link

seijikun commented May 27, 2025

I made the mentioned change on top of your branch locally. And as soon as that is changed...:

use std::{collections::HashMap, time::Duration};

fn main() {
    let mut map = HashMap::new();
    map.insert("Ayush1325", "is awesome");
    println!("{:?}", map);
    std::thread::sleep(Duration::from_millis(31337));
}

Screenshot_20250527_140443
... it works! Thank you!

@Ayush1325 Ayush1325 force-pushed the uefi-rand-fallback branch from 1c3abfb to 5c59ba8 Compare May 27, 2025 15:56
@Ayush1325
Copy link
Contributor Author

@Ayush1325 There is currently still a bug in the implementation. UEFI's locate_handles function has one very weird quirk. When the resulting list is empty (as in my case), it doesn't return an empty list but instead fails with EFI_NOT_FOUND. That's an error I have stumbled over more than I'd like to admit...

Thanks for reporting this. I have updated the PR (and also done some refactoring).

@seijikun
Copy link

Just tested against the newest commit: 5c59ba8
Works ✔️

@Ayush1325
Copy link
Contributor Author

@joboet Does it look fine now?

@workingjubilee
Copy link
Member

r? @joboet

@rustbot rustbot assigned joboet and unassigned workingjubilee Jun 4, 2025
Copy link
Member

@joboet joboet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fine to me, only the zero-request case is handled incorrectly.

@@ -1,27 +1,156 @@
use r_efi::protocols::rng;
pub fn fill_bytes(bytes: &mut [u8]) {
// Try EFI_RNG_PROTOCOL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A zero-byte request should always succeed, but will currently panic if rdrand is not available as the UEFI protocol will return EFI_INVALID_PARAMETER.

Suggested change
// Try EFI_RNG_PROTOCOL
if bytes.is_empty() {
return;
}
// Try EFI_RNG_PROTOCOL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

unsafe fn rdrand_exact(dest: &mut [u8]) -> Option<()> {
// We use chunks_exact_mut instead of chunks_mut as it allows almost all
// calls to memcpy to be elided by the compiler.
let mut chunks = dest.chunks_exact_mut(size_of::<Word>());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also use array_chunks_mut to eliminate the copy_from_slice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 5, 2025
@Ayush1325 Ayush1325 force-pushed the uefi-rand-fallback branch from 5c59ba8 to a6b2195 Compare June 6, 2025 04:43
@rustbot

This comment has been minimized.

Some UEFI systems based on American Megatrends Inc. v3.3 do not provide
RNG support [1]. So fallback to rdrand in such cases.

[1]: rust-lang#138252 (comment)

Signed-off-by: Ayush Singh <[email protected]>
@Ayush1325 Ayush1325 force-pushed the uefi-rand-fallback branch from a6b2195 to 669564d Compare June 6, 2025 04:47
@Ayush1325 Ayush1325 requested a review from joboet June 8, 2025 13:36
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

HashMap::new() panics inside UEFI environments that don't support RNG protocol
5 participants