Skip to content

Commit 81e59e6

Browse files
committed
Auto merge of #1930 - tavianator:avoid-adjacent-allocations, r=RalfJung
intptrcast: Never allocate two objects directly adjecent When two objects directly follow each other in memory, what is the provenance of an integer cast to a pointer that points directly between them? For a zero-size region, it could point into the end of the first object, or the start of the second. We can avoid answering this difficult question by simply never allocating two objects directly beside each other. This fixes some of the false positives from #1866.
2 parents 4eadf66 + 6a98c64 commit 81e59e6

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

src/intptrcast.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::cell::RefCell;
2-
use std::cmp::max;
32
use std::collections::hash_map::Entry;
43

54
use log::trace;
@@ -107,9 +106,11 @@ impl<'mir, 'tcx> GlobalState {
107106
slack,
108107
);
109108

110-
// Remember next base address. If this allocation is zero-sized, leave a gap
111-
// of at least 1 to avoid two allocations having the same base address.
112-
global_state.next_base_addr = base_addr.checked_add(max(size.bytes(), 1)).unwrap();
109+
// Remember next base address. Leave a gap of at least 1 to avoid two zero-sized allocations
110+
// having the same base address, and to avoid ambiguous provenance for the address between two
111+
// allocations (also see https://github.com/rust-lang/unsafe-code-guidelines/issues/313).
112+
let size_plus_1 = size.bytes().checked_add(1).unwrap();
113+
global_state.next_base_addr = base_addr.checked_add(size_plus_1).unwrap();
113114
// Given that `next_base_addr` increases in each allocation, pushing the
114115
// corresponding tuple keeps `int_to_ptr_map` sorted
115116
global_state.int_to_ptr_map.push((base_addr, alloc_id));

tests/run-pass/adjacent-allocs.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
fn main() {
2+
// The slack between allocations is random.
3+
// Loop a few times to hit the zero-slack case.
4+
for _ in 0..1024 {
5+
let n = 0u64;
6+
let ptr: *const u64 = &n;
7+
8+
// Allocate a new stack variable whose lifetime quickly ends.
9+
// If there's a chance that &m == ptr.add(1), then an int-to-ptr cast of
10+
// that value will have ambiguous provenance between n and m.
11+
// See https://github.com/rust-lang/miri/issues/1866#issuecomment-985770125
12+
{
13+
let m = 0u64;
14+
let _ = &m as *const u64;
15+
}
16+
17+
let iptr = ptr as usize;
18+
let zst = (iptr + 8) as *const ();
19+
// This is a ZST ptr just at the end of `n`, so it should be valid to deref.
20+
unsafe { *zst }
21+
}
22+
}

0 commit comments

Comments
 (0)