diff --git a/src/shims/unix/env.rs b/src/shims/unix/env.rs index 41bf70c346..3dd0bc9cc0 100644 --- a/src/shims/unix/env.rs +++ b/src/shims/unix/env.rs @@ -1,6 +1,6 @@ -use std::env; -use std::ffi::{OsStr, OsString}; -use std::io::ErrorKind; +use std::ffi::{CStr, OsStr, OsString}; +use std::io::{self, ErrorKind}; +use std::{env, slice}; use rustc_abi::{FieldIdx, Size}; use rustc_data_structures::fx::FxHashMap; @@ -272,6 +272,61 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_u32(this.get_current_tid())) } + fn uname(&mut self, uname: &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)?; + if this.ptr_is_null(uname_ptr)? { + return this.set_last_error_and_return_i32(LibcError("EFAULT")); + } + + let mut uname_buf = unsafe { std::mem::zeroed() }; + if this.machine.communicate() { + let result = unsafe { libc::uname(&mut uname_buf) }; + if result != 0 { + let err = this.io_error_to_errnum(io::Error::last_os_error())?; + this.set_last_error(err)?; + return interp_ok(Scalar::from_i32(result)); + } + } else { + fn write_slice(dst: &mut [libc::c_char], src: &[u8]) { + dst[..src.len()].copy_from_slice(unsafe { + slice::from_raw_parts(src.as_ptr().cast(), src.len()) + }); + } + write_slice(uname_buf.sysname.as_mut_slice(), b"Linux"); + write_slice(uname_buf.nodename.as_mut_slice(), b"Miri"); + write_slice(uname_buf.release.as_mut_slice(), b"6.18.1-arch1-2"); + write_slice( + uname_buf.version.as_mut_slice(), + b"#1 SMP PREEMPT_DYNAMIC Sat, 13 Dec 2025 18:23:21 +0000", + ); + write_slice(uname_buf.machine.as_mut_slice(), b"x86_64"); + #[cfg(any(target_os = "linux", target_os = "android"))] + write_slice(uname_buf.domainname.as_mut_slice(), b"(none)"); + } + + let uname = this.deref_pointer_as(uname, this.libc_ty_layout("utsname"))?; + let values = [ + ("sysname", uname_buf.sysname.as_ptr()), + ("nodename", uname_buf.nodename.as_ptr()), + ("release", uname_buf.release.as_ptr()), + ("version", uname_buf.version.as_ptr()), + ("machine", uname_buf.machine.as_ptr()), + #[cfg(any(target_os = "linux", target_os = "android"))] + ("domainname", uname_buf.domainname.as_ptr()), + ]; + for (name, value) in values { + let value = unsafe { CStr::from_ptr(value) }.to_bytes(); + let field = this.project_field_named(&uname, name)?; + let size = field.layout().layout.size().bytes(); + let (written, _) = this.write_c_str(value, 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/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 64b8376ff4..430a4f0430 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -172,6 +172,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.getpid()?; this.write_scalar(result, dest)?; } + "uname" => { + let [uname] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> i32), + link_name, + abi, + args, + )?; + let result = this.uname(uname)?; + this.write_scalar(result, dest)?; + } "sysconf" => { let [val] = this.check_shim_sig( shim_sig!(extern "C" fn(i32) -> isize), diff --git a/tests/pass-dep/libc/libc-uname.rs b/tests/pass-dep/libc/libc-uname.rs new file mode 100644 index 0000000000..53390eafea --- /dev/null +++ b/tests/pass-dep/libc/libc-uname.rs @@ -0,0 +1,34 @@ +use std::ffi::CStr; +use std::{io, ptr}; + +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() }; + let result = unsafe { libc::uname(&mut uname) }; + if result != 0 { + panic!("failed to call uname"); + } + + // These values are only correct when running isolated. + assert_eq!(unsafe { CStr::from_ptr(&uname.sysname as *const _) }, c"Linux"); + assert_eq!(unsafe { CStr::from_ptr(&uname.nodename as *const _) }, c"Miri"); + assert_eq!(unsafe { CStr::from_ptr(&uname.release as *const _) }, c"6.18.1-arch1-2"); + assert_eq!( + unsafe { CStr::from_ptr(&uname.version as *const _) }, + c"#1 SMP PREEMPT_DYNAMIC Sat, 13 Dec 2025 18:23:21 +0000" + ); + assert_eq!(unsafe { CStr::from_ptr(&uname.machine as *const _) }, c"x86_64"); + #[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 result = unsafe { libc::uname(ptr::null_mut()) }; + assert_eq!(result, -1); + assert_eq!(io::Error::last_os_error().raw_os_error(), Some(libc::EFAULT)); +}