From 27afeb0085db8f066636a512241ed8e2003a3fe2 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Wed, 8 Oct 2025 01:59:44 +0530 Subject: [PATCH 1/2] feat: add support for libc::memset Signed-off-by: vishruth-thimmaiah --- src/tools/miri/src/shims/foreign_items.rs | 16 +++++++ .../miri/tests/pass-dep/libc/libc-mem.rs | 42 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 1d086906e7a5..3fd57d3c8db2 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -827,6 +827,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?; this.write_pointer(ptr_dest, dest)?; } + "memset" => { + let [ptr_dest, val, n] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let ptr_dest = this.read_pointer(ptr_dest)?; + let val = this.read_scalar(val)?.to_i32()?; + let n = this.read_target_usize(n)?; + // The docs say val is "interpreted as unsigned char". + #[expect(clippy::as_conversions)] + let val = val as u8; + + this.ptr_get_alloc_id(ptr_dest, 0)?; + + let bytes = std::iter::repeat_n(val, n.try_into().unwrap()); + this.write_bytes_ptr(ptr_dest, bytes)?; + this.write_pointer(ptr_dest, dest)?; + } // LLVM intrinsics "llvm.prefetch" => { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 727533a9de61..531d637d1f24 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -78,6 +78,47 @@ fn test_strcpy() { } } +fn test_memset() { + unsafe { + let val = 1; + let dest = libc::calloc(3, 1); + libc::memset(dest, val, 3); + let slc = std::slice::from_raw_parts(dest as *const i8, 3); + assert_eq!(*slc, [1i8, 1, 1]); + libc::free(dest); + } + + unsafe { + let val = 1; + let dest = libc::calloc(4, 1); + libc::memset(dest, val, 3); + let slc = std::slice::from_raw_parts(dest as *const i8, 4); + assert_eq!(*slc, [1i8, 1, 1, 0]); + libc::free(dest); + } + + unsafe { + let val = 1; + let mut dest = 0_i8; + libc::memset(&mut dest as *mut i8 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, val as i8); + } + + unsafe { + let val = 1; + let mut dest = 0_i16; + libc::memset(&mut dest as *mut i16 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, 257); + } + + unsafe { + let val = 257; + let mut dest = 0_i16; + libc::memset(&mut dest as *mut i16 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, 257); + } +} + fn test_malloc() { // Test that small allocations sometimes *are* not very aligned. let saw_unaligned = (0..64).any(|_| unsafe { @@ -310,4 +351,5 @@ fn main() { test_memcpy(); test_strcpy(); + test_memset(); } From afea346b3548a9f995b2aacf389dfb094d3997b4 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Fri, 10 Oct 2025 14:55:09 +0530 Subject: [PATCH 2/2] feat: add failing test for zero-sized memset Signed-off-by: vishruth-thimmaiah --- src/tools/miri/src/shims/foreign_items.rs | 1 + src/tools/miri/tests/fail-dep/libc/memset_null.rs | 8 ++++++++ .../miri/tests/fail-dep/libc/memset_null.stderr | 15 +++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/tools/miri/tests/fail-dep/libc/memset_null.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/memset_null.stderr diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 3fd57d3c8db2..08964ba7b329 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -837,6 +837,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { #[expect(clippy::as_conversions)] let val = val as u8; + // C requires that this must always be a valid pointer, even if `n` is zero, so we better check that. this.ptr_get_alloc_id(ptr_dest, 0)?; let bytes = std::iter::repeat_n(val, n.try_into().unwrap()); diff --git a/src/tools/miri/tests/fail-dep/libc/memset_null.rs b/src/tools/miri/tests/fail-dep/libc/memset_null.rs new file mode 100644 index 000000000000..c3fa9973e762 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/memset_null.rs @@ -0,0 +1,8 @@ +use std::ptr; + +// null is explicitly called out as UB in the C docs for `memset`. +fn main() { + unsafe { + libc::memset(ptr::null_mut(), 0, 0); //~ERROR: null pointer + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/memset_null.stderr b/src/tools/miri/tests/fail-dep/libc/memset_null.stderr new file mode 100644 index 000000000000..fdc8f3a29f94 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/memset_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got null pointer + --> tests/fail-dep/libc/memset_null.rs:LL:CC + | +LL | libc::memset(ptr::null_mut(), 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail-dep/libc/memset_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error +