Skip to content

Commit 6714e03

Browse files
committed
Auto merge of #3580 - tiif:feat/malloc0-non-null-pointer, r=RalfJung
Implement non-null pointer for malloc(0) Use non-null pointer for malloc(0) as mentioned in #3576 to detect leaks and double free of ``malloc(0)`` addresses.
2 parents d9188da + 3a2524a commit 6714e03

File tree

6 files changed

+74
-19
lines changed

6 files changed

+74
-19
lines changed

src/shims/alloc.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
3636
if kind == MiriMemoryKind::WinHeap || size >= min_align {
3737
return Align::from_bytes(min_align).unwrap();
3838
}
39+
if size == 0 {
40+
return Align::ONE;
41+
}
3942
// We have `size < min_align`. Round `size` *down* to the next power of two and use that.
4043
fn prev_power_of_two(x: u64) -> u64 {
4144
let next_pow2 = x.next_power_of_two();
@@ -85,21 +88,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
8588
kind: MiriMemoryKind,
8689
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
8790
let this = self.eval_context_mut();
88-
if size == 0 {
89-
Ok(Pointer::null())
90-
} else {
91-
let align = this.min_align(size, kind);
92-
let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?;
93-
if zero_init {
94-
// We just allocated this, the access is definitely in-bounds and fits into our address space.
95-
this.write_bytes_ptr(
96-
ptr.into(),
97-
iter::repeat(0u8).take(usize::try_from(size).unwrap()),
98-
)
99-
.unwrap();
100-
}
101-
Ok(ptr.into())
91+
let align = this.min_align(size, kind);
92+
let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?;
93+
if zero_init {
94+
// We just allocated this, the access is definitely in-bounds and fits into our address space.
95+
this.write_bytes_ptr(
96+
ptr.into(),
97+
iter::repeat(0u8).take(usize::try_from(size).unwrap()),
98+
)
99+
.unwrap();
102100
}
101+
Ok(ptr.into())
103102
}
104103

105104
fn free(
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
unsafe {
3+
let ptr = libc::malloc(0);
4+
libc::free(ptr);
5+
libc::free(ptr); //~ERROR: dangling
6+
}
7+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
2+
--> $DIR/malloc_zero_double_free.rs:LL:CC
3+
|
4+
LL | libc::free(ptr);
5+
| ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
help: ALLOC was allocated here:
10+
--> $DIR/malloc_zero_double_free.rs:LL:CC
11+
|
12+
LL | let ptr = libc::malloc(0);
13+
| ^^^^^^^^^^^^^^^
14+
help: ALLOC was deallocated here:
15+
--> $DIR/malloc_zero_double_free.rs:LL:CC
16+
|
17+
LL | libc::free(ptr);
18+
| ^^^^^^^^^^^^^^^
19+
= note: BACKTRACE (of the first span):
20+
= note: inside `main` at $DIR/malloc_zero_double_free.rs:LL:CC
21+
22+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23+
24+
error: aborting due to 1 previous error
25+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
unsafe {
3+
let _ptr = libc::malloc(0); //~ERROR: memory leak
4+
}
5+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: memory leaked: ALLOC (C heap, size: 0, align: 1), allocated here:
2+
--> $DIR/malloc_zero_memory_leak.rs:LL:CC
3+
|
4+
LL | let _ptr = libc::malloc(0);
5+
| ^^^^^^^^^^^^^^^
6+
|
7+
= note: BACKTRACE:
8+
= note: inside `main` at $DIR/malloc_zero_memory_leak.rs:LL:CC
9+
10+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
11+
12+
note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
13+
14+
error: aborting due to 1 previous error
15+

tests/pass-dep/libc/libc-mem.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,10 @@ fn test_malloc() {
110110
}
111111

112112
unsafe {
113-
// Realloc with size 0 is okay for the null pointer
113+
// Realloc with size 0 is okay for the null pointer (and acts like `malloc(0)`)
114114
let p2 = libc::realloc(ptr::null_mut(), 0);
115-
assert!(p2.is_null());
115+
assert!(!p2.is_null());
116+
libc::free(p2);
116117
}
117118

118119
unsafe {
@@ -126,13 +127,16 @@ fn test_malloc() {
126127
fn test_calloc() {
127128
unsafe {
128129
let p1 = libc::calloc(0, 0);
129-
assert!(p1.is_null());
130+
assert!(!p1.is_null());
131+
libc::free(p1);
130132

131133
let p2 = libc::calloc(20, 0);
132-
assert!(p2.is_null());
134+
assert!(!p2.is_null());
135+
libc::free(p2);
133136

134137
let p3 = libc::calloc(0, 20);
135-
assert!(p3.is_null());
138+
assert!(!p3.is_null());
139+
libc::free(p3);
136140

137141
let p4 = libc::calloc(4, 8);
138142
assert!(!p4.is_null());

0 commit comments

Comments
 (0)