Skip to content

Commit a25b65e

Browse files
committed
Initial work on Miri permissive-exposed-provenance
1 parent fb01df5 commit a25b65e

File tree

6 files changed

+203
-50
lines changed

6 files changed

+203
-50
lines changed

src/helpers.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -750,9 +750,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
750750

751751
/// Mark a machine allocation that was just created as immutable.
752752
fn mark_immutable(&mut self, mplace: &MemPlace<Tag>) {
753-
let this = self.eval_context_mut();
754-
this.alloc_mark_immutable(mplace.ptr.into_pointer_or_addr().unwrap().provenance.alloc_id)
755-
.unwrap();
753+
if let machine::AllocType::Concrete(alloc_id) =
754+
mplace.ptr.into_pointer_or_addr().unwrap().provenance.alloc_id
755+
{
756+
let this = self.eval_context_mut();
757+
this.alloc_mark_immutable(alloc_id).unwrap();
758+
} else {
759+
bug!("Machine allocation that was just created have concrete provenance");
760+
}
756761
}
757762
}
758763

src/intptrcast.rs

+78-24
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::collections::hash_map::Entry;
44
use log::trace;
55
use rand::Rng;
66

7-
use rustc_data_structures::fx::FxHashMap;
7+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
88
use rustc_target::abi::{HasDataLayout, Size};
99

1010
use crate::*;
@@ -21,6 +21,9 @@ pub struct GlobalStateInner {
2121
/// they do not have an `AllocExtra`.
2222
/// This is the inverse of `int_to_ptr_map`.
2323
base_addr: FxHashMap<AllocId, u64>,
24+
/// Whether an allocation has been exposed or not. This cannot be put
25+
/// into `AllocExtra` for the same reason as `base_addr`.
26+
exposed: FxHashSet<AllocId>,
2427
/// This is used as a memory address when a new pointer is casted to an integer. It
2528
/// is always larger than any address that was previously made part of a block.
2629
next_base_addr: u64,
@@ -34,24 +37,35 @@ impl GlobalStateInner {
3437
GlobalStateInner {
3538
int_to_ptr_map: Vec::default(),
3639
base_addr: FxHashMap::default(),
40+
exposed: FxHashSet::default(),
3741
next_base_addr: STACK_ADDR,
3842
strict_provenance: config.strict_provenance,
3943
}
4044
}
4145
}
4246

4347
impl<'mir, 'tcx> GlobalStateInner {
44-
pub fn ptr_from_addr(addr: u64, ecx: &MiriEvalContext<'mir, 'tcx>) -> Pointer<Option<Tag>> {
45-
trace!("Casting 0x{:x} to a pointer", addr);
48+
// Returns the `AllocId` that corresponds to the specified addr,
49+
// or `None` if the addr is out of bounds
50+
fn alloc_id_from_addr(addr: u64, ecx: &MiriEvalContext<'mir, 'tcx>) -> Option<AllocId> {
4651
let global_state = ecx.machine.intptrcast.borrow();
4752

48-
if global_state.strict_provenance {
49-
return Pointer::new(None, Size::from_bytes(addr));
53+
if addr == 0 {
54+
return None;
5055
}
5156

5257
let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr);
53-
let alloc_id = match pos {
54-
Ok(pos) => Some(global_state.int_to_ptr_map[pos].1),
58+
59+
match pos {
60+
Ok(pos) => {
61+
let (_, alloc_id) = global_state.int_to_ptr_map[pos];
62+
63+
if global_state.exposed.contains(&alloc_id) {
64+
Some(global_state.int_to_ptr_map[pos].1)
65+
} else {
66+
None
67+
}
68+
}
5569
Err(0) => None,
5670
Err(pos) => {
5771
// This is the largest of the adresses smaller than `int`,
@@ -60,22 +74,72 @@ impl<'mir, 'tcx> GlobalStateInner {
6074
// This never overflows because `addr >= glb`
6175
let offset = addr - glb;
6276
// If the offset exceeds the size of the allocation, don't use this `alloc_id`.
63-
if offset
64-
<= ecx
65-
.get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead)
66-
.unwrap()
67-
.0
68-
.bytes()
77+
78+
if global_state.exposed.contains(&alloc_id)
79+
&& offset
80+
<= ecx
81+
.get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead)
82+
.unwrap()
83+
.0
84+
.bytes()
6985
{
7086
Some(alloc_id)
7187
} else {
7288
None
7389
}
7490
}
91+
}
92+
}
93+
94+
pub fn expose_addr(ecx: &MiriEvalContext<'mir, 'tcx>, ptr: Pointer<Tag>) {
95+
let mut global_state = ecx.machine.intptrcast.borrow_mut();
96+
let (tag, _) = ptr.into_parts();
97+
98+
if let machine::AllocType::Concrete(alloc_id) = tag.alloc_id {
99+
global_state.exposed.insert(alloc_id);
100+
}
101+
}
102+
103+
pub fn abs_ptr_to_rel(
104+
ecx: &MiriEvalContext<'mir, 'tcx>,
105+
ptr: Pointer<Tag>,
106+
) -> Option<(AllocId, Size)> {
107+
let (tag, addr) = ptr.into_parts(); // addr is absolute
108+
109+
let alloc_id = if let machine::AllocType::Concrete(alloc_id) = tag.alloc_id {
110+
alloc_id
111+
} else {
112+
match GlobalStateInner::alloc_id_from_addr(addr.bytes(), ecx) {
113+
Some(alloc_id) => alloc_id,
114+
None => return None,
115+
}
75116
};
117+
118+
let base_addr = GlobalStateInner::alloc_base_addr(ecx, alloc_id);
119+
120+
// Wrapping "addr - base_addr"
121+
let dl = ecx.data_layout();
122+
let neg_base_addr = (base_addr as i64).wrapping_neg();
123+
124+
Some((
125+
alloc_id,
126+
Size::from_bytes(dl.overflowing_signed_offset(addr.bytes(), neg_base_addr).0),
127+
))
128+
}
129+
130+
pub fn ptr_from_addr(ecx: &MiriEvalContext<'mir, 'tcx>, addr: u64) -> Pointer<Option<Tag>> {
131+
trace!("Casting 0x{:x} to a pointer", addr);
132+
let global_state = ecx.machine.intptrcast.borrow();
133+
134+
if global_state.strict_provenance {
135+
return Pointer::new(None, Size::from_bytes(addr));
136+
}
137+
138+
let alloc_id = GlobalStateInner::alloc_id_from_addr(addr, ecx);
139+
76140
// Pointers created from integers are untagged.
77141
Pointer::new(
78-
alloc_id.map(|alloc_id| Tag { alloc_id, sb: SbTag::Untagged }),
142+
alloc_id.map(|_| Tag { alloc_id: machine::AllocType::Casted, sb: SbTag::Untagged }),
79143
Size::from_bytes(addr),
80144
)
81145
}
@@ -136,16 +200,6 @@ impl<'mir, 'tcx> GlobalStateInner {
136200
dl.overflowing_offset(base_addr, offset.bytes()).0
137201
}
138202

139-
pub fn abs_ptr_to_rel(ecx: &MiriEvalContext<'mir, 'tcx>, ptr: Pointer<Tag>) -> Size {
140-
let (tag, addr) = ptr.into_parts(); // addr is absolute
141-
let base_addr = GlobalStateInner::alloc_base_addr(ecx, tag.alloc_id);
142-
143-
// Wrapping "addr - base_addr"
144-
let dl = ecx.data_layout();
145-
let neg_base_addr = (base_addr as i64).wrapping_neg();
146-
Size::from_bytes(dl.overflowing_signed_offset(addr.bytes(), neg_base_addr).0)
147-
}
148-
149203
/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
150204
/// of `align` that is larger or equal to `addr`
151205
fn align_addr(addr: u64, align: u64) -> u64 {

src/machine.rs

+43-17
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,16 @@ impl fmt::Display for MiriMemoryKind {
120120
}
121121
}
122122

123+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
124+
pub enum AllocType {
125+
Concrete(AllocId),
126+
Casted,
127+
}
128+
123129
/// Pointer provenance (tag).
124130
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125131
pub struct Tag {
126-
pub alloc_id: AllocId,
132+
pub alloc_id: AllocType,
127133
/// Stacked Borrows tag.
128134
pub sb: SbTag,
129135
}
@@ -148,14 +154,21 @@ impl Provenance for Tag {
148154
write!(f, "{:?}", tag.sb)
149155
}
150156

151-
fn get_alloc_id(self) -> AllocId {
152-
self.alloc_id
157+
fn get_alloc_id(self) -> Option<AllocId> {
158+
if let machine::AllocType::Concrete(alloc_id) = self.alloc_id {
159+
Some(alloc_id)
160+
} else {
161+
None
162+
}
153163
}
154164
}
155165

156166
/// Extra per-allocation data
157167
#[derive(Debug, Clone)]
158168
pub struct AllocExtra {
169+
// TODO: this really doesn't need to be here,
170+
// but we're forced to bodge it here for now
171+
pub alloc_id: AllocId,
159172
/// Stacked Borrows state is only added if it is enabled.
160173
pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
161174
/// Data race detection via the use of a vector-clock,
@@ -566,7 +579,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
566579
};
567580
let alloc: Allocation<Tag, Self::AllocExtra> = alloc.convert_tag_add_extra(
568581
&ecx.tcx,
569-
AllocExtra { stacked_borrows: stacks, data_race: race_alloc },
582+
AllocExtra { alloc_id: id, stacked_borrows: stacks, data_race: race_alloc },
570583
|ptr| Evaluator::tag_alloc_base_pointer(ecx, ptr),
571584
);
572585
Cow::Owned(alloc)
@@ -582,40 +595,47 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
582595
} else {
583596
SbTag::Untagged
584597
};
585-
Pointer::new(Tag { alloc_id: ptr.provenance, sb: sb_tag }, Size::from_bytes(absolute_addr))
598+
Pointer::new(
599+
Tag { alloc_id: machine::AllocType::Concrete(ptr.provenance), sb: sb_tag },
600+
Size::from_bytes(absolute_addr),
601+
)
586602
}
587603

588604
#[inline(always)]
589605
fn ptr_from_addr(
590606
ecx: &MiriEvalContext<'mir, 'tcx>,
591607
addr: u64,
592608
) -> Pointer<Option<Self::PointerTag>> {
593-
intptrcast::GlobalStateInner::ptr_from_addr(addr, ecx)
609+
intptrcast::GlobalStateInner::ptr_from_addr(ecx, addr)
594610
}
595611

596612
/// Convert a pointer with provenance into an allocation-offset pair,
597613
/// or a `None` with an absolute address if that conversion is not possible.
598614
fn ptr_get_alloc(
599615
ecx: &MiriEvalContext<'mir, 'tcx>,
600616
ptr: Pointer<Self::PointerTag>,
601-
) -> (AllocId, Size) {
602-
let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr);
603-
(ptr.provenance.alloc_id, rel)
617+
) -> Option<(AllocId, Size)> {
618+
intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr)
619+
}
620+
621+
fn expose_addr(ecx: &MiriEvalContext<'mir, 'tcx>, ptr: Pointer<Self::PointerTag>) {
622+
intptrcast::GlobalStateInner::expose_addr(ecx, ptr)
604623
}
605624

606625
#[inline(always)]
607626
fn memory_read(
627+
_tcx: TyCtxt<'tcx>,
608628
machine: &Self,
609629
alloc_extra: &AllocExtra,
610630
tag: Tag,
611631
range: AllocRange,
612632
) -> InterpResult<'tcx> {
613633
if let Some(data_race) = &alloc_extra.data_race {
614-
data_race.read(tag.alloc_id, range, machine.data_race.as_ref().unwrap())?;
634+
data_race.read(alloc_extra.alloc_id, range, machine.data_race.as_ref().unwrap())?;
615635
}
616636
if let Some(stacked_borrows) = &alloc_extra.stacked_borrows {
617637
stacked_borrows.memory_read(
618-
tag.alloc_id,
638+
alloc_extra.alloc_id,
619639
tag.sb,
620640
range,
621641
machine.stacked_borrows.as_ref().unwrap(),
@@ -627,17 +647,18 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
627647

628648
#[inline(always)]
629649
fn memory_written(
650+
_tcx: TyCtxt<'tcx>,
630651
machine: &mut Self,
631652
alloc_extra: &mut AllocExtra,
632653
tag: Tag,
633654
range: AllocRange,
634655
) -> InterpResult<'tcx> {
635656
if let Some(data_race) = &mut alloc_extra.data_race {
636-
data_race.write(tag.alloc_id, range, machine.data_race.as_mut().unwrap())?;
657+
data_race.write(alloc_extra.alloc_id, range, machine.data_race.as_mut().unwrap())?;
637658
}
638659
if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
639660
stacked_borrows.memory_written(
640-
tag.alloc_id,
661+
alloc_extra.alloc_id,
641662
tag.sb,
642663
range,
643664
machine.stacked_borrows.as_mut().unwrap(),
@@ -649,20 +670,25 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
649670

650671
#[inline(always)]
651672
fn memory_deallocated(
673+
_tcx: TyCtxt<'tcx>,
652674
machine: &mut Self,
653675
alloc_extra: &mut AllocExtra,
654676
tag: Tag,
655677
range: AllocRange,
656678
) -> InterpResult<'tcx> {
657-
if Some(tag.alloc_id) == machine.tracked_alloc_id {
658-
register_diagnostic(NonHaltingDiagnostic::FreedAlloc(tag.alloc_id));
679+
if Some(alloc_extra.alloc_id) == machine.tracked_alloc_id {
680+
register_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_extra.alloc_id));
659681
}
660682
if let Some(data_race) = &mut alloc_extra.data_race {
661-
data_race.deallocate(tag.alloc_id, range, machine.data_race.as_mut().unwrap())?;
683+
data_race.deallocate(
684+
alloc_extra.alloc_id,
685+
range,
686+
machine.data_race.as_mut().unwrap(),
687+
)?;
662688
}
663689
if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
664690
stacked_borrows.memory_deallocated(
665-
tag.alloc_id,
691+
alloc_extra.alloc_id,
666692
tag.sb,
667693
range,
668694
machine.stacked_borrows.as_mut().unwrap(),

src/shims/mod.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8585
let ptr = this.read_pointer(ptr_op)?;
8686
if let Ok(ptr) = ptr.into_pointer_or_addr() {
8787
// Only do anything if we can identify the allocation this goes to.
88-
let (_, cur_align) =
89-
this.get_alloc_size_and_align(ptr.provenance.alloc_id, AllocCheck::MaybeDead)?;
90-
if cur_align.bytes() >= req_align {
91-
// If the allocation alignment is at least the required alignment we use the
92-
// real implementation.
93-
return Ok(false);
88+
89+
// TODO: should we test for casted pointers here too?
90+
if let machine::AllocType::Concrete(alloc_id) = ptr.provenance.alloc_id {
91+
let (_, cur_align) =
92+
this.get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead)?;
93+
if cur_align.bytes() >= req_align {
94+
// If the allocation alignment is at least the required alignment we use the
95+
// real implementation.
96+
return Ok(false);
97+
}
9498
}
9599
}
96100

tests/compile-fail/ptr_int_guess.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// compile-flags: -Zmiri-disable-stacked-borrows -Zmiri-seed=0
2+
3+
fn ptr_int_guess() {
4+
let x: i32 = 3;
5+
// we fix the seed so that this pointer's value is 0x23080
6+
let _x_ptr = &x as *const i32;
7+
8+
// try and get lucky with a guess
9+
let x_usize = 0x23080 as usize;
10+
let ptr = x_usize as *const i32;
11+
assert_eq!(unsafe { *ptr }, 3); //~ ERROR 0x23080 is not a valid pointer
12+
}
13+
14+
fn main() {
15+
ptr_int_guess();
16+
}

0 commit comments

Comments
 (0)