From 9ebf1e5daba7a281e701899e47c09a4d9cc2b1b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 13 Nov 2023 18:30:39 +0100 Subject: [PATCH] share getentropy shim across various unixes --- src/shims/unix/foreign_items.rs | 30 +++++++++++++++++++++++++ src/shims/unix/freebsd/foreign_items.rs | 17 -------------- src/shims/unix/macos/foreign_items.rs | 16 ++----------- tests/pass-dep/getrandom.rs | 3 ++- tests/pass-dep/shims/libc-getentropy.rs | 15 ++++++++++--- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index c013d27502..13bbd15cf3 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -27,6 +27,8 @@ fn is_dyn_sym(name: &str, target_os: &str) -> bool { // `signal` is set up as a weak symbol in `init_extern_statics` (on Android) so we might as // well allow it in `dlsym`. "signal" => true, + // needed at least on macOS to avoid file-based fallback in getrandom + "getentropy" => true, // Give specific OSes a chance to allow their symbols. _ => match target_os { @@ -525,6 +527,34 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.getpid()?; this.write_scalar(Scalar::from_i32(result), dest)?; } + "getentropy" => { + // This function is non-standard but exists with the same signature and behavior on + // Linux, macOS, and freeBSD. + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd") { + throw_unsup_format!( + "`getentropy` is not supported on {}", + this.tcx.sess.target.os + ); + } + + let [buf, bufsize] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let buf = this.read_pointer(buf)?; + let bufsize = this.read_target_usize(bufsize)?; + + // getentropy sets errno to EIO when the buffer size exceeds 256 bytes. + // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&format=html + // Linux: https://man7.org/linux/man-pages/man3/getentropy.3.html + // macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html + if bufsize > 256 { + let err = this.eval_libc("EIO"); + this.set_last_error(err)?; + this.write_scalar(Scalar::from_i32(-1), dest)? + } else { + this.gen_random(buf, bufsize)?; + this.write_scalar(Scalar::from_i32(0), dest)?; + } + } // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. diff --git a/src/shims/unix/freebsd/foreign_items.rs b/src/shims/unix/freebsd/foreign_items.rs index f81710a41a..96e322c4cf 100644 --- a/src/shims/unix/freebsd/foreign_items.rs +++ b/src/shims/unix/freebsd/foreign_items.rs @@ -47,23 +47,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.read_scalar(len)?, )?; } - "getentropy" => { - let [buf, bufsize] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let buf = this.read_pointer(buf)?; - let bufsize = this.read_target_usize(bufsize)?; - - // getentropy sets errno to EIO when the buffer size exceeds 256 bytes. - // https://man.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&format=html - if bufsize > 256 { - let err = this.eval_libc("EIO"); - this.set_last_error(err)?; - this.write_scalar(Scalar::from_i32(-1), dest)? - } else { - this.gen_random(buf, bufsize)?; - this.write_scalar(Scalar::from_i32(0), dest)?; - } - } // errno "__error" => { diff --git a/src/shims/unix/macos/foreign_items.rs b/src/shims/unix/macos/foreign_items.rs index 5881a3f46f..e8f35e7ba5 100644 --- a/src/shims/unix/macos/foreign_items.rs +++ b/src/shims/unix/macos/foreign_items.rs @@ -6,8 +6,8 @@ use shims::foreign_items::EmulateForeignItemResult; use shims::unix::fs::EvalContextExt as _; use shims::unix::thread::EvalContextExt as _; -pub fn is_dyn_sym(name: &str) -> bool { - matches!(name, "getentropy") +pub fn is_dyn_sym(_name: &str) -> bool { + false } impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -113,18 +113,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(result, dest)?; } - // Random generation related shims - "getentropy" => { - let [buf, bufsize] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let buf = this.read_pointer(buf)?; - let bufsize = this.read_target_usize(bufsize)?; - - this.gen_random(buf, bufsize)?; - - this.write_scalar(Scalar::from_i32(0), dest)?; // KERN_SUCCESS - } - // Access to command-line arguments "_NSGetArgc" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/tests/pass-dep/getrandom.rs b/tests/pass-dep/getrandom.rs index 60d6dd31e7..c0d9296a9a 100644 --- a/tests/pass-dep/getrandom.rs +++ b/tests/pass-dep/getrandom.rs @@ -1,7 +1,8 @@ // mac-os `getrandom_01` does some pointer shenanigans //@compile-flags: -Zmiri-permissive-provenance -/// Test direct calls of getrandom 0.1 and 0.2 +/// Test direct calls of getrandom 0.1 and 0.2. +/// Make sure they work even with isolation enabled (i.e., we do not hit a file-based fallback path). fn main() { let mut data = vec![0; 16]; getrandom_01::getrandom(&mut data).unwrap(); diff --git a/tests/pass-dep/shims/libc-getentropy.rs b/tests/pass-dep/shims/libc-getentropy.rs index 468727f70e..4b863f6851 100644 --- a/tests/pass-dep/shims/libc-getentropy.rs +++ b/tests/pass-dep/shims/libc-getentropy.rs @@ -1,11 +1,20 @@ -//@only-target-freebsd +//@ignore-target-windows: no libc + +// on macOS this is not in the `libc` crate. +#[cfg(target_os = "macos")] +extern "C" { + fn getentropy(bytes: *mut libc::c_void, count: libc::size_t) -> libc::c_int; +} + +#[cfg(not(target_os = "macos"))] +use libc::getentropy; fn main() { let mut buf1 = [0u8; 256]; let mut buf2 = [0u8; 257]; unsafe { - assert_eq!(libc::getentropy(buf1.as_mut_ptr() as *mut libc::c_void, buf1.len()), 0); - assert_eq!(libc::getentropy(buf2.as_mut_ptr() as *mut libc::c_void, buf2.len()), -1); + assert_eq!(getentropy(buf1.as_mut_ptr() as *mut libc::c_void, buf1.len()), 0); + assert_eq!(getentropy(buf2.as_mut_ptr() as *mut libc::c_void, buf2.len()), -1); assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::EIO); } }