diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 41bf70c34634..488d82f2109a 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -272,6 +272,53 @@ fn unix_gettid(&mut self, link_name: &str) -> InterpResult<'tcx, Scalar> { interp_ok(Scalar::from_u32(this.get_current_tid())) } + /// `fields_size`, if present, says how large each field of the struct is. + fn uname( + &mut self, + uname: &OpTy<'tcx>, + fields_size: Option<&OpTy<'tcx>>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + this.assert_target_os_is_unix("uname"); + + let uname_ptr = this.read_pointer(uname)?; + let fields_size = match fields_size { + None => None, + Some(size) => Some(this.read_scalar(size)?.to_i32()?), + }; + + if this.ptr_is_null(uname_ptr)? { + return this.set_last_error_and_return_i32(LibcError("EFAULT")); + } + + let uname = this.deref_pointer_as(uname, this.libc_ty_layout("utsname"))?; + let arch = this.machine.tcx.sess.target.arch.desc_symbol(); + // Values required by POSIX. + let mut values = vec![ + ("sysname", "Miri"), + ("nodename", "Miri"), + ("release", env!("CARGO_PKG_VERSION")), + ("version", concat!("Miri ", env!("CARGO_PKG_VERSION"))), + ("machine", arch.as_str()), + ]; + if matches!(this.machine.tcx.sess.target.os, Os::Linux | Os::Android) { + values.push(("domainname", "(none)")); + } + + for (name, value) in values { + let field = this.project_field_named(&uname, name)?; + let size = field.layout().layout.size().bytes(); + if fields_size.is_some_and(|fields_size| u64::try_from(fields_size) != Ok(size)) { + throw_unsup_format!( + "the fields size passed to `uname` does not match the type in the libc crate" + ); + } + let (written, _) = this.write_c_str(value.as_bytes(), field.ptr(), size)?; + assert!(written); // All values should fit. + } + interp_ok(Scalar::from_i32(0)) + } + /// The Apple-specific `int pthread_threadid_np(pthread_t thread, uint64_t *thread_id)`, which /// allows querying the ID for arbitrary threads, identified by their pthread_t. /// diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 9a24cbc5048a..bf9482a6264b 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -174,6 +174,21 @@ fn emulate_foreign_item_inner( let result = this.getpid()?; this.write_scalar(result, dest)?; } + "uname" => { + // Not all Unixes have the `uname` symbol, e.g. FreeBSD does not. + this.check_target_os( + &[Os::Linux, Os::Android, Os::MacOs, Os::Solaris, Os::Illumos], + link_name, + )?; + let [uname] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> i32), + link_name, + abi, + args, + )?; + let result = this.uname(uname, None)?; + this.write_scalar(result, dest)?; + } "sysconf" => { let [val] = this.check_shim_sig( shim_sig!(extern "C" fn(i32) -> isize), diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index f64ee3b16220..de08f4c6afe4 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -165,6 +165,19 @@ fn emulate_foreign_item_inner( let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } + "__xuname" => { + // FreeBSD uses __xuname under the hood to implement uname, see: + // https://github.com/freebsd/freebsd-src/blob/3542d60fb8042474f66fbf2d779ed8c5a80d0f78/sys/sys/utsname.h#L64 + // https://github.com/freebsd/freebsd-src/blob/3542d60fb8042474f66fbf2d779ed8c5a80d0f78/lib/libc/gen/uname.c#L44 + let [size, uname] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _) -> i32), + link_name, + abi, + args, + )?; + let result = this.uname(uname, Some(size))?; + this.write_scalar(result, 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/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index b80fb0025530..141e0009101a 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -3,6 +3,7 @@ #[path = "../../utils/libc.rs"] mod libc_utils; + use std::time::{Duration, Instant}; use std::{env, mem, ptr}; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-uname.rs b/src/tools/miri/tests/pass-dep/libc/libc-uname.rs new file mode 100644 index 000000000000..b071e0522582 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-uname.rs @@ -0,0 +1,40 @@ +//@ignore-target: windows # No libc + +#[path = "../../utils/libc.rs"] +mod libc_utils; + +use std::ffi::CStr; +use std::{io, ptr}; + +use libc_utils::*; + +fn main() { + test_ok(); + test_null_ptr(); +} + +fn test_ok() { + // SAFETY: all zeros for `utsname` is valid. + let mut uname: libc::utsname = unsafe { std::mem::zeroed() }; + errno_check(unsafe { libc::uname(&mut uname) }); + + assert_eq!(unsafe { CStr::from_ptr(&uname.sysname as *const _) }, c"Miri"); + assert_eq!(unsafe { CStr::from_ptr(&uname.nodename as *const _) }, c"Miri"); + assert_eq!( + unsafe { CStr::from_ptr(&uname.release as *const _) }.to_str().unwrap(), + env!("CARGO_PKG_VERSION") + ); + assert_eq!(unsafe { CStr::from_ptr(&uname.version as *const _) }, c"Miri 0.1.0"); + assert_eq!( + unsafe { CStr::from_ptr(&uname.machine as *const _) }.to_str().unwrap(), + std::env::consts::ARCH + ); + #[cfg(any(target_os = "linux", target_os = "android"))] + assert_eq!(unsafe { CStr::from_ptr(&uname.domainname as *const _) }, c"(none)"); +} + +fn test_null_ptr() { + let err = errno_result(unsafe { libc::uname(ptr::null_mut()) }).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EFAULT)); + assert_eq!(io::Error::last_os_error().raw_os_error(), Some(libc::EFAULT)); +}