Skip to content

Commit 384c2be

Browse files
authored
Merge pull request #453 from RalfJung/zst
ZST pointers and pointer equality
2 parents cf44c36 + 83ae053 commit 384c2be

21 files changed

+150
-41
lines changed

rust-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
nightly-2018-09-17
1+
nightly-2018-09-18

src/memory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub enum MemoryKind {
4646
C,
4747
/// Part of env var emulation
4848
Env,
49-
// mutable statics
49+
/// mutable statics
5050
MutStatic,
5151
}
5252

src/operator.rs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,18 +152,50 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
152152
(Scalar::Ptr(ptr), Scalar::Bits { bits, size }) |
153153
(Scalar::Bits { bits, size }, Scalar::Ptr(ptr)) => {
154154
assert_eq!(size as u64, self.pointer_size().bytes());
155+
let bits = bits as u64;
156+
let (alloc_size, alloc_align) = self.memory.get_size_and_align(ptr.alloc_id)?;
155157

158+
// Case I: Comparing with NULL
156159
if bits == 0 {
157-
// Nothing equals 0, not even dangling pointers. Ideally we would
158-
// require them to be in-bounds of their (possilby dead) allocation,
159-
// but with the allocation gonew e cannot check that.
160-
false
161-
} else {
162-
// Live pointers cannot equal an integer, but again do not
163-
// allow comparing dead pointers.
164-
self.memory.check_bounds(ptr, false)?;
165-
false
160+
// Test if the ptr is in-bounds. Then it cannot be NULL.
161+
if ptr.offset <= alloc_size {
162+
return Ok(false);
163+
}
164+
}
165+
// Case II: Alignment gives it away
166+
if ptr.offset.bytes() % alloc_align.abi() == 0 {
167+
// The offset maintains the allocation alignment, so we know `base+offset`
168+
// is aligned by `alloc_align`.
169+
// FIXME: We could be even more general, e.g. offset 2 into a 4-aligned
170+
// allocation cannot equal 3.
171+
if bits % alloc_align.abi() != 0 {
172+
// The integer is *not* aligned. So they cannot be equal.
173+
return Ok(false);
174+
}
166175
}
176+
// Case III: The integer is too big, and the allocation goes on a bit
177+
// without wrapping around the address space.
178+
{
179+
// Compute the highest address at which this allocation could live.
180+
// Substract one more, because it must be possible to add the size
181+
// to the base address without overflowing -- IOW, the very last address
182+
// of the address space is never dereferencable (but it can be in-bounds, i.e.,
183+
// one-past-the-end).
184+
let max_base_addr =
185+
((1u128 << self.pointer_size().bits())
186+
- u128::from(alloc_size.bytes())
187+
- 1
188+
) as u64;
189+
if let Some(max_addr) = max_base_addr.checked_add(ptr.offset.bytes()) {
190+
if bits > max_addr {
191+
// The integer is too big, this cannot possibly be equal
192+
return Ok(false)
193+
}
194+
}
195+
}
196+
197+
// None of the supported cases.
198+
return err!(InvalidPointerMath);
167199
}
168200
})
169201
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
// This pointer *could* be NULL so we cannot load from it, not even at ZST
3+
let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const ();
4+
let _x: () = unsafe { *ptr }; //~ ERROR outside bounds
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
// This pointer *could* be NULL so we cannot load from it, not even at ZST.
3+
// Not using the () type here, as writes of that type do not even have MIR generated.
4+
// Also not assigning directly as that's array initialization, not assignment.
5+
let zst_val = [1u8; 0];
6+
let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0];
7+
unsafe { *ptr = zst_val; } //~ ERROR outside bounds
8+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
let x: () = unsafe { *std::ptr::null() }; //~ ERROR constant evaluation error: invalid use of NULL pointer
3+
panic!("this should never print: {:?}", x);
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
unsafe { *std::ptr::null_mut() = 0i32 }; //~ ERROR constant evaluation error: invalid use of NULL pointer
3+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
// Not using the () type here, as writes of that type do not even have MIR generated.
3+
// Also not assigning directly as that's array initialization, not assignment.
4+
let zst_val = [1u8; 0];
5+
unsafe { *std::ptr::null_mut() = zst_val }; //~ ERROR constant evaluation error: invalid use of NULL pointer
6+
}

tests/compile-fail/ptr_eq_dangling.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
fn main() {
2+
let b = Box::new(0);
3+
let x = &*b as *const i32; // soon-to-be dangling
4+
drop(b);
5+
let b = Box::new(0);
6+
let y = &*b as *const i32; // different allocation
7+
// We cannot compare these even though both are inbounds -- they *could* be
8+
// equal if memory was reused.
9+
assert!(x != y); //~ ERROR dangling pointer
10+
}

tests/compile-fail/ptr_eq_integer.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use std::mem;
2+
3+
fn main() {
4+
let b = Box::new(0);
5+
let x = &*b as *const i32;
6+
// We cannot compare this with a non-NULL integer. After all, these *could* be equal (with the right base address).
7+
assert!(x != mem::align_of::<i32>() as *const i32); //~ ERROR invalid arithmetic on pointers
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn main() {
2+
let b = Box::new(0);
3+
let x = (&*b as *const i32).wrapping_sub(0x800); // out-of-bounds
4+
let b = Box::new(0);
5+
let y = &*b as *const i32; // different allocation
6+
// We cannot compare these even though both allocations are live -- they *could* be
7+
// equal (with the right base addresses).
8+
assert!(x != y); //~ ERROR outside bounds
9+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
let b = Box::new(0);
3+
let x = (&*b as *const i32).wrapping_sub(0x800); // out-of-bounds
4+
// We cannot compare this with NULL. After all, this *could* be NULL (with the right base address).
5+
assert!(x != std::ptr::null()); //~ ERROR invalid arithmetic on pointers
6+
}

tests/run-pass-fullmir/loop-break-value.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ pub fn main() {
6161
break Default::default()
6262
};
6363
};
64-
assert_eq!(trait_unified_2, [""]);
64+
// compare lengths; ptr comparison is not deterministic
65+
assert_eq!(trait_unified_2.len(), 1);
66+
assert_eq!(trait_unified_2[0].len(), 0);
6567

6668
let trait_unified_3 = loop {
6769
break if false {

tests/run-pass/cast-rfc0401-vtable-kinds.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
12-
// FIXME: remove the next line when https://github.com/rust-lang/rust/issues/43358 is resolved
13-
// compile-flags: -Zmir-opt-level=0
14-
1511
// Check that you can cast between different pointers to trait objects
1612
// whose vtable have the same kind (both lengths, or both trait pointers).
1713

tests/run-pass/const-vec-of-fns.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// pretty-expanded FIXME #23616
12-
1311
/*!
1412
* Try to double-check that static fns have the right size (with or
1513
* without dummy env ptr, as appropriate) by iterating a size-2 array.

tests/run-pass/issue-20575.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
// Test that overloaded calls work with zero arity closures
1212

13-
// pretty-expanded FIXME #23616
14-
1513
fn main() {
1614
let functions: [Box<Fn() -> Option<()>>; 1] = [Box::new(|| None)];
1715

tests/run-pass/pointers.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::usize;
2+
13
fn one_line_ref() -> i16 {
24
*&1
35
}
@@ -44,8 +46,8 @@ fn match_ref_mut() -> i8 {
4446
}
4547

4648
fn dangling_pointer() -> *const i32 {
47-
let b = Box::new(42);
48-
&*b as *const i32
49+
let b = Box::new((42, 42)); // make it bigger than the alignment, so that there is some "room" after this pointer
50+
&b.0 as *const i32
4951
}
5052

5153
fn main() {
@@ -55,7 +57,30 @@ fn main() {
5557
assert_eq!(basic_ref_mut_var(), 3);
5658
assert_eq!(tuple_ref_mut(), (10, 22));
5759
assert_eq!(match_ref_mut(), 42);
58-
// FIXME: improve this test... how?
60+
61+
// Compare even dangling pointers with NULL, and with others in the same allocation, including
62+
// out-of-bounds.
5963
assert!(dangling_pointer() != std::ptr::null());
6064
assert!(match dangling_pointer() as usize { 0 => false, _ => true });
65+
let dangling = dangling_pointer();
66+
assert!(dangling == dangling);
67+
assert!(dangling.wrapping_add(1) != dangling);
68+
assert!(dangling.wrapping_sub(1) != dangling);
69+
70+
// Compare pointer with BIG integers
71+
let dangling = dangling as usize;
72+
assert!(dangling != usize::MAX);
73+
assert!(dangling != usize::MAX - 1);
74+
assert!(dangling != usize::MAX - 2);
75+
assert!(dangling != usize::MAX - 3); // this is even 4-aligned, but it still cannot be equal because of the extra "room" after this pointer
76+
assert_eq!((usize::MAX - 3) % 4, 0); // just to be sure we got this right
77+
78+
// Compare pointer with unaligned integers
79+
assert!(dangling != 1usize);
80+
assert!(dangling != 2usize);
81+
assert!(dangling != 3usize);
82+
// 4 is a possible choice! So we cannot compare with that.
83+
assert!(dangling != 5usize);
84+
assert!(dangling != 6usize);
85+
assert!(dangling != 7usize);
6186
}

tests/run-pass/regions-lifetime-nonfree-late-bound.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
// doing region-folding, when really all clients of the region-folding
2323
// case only want to see FREE lifetime variables, not bound ones.
2424

25-
// pretty-expanded FIXME #23616
26-
2725
#![allow(unused_features)]
2826
#![feature(box_syntax)]
2927

tests/run-pass/sendable-class.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
// Test that a class with only sendable fields can be sent
1212

13-
// pretty-expanded FIXME #23616
14-
1513
use std::sync::mpsc::channel;
1614

1715
#[allow(dead_code)]

tests/run-pass/zst.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,23 @@ fn use_zst() -> A {
1111
}
1212

1313
fn main() {
14+
// Not using the () type here, as writes of that type do not even have MIR generated.
15+
// Also not assigning directly as that's array initialization, not assignment.
16+
let zst_val = [1u8; 0];
17+
1418
assert_eq!(zst_ret(), A);
1519
assert_eq!(use_zst(), A);
16-
let x = 42 as *mut ();
17-
unsafe { *x = (); }
20+
let x = 42 as *mut [u8; 0];
21+
// reading and writing is okay
22+
unsafe { *x = zst_val; }
23+
unsafe { let _y = *x; }
24+
25+
// We should even be able to use "true" pointers for ZST when the allocation has been
26+
// removed already. The box is for a non-ZST to make sure there actually is an allocation.
27+
let mut x_box = Box::new(((), 1u8));
28+
let x = &mut x_box.0 as *mut _ as *mut [u8; 0];
29+
drop(x_box);
30+
// reading and writing is okay
31+
unsafe { *x = zst_val; }
32+
unsafe { let _y = *x; }
1833
}

tests/run-pass/zst2.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)