Skip to content

Commit c48445e

Browse files
committed
Auto merge of #114330 - RalfJung:dagling-ptr-deref, r=oli-obk
don't UB on dangling ptr deref, instead check inbounds on projections This implements rust-lang/reference#1387 in Miri. See that PR for what the change is about. Detecting dangling references in `let x = &...;` is now done by validity checking only, so some tests need to have validity checking enabled. There is no longer inherently a "nodangle" check in evaluating the expression `&*ptr` (aside from the aliasing model). r? `@oli-obk` Based on: - rust-lang/reference#1387 - rust-lang/rust#115524
2 parents e35c181 + dd7e34c commit c48445e

File tree

92 files changed

+380
-295
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+380
-295
lines changed

src/borrow_tracker/stacked_borrows/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use log::trace;
1414
use rustc_data_structures::fx::FxHashSet;
1515
use rustc_middle::mir::{Mutability, RetagKind};
1616
use rustc_middle::ty::{self, layout::HasParamEnv, Ty};
17-
use rustc_target::abi::{Abi, Align, Size};
17+
use rustc_target::abi::{Abi, Size};
1818

1919
use crate::borrow_tracker::{
2020
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder},
@@ -616,7 +616,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
616616
) -> InterpResult<'tcx, Option<Provenance>> {
617617
let this = self.eval_context_mut();
618618
// Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
619-
this.check_ptr_access_align(place.ptr(), size, Align::ONE, CheckInAllocMsg::InboundsTest)?;
619+
this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::InboundsTest)?;
620620

621621
// It is crucial that this gets called on all code paths, to ensure we track tag creation.
622622
let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,

src/borrow_tracker/tree_borrows/mod.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use log::trace;
22

3-
use rustc_target::abi::{Abi, Align, Size};
3+
use rustc_target::abi::{Abi, Size};
44

55
use crate::borrow_tracker::{
66
AccessKind, GlobalState, GlobalStateInner, ProtectorKind, RetagFields,
@@ -206,10 +206,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
206206
// Make sure the new permission makes sense as the initial permission of a fresh tag.
207207
assert!(new_perm.initial_state.is_initial());
208208
// Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
209-
this.check_ptr_access_align(
209+
this.check_ptr_access(
210210
place.ptr(),
211211
ptr_size,
212-
Align::ONE,
213212
CheckInAllocMsg::InboundsTest,
214213
)?;
215214

src/concurrency/data_race.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1017,11 +1017,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
10171017
// even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
10181018
// be 8-aligned).
10191019
let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1020-
this.check_ptr_access_align(
1020+
this.check_ptr_align(
10211021
place.ptr(),
1022-
place.layout.size,
10231022
align,
1024-
CheckInAllocMsg::MemoryAccessTest,
10251023
)?;
10261024
// Ensure the allocation is mutable. Even failing (read-only) compare_exchange need mutable
10271025
// memory on many targets (i.e., they segfault if taht memory is mapped read-only), and

src/helpers.rs

+6-25
Original file line numberDiff line numberDiff line change
@@ -697,27 +697,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
697697
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
698698
let this = self.eval_context_ref();
699699
let ptr = this.read_pointer(op)?;
700-
701-
let mplace = MPlaceTy::from_aligned_ptr(ptr, layout);
702-
703-
this.check_mplace(&mplace)?;
704-
705-
Ok(mplace)
706-
}
707-
708-
/// Deref' a pointer *without* checking that the place is dereferenceable.
709-
fn deref_pointer_unchecked(
710-
&self,
711-
val: &ImmTy<'tcx, Provenance>,
712-
layout: TyAndLayout<'tcx>,
713-
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
714-
let this = self.eval_context_ref();
715-
let mut mplace = this.ref_to_mplace(val)?;
716-
717-
mplace.layout = layout;
718-
mplace.align = layout.align.abi;
719-
720-
Ok(mplace)
700+
Ok(this.ptr_to_mplace(ptr, layout))
721701
}
722702

723703
/// Calculates the MPlaceTy given the offset and layout of an access on an operand
@@ -805,7 +785,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
805785
loop {
806786
// FIXME: We are re-getting the allocation each time around the loop.
807787
// Would be nice if we could somehow "extend" an existing AllocRange.
808-
let alloc = this.get_ptr_alloc(ptr.offset(len, this)?, size1, Align::ONE)?.unwrap(); // not a ZST, so we will get a result
788+
let alloc = this.get_ptr_alloc(ptr.offset(len, this)?, size1)?.unwrap(); // not a ZST, so we will get a result
809789
let byte = alloc.read_integer(alloc_range(Size::ZERO, size1))?.to_u8()?;
810790
if byte == 0 {
811791
break;
@@ -845,13 +825,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
845825
fn read_wide_str(&self, mut ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
846826
let this = self.eval_context_ref();
847827
let size2 = Size::from_bytes(2);
848-
let align2 = Align::from_bytes(2).unwrap();
828+
this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
849829

850830
let mut wchars = Vec::new();
851831
loop {
852832
// FIXME: We are re-getting the allocation each time around the loop.
853833
// Would be nice if we could somehow "extend" an existing AllocRange.
854-
let alloc = this.get_ptr_alloc(ptr, size2, align2)?.unwrap(); // not a ZST, so we will get a result
834+
let alloc = this.get_ptr_alloc(ptr, size2)?.unwrap(); // not a ZST, so we will get a result
855835
let wchar = alloc.read_integer(alloc_range(Size::ZERO, size2))?.to_u16()?;
856836
if wchar == 0 {
857837
break;
@@ -887,8 +867,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
887867
// Store the UTF-16 string.
888868
let size2 = Size::from_bytes(2);
889869
let this = self.eval_context_mut();
870+
this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
890871
let mut alloc = this
891-
.get_ptr_alloc_mut(ptr, size2 * string_length, Align::from_bytes(2).unwrap())?
872+
.get_ptr_alloc_mut(ptr, size2 * string_length)?
892873
.unwrap(); // not a ZST, so we will get a result
893874
for (offset, wchar) in wide_str.iter().copied().chain(iter::once(0x0000)).enumerate() {
894875
let offset = u64::try_from(offset).unwrap();

src/machine.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
12851285
// We do need to write `uninit` so that even after the call ends, the former contents of
12861286
// this place cannot be observed any more. We do the write after retagging so that for
12871287
// Tree Borrows, this is considered to activate the new tag.
1288+
// Conveniently this also ensures that the place actually points to suitable memory.
12881289
ecx.write_uninit(&protected_place)?;
12891290
// Now we throw away the protected place, ensuring its tag is never used again.
12901291
Ok(())

src/shims/foreign_items.rs

-4
Original file line numberDiff line numberDiff line change
@@ -807,9 +807,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
807807

808808
this.mem_copy(
809809
ptr_src,
810-
Align::ONE,
811810
ptr_dest,
812-
Align::ONE,
813811
Size::from_bytes(n),
814812
true,
815813
)?;
@@ -830,9 +828,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
830828
let n = this.read_c_str(ptr_src)?.len().checked_add(1).unwrap();
831829
this.mem_copy(
832830
ptr_src,
833-
Align::ONE,
834831
ptr_dest,
835-
Align::ONE,
836832
Size::from_bytes(n),
837833
true,
838834
)?;

src/shims/unix/fs.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use log::trace;
1313

1414
use rustc_data_structures::fx::FxHashMap;
1515
use rustc_middle::ty::TyCtxt;
16-
use rustc_target::abi::{Align, Size};
16+
use rustc_target::abi::Size;
1717

1818
use crate::shims::os_str::bytes_to_os_str;
1919
use crate::*;
@@ -756,10 +756,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
756756
trace!("Reading from FD {}, size {}", fd, count);
757757

758758
// Check that the *entire* buffer is actually valid memory.
759-
this.check_ptr_access_align(
759+
this.check_ptr_access(
760760
buf,
761761
Size::from_bytes(count),
762-
Align::ONE,
763762
CheckInAllocMsg::MemoryAccessTest,
764763
)?;
765764

@@ -810,10 +809,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
810809
// Isolation check is done via `FileDescriptor` trait.
811810

812811
// Check that the *entire* buffer is actually valid memory.
813-
this.check_ptr_access_align(
812+
this.check_ptr_access(
814813
buf,
815814
Size::from_bytes(count),
816-
Align::ONE,
817815
CheckInAllocMsg::MemoryAccessTest,
818816
)?;
819817

@@ -1370,7 +1368,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
13701368
("d_reclen", size.into()),
13711369
("d_type", file_type.into()),
13721370
],
1373-
&MPlaceTy::from_aligned_ptr(entry, dirent64_layout),
1371+
&this.ptr_to_mplace(entry, dirent64_layout),
13741372
)?;
13751373

13761374
let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?;

src/shims/unix/linux/sync.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub fn futex<'tcx>(
3434

3535
let thread = this.get_active_thread();
3636
// This is a vararg function so we have to bring our own type for this pointer.
37-
let addr = MPlaceTy::from_aligned_ptr(addr, this.machine.layouts.i32);
37+
let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32);
3838
let addr_usize = addr.ptr().addr().bytes();
3939

4040
let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG");
@@ -85,9 +85,8 @@ pub fn futex<'tcx>(
8585
return Ok(());
8686
}
8787

88-
// `read_timespec` will check the place when it is not null.
89-
let timeout = this.deref_pointer_unchecked(
90-
&this.read_immediate(&args[3])?,
88+
let timeout = this.deref_pointer_as(
89+
&args[3],
9190
this.libc_ty_layout("timespec"),
9291
)?;
9392
let timeout_time = if this.ptr_is_null(timeout.ptr())? {

src/shims/windows/sync.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
322322

323323
let layout = this.machine.layouts.uint(size).unwrap();
324324
let futex_val = this
325-
.read_scalar_atomic(&MPlaceTy::from_aligned_ptr(ptr, layout), AtomicReadOrd::Relaxed)?;
326-
let compare_val = this.read_scalar(&MPlaceTy::from_aligned_ptr(compare, layout))?;
325+
.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Relaxed)?;
326+
let compare_val = this.read_scalar(&this.ptr_to_mplace(compare, layout))?;
327327

328328
if futex_val == compare_val {
329329
// If the values are the same, we have to block.

src/shims/x86/sse3.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use rustc_middle::mir;
22
use rustc_span::Symbol;
3-
use rustc_target::abi::Align;
43
use rustc_target::spec::abi::Abi;
54

65
use super::horizontal_bin_op;
@@ -76,9 +75,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
7675

7776
this.mem_copy(
7877
src_ptr,
79-
Align::ONE,
8078
dest.ptr(),
81-
Align::ONE,
8279
dest.layout.size,
8380
/*nonoverlapping*/ true,
8481
)?;

tests/compiletest.rs

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ regexes! {
181181
r"0x[0-9a-fA-F]+[0-9a-fA-F]{2,2}" => "$$HEX",
182182
// erase specific alignments
183183
"alignment [0-9]+" => "alignment ALIGN",
184+
"[0-9]+ byte alignment but found [0-9]+" => "ALIGN byte alignment but found ALIGN",
184185
// erase thread caller ids
185186
r"call [0-9]+" => "call ID",
186187
// erase platform module paths

tests/fail-dep/shims/mmap_use_after_munmap.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ LL | libc::munmap(ptr, 4096);
1313
= note: BACKTRACE:
1414
= note: inside `main` at $DIR/mmap_use_after_munmap.rs:LL:CC
1515

16-
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
16+
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
1717
--> $DIR/mmap_use_after_munmap.rs:LL:CC
1818
|
1919
LL | let _x = *(ptr as *mut u8);
20-
| ^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
20+
| ^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
2121
|
2222
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
2323
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/alloc/reallocate-change-alloc.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
1+
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
22
--> $DIR/reallocate-change-alloc.rs:LL:CC
33
|
44
LL | let _z = *x;
5-
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
5+
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/concurrency/thread_local_static_dealloc.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
1+
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
22
--> $DIR/thread_local_static_dealloc.rs:LL:CC
33
|
44
LL | let _val = *dangling_ptr.0;
5-
| ^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
5+
| ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/const-ub-checks.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
22
--> $DIR/const-ub-checks.rs:LL:CC
33
|
44
LL | ptr.read();
5-
| ^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
5+
| ^^^^^^^^^^ accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required
66

77
note: erroneous constant encountered
88
--> $DIR/const-ub-checks.rs:LL:CC

tests/fail/dangling_pointers/dangling_pointer_addr_of.rs

-12
This file was deleted.

tests/fail/dangling_pointers/dangling_pointer_addr_of.stderr

-26
This file was deleted.

tests/fail/dangling_pointers/dangling_pointer_deref.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
1+
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
22
--> $DIR/dangling_pointer_deref.rs:LL:CC
33
|
44
LL | let x = unsafe { *p };
5-
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
5+
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/dangling_pointers/dangling_pointer_project_underscore.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
fn main() {
55
let p = {
66
let b = Box::new(42);
7-
&*b as *const i32
7+
&*b as *const i32 as *const (u8, u8, u8, u8)
88
};
99
unsafe {
10-
let _ = *p; //~ ERROR: has been freed
10+
let _ = (*p).1; //~ ERROR: out-of-bounds pointer arithmetic
1111
}
12-
panic!("this should never print");
1312
}

tests/fail/dangling_pointers/dangling_pointer_project_underscore.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
1+
error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling
22
--> $DIR/dangling_pointer_project_underscore.rs:LL:CC
33
|
4-
LL | let _ = *p;
5-
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
4+
LL | let _ = (*p).1;
5+
| ^^^^^^ out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/dangling_pointers/dangling_primitive.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
1+
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
22
--> $DIR/dangling_primitive.rs:LL:CC
33
|
44
LL | dbg!(*ptr);
5-
| ^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
5+
| ^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/dangling_pointers/dangling_zst_deref.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
1+
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
22
--> $DIR/dangling_zst_deref.rs:LL:CC
33
|
44
LL | let _x = unsafe { *p };
5-
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
5+
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/dangling_pointers/deref-invalid-ptr.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: dereferencing pointer failed: 0x10[noalloc] is a dangling pointer (it has no provenance)
1+
error: Undefined Behavior: out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
22
--> $DIR/deref-invalid-ptr.rs:LL:CC
33
|
44
LL | let _y = unsafe { &*x as *const u32 };
5-
| ^^^ dereferencing pointer failed: 0x10[noalloc] is a dangling pointer (it has no provenance)
5+
| ^^^ out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

0 commit comments

Comments
 (0)