From 4ab79e56222de65e02aa5b43a6bb9779895f6104 Mon Sep 17 00:00:00 2001 From: tiif Date: Thu, 9 May 2024 04:34:32 +0800 Subject: [PATCH] Implement non-null pointer for malloc(0) --- src/tools/miri/src/shims/alloc.rs | 27 +++++++++---------- .../fail-dep/libc/malloc_zero_double_free.rs | 7 +++++ .../libc/malloc_zero_double_free.stderr | 25 +++++++++++++++++ .../fail-dep/libc/malloc_zero_memory_leak.rs | 5 ++++ .../libc/malloc_zero_memory_leak.stderr | 15 +++++++++++ .../miri/tests/pass-dep/libc/libc-mem.rs | 14 ++++++---- 6 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 61e639f76ed..4eefb8b439b 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -36,6 +36,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if kind == MiriMemoryKind::WinHeap || size >= min_align { return Align::from_bytes(min_align).unwrap(); } + if size == 0 { + return Align::ONE; + } // We have `size < min_align`. Round `size` *down* to the next power of two and use that. fn prev_power_of_two(x: u64) -> u64 { let next_pow2 = x.next_power_of_two(); @@ -85,21 +88,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { kind: MiriMemoryKind, ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); - if size == 0 { - Ok(Pointer::null()) - } else { - let align = this.min_align(size, kind); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; - if zero_init { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); - } - Ok(ptr.into()) + let align = this.min_align(size, kind); + let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; + if zero_init { + // We just allocated this, the access is definitely in-bounds and fits into our address space. + this.write_bytes_ptr( + ptr.into(), + iter::repeat(0u8).take(usize::try_from(size).unwrap()), + ) + .unwrap(); } + Ok(ptr.into()) } fn free( diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs new file mode 100644 index 00000000000..3298d61c8e8 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs @@ -0,0 +1,7 @@ +fn main() { + unsafe { + let ptr = libc::malloc(0); + libc::free(ptr); + libc::free(ptr); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr new file mode 100644 index 00000000000..6437c9dbeb4 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr @@ -0,0 +1,25 @@ +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling + | + = 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 +help: ALLOC was allocated here: + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | let ptr = libc::malloc(0); + | ^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/malloc_zero_double_free.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 + diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs new file mode 100644 index 00000000000..b09a74f41d7 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs @@ -0,0 +1,5 @@ +fn main() { + unsafe { + let _ptr = libc::malloc(0); //~ERROR: memory leak + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr new file mode 100644 index 00000000000..65ce0dcdcdd --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr @@ -0,0 +1,15 @@ +error: memory leaked: ALLOC (C heap, size: 0, align: 1), allocated here: + --> $DIR/malloc_zero_memory_leak.rs:LL:CC + | +LL | let _ptr = libc::malloc(0); + | ^^^^^^^^^^^^^^^ + | + = note: BACKTRACE: + = note: inside `main` at $DIR/malloc_zero_memory_leak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to 1 previous error + 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 33cdc18b70f..5df3ace7496 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -110,9 +110,10 @@ fn test_malloc() { } unsafe { - // Realloc with size 0 is okay for the null pointer + // Realloc with size 0 is okay for the null pointer (and acts like `malloc(0)`) let p2 = libc::realloc(ptr::null_mut(), 0); - assert!(p2.is_null()); + assert!(!p2.is_null()); + libc::free(p2); } unsafe { @@ -126,13 +127,16 @@ fn test_malloc() { fn test_calloc() { unsafe { let p1 = libc::calloc(0, 0); - assert!(p1.is_null()); + assert!(!p1.is_null()); + libc::free(p1); let p2 = libc::calloc(20, 0); - assert!(p2.is_null()); + assert!(!p2.is_null()); + libc::free(p2); let p3 = libc::calloc(0, 20); - assert!(p3.is_null()); + assert!(!p3.is_null()); + libc::free(p3); let p4 = libc::calloc(4, 8); assert!(!p4.is_null());