@@ -4,11 +4,25 @@ use std::collections::hash_map::Entry;
44use log:: trace;
55use rand:: Rng ;
66
7- use rustc_data_structures:: fx:: FxHashMap ;
7+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
88use rustc_target:: abi:: { HasDataLayout , Size } ;
99
1010use crate :: * ;
1111
12+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
13+ pub enum ProvenanceMode {
14+ /// Int2ptr casts return pointers with "wildcard" provenance
15+ /// that basically matches that of all exposed pointers
16+ /// (and SB tags, if enabled).
17+ Permissive ,
18+ /// Int2ptr casts return pointers with an invalid provenance,
19+ /// i.e., not valid for any memory access.
20+ Strict ,
21+ /// Int2ptr casts determine the allocation they point to at cast time.
22+ /// All allocations are considered exposed.
23+ Legacy ,
24+ }
25+
1226pub type GlobalState = RefCell < GlobalStateInner > ;
1327
1428#[ derive( Clone , Debug ) ]
@@ -21,35 +35,37 @@ pub struct GlobalStateInner {
2135 /// they do not have an `AllocExtra`.
2236 /// This is the inverse of `int_to_ptr_map`.
2337 base_addr : FxHashMap < AllocId , u64 > ,
38+ /// Whether an allocation has been exposed or not. This cannot be put
39+ /// into `AllocExtra` for the same reason as `base_addr`.
40+ exposed : FxHashSet < AllocId > ,
2441 /// This is used as a memory address when a new pointer is casted to an integer. It
2542 /// is always larger than any address that was previously made part of a block.
2643 next_base_addr : u64 ,
27- /// Whether to enforce "strict provenance" rules. Enabling this means int2ptr casts return
28- /// pointers with an invalid provenance, i.e., not valid for any memory access.
29- strict_provenance : bool ,
44+ /// The provenance to use for int2ptr casts
45+ provenance_mode : ProvenanceMode ,
3046}
3147
3248impl GlobalStateInner {
3349 pub fn new ( config : & MiriConfig ) -> Self {
3450 GlobalStateInner {
3551 int_to_ptr_map : Vec :: default ( ) ,
3652 base_addr : FxHashMap :: default ( ) ,
53+ exposed : FxHashSet :: default ( ) ,
3754 next_base_addr : STACK_ADDR ,
38- strict_provenance : config. strict_provenance ,
55+ provenance_mode : config. provenance_mode ,
3956 }
4057 }
4158}
4259
4360impl < ' 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) ;
61+ // Returns the exposed `AllocId` that corresponds to the specified addr,
62+ // or `None` if the addr is out of bounds
63+ fn alloc_id_from_addr ( ecx : & MiriEvalContext < ' mir , ' tcx > , addr : u64 ) -> Option < AllocId > {
4664 let global_state = ecx. machine . intptrcast . borrow ( ) ;
47-
48- if global_state. strict_provenance {
49- return Pointer :: new ( None , Size :: from_bytes ( addr) ) ;
50- }
65+ assert ! ( global_state. provenance_mode != ProvenanceMode :: Strict ) ;
5166
5267 let pos = global_state. int_to_ptr_map . binary_search_by_key ( & addr, |( addr, _) | * addr) ;
68+
5369 let alloc_id = match pos {
5470 Ok ( pos) => Some ( global_state. int_to_ptr_map [ pos] . 1 ) ,
5571 Err ( 0 ) => None ,
@@ -60,6 +76,7 @@ impl<'mir, 'tcx> GlobalStateInner {
6076 // This never overflows because `addr >= glb`
6177 let offset = addr - glb;
6278 // If the offset exceeds the size of the allocation, don't use this `alloc_id`.
79+
6380 if offset
6481 <= ecx
6582 . get_alloc_size_and_align ( alloc_id, AllocCheck :: MaybeDead )
@@ -72,12 +89,65 @@ impl<'mir, 'tcx> GlobalStateInner {
7289 None
7390 }
7491 }
75- } ;
76- // Pointers created from integers are untagged.
77- Pointer :: new (
78- alloc_id. map ( |alloc_id| Tag { alloc_id, sb : SbTag :: Untagged } ) ,
79- Size :: from_bytes ( addr) ,
80- )
92+ } ?;
93+
94+ // In legacy mode, we consider all allocations exposed.
95+ if global_state. provenance_mode == ProvenanceMode :: Legacy
96+ || global_state. exposed . contains ( & alloc_id)
97+ {
98+ Some ( alloc_id)
99+ } else {
100+ None
101+ }
102+ }
103+
104+ pub fn expose_addr ( ecx : & MiriEvalContext < ' mir , ' tcx > , alloc_id : AllocId ) {
105+ trace ! ( "Exposing allocation id {:?}" , alloc_id) ;
106+
107+ let mut global_state = ecx. machine . intptrcast . borrow_mut ( ) ;
108+ if global_state. provenance_mode == ProvenanceMode :: Permissive {
109+ global_state. exposed . insert ( alloc_id) ;
110+ }
111+ }
112+
113+ pub fn ptr_from_addr_transmute (
114+ ecx : & MiriEvalContext < ' mir , ' tcx > ,
115+ addr : u64 ,
116+ ) -> Pointer < Option < Tag > > {
117+ trace ! ( "Transmuting 0x{:x} to a pointer" , addr) ;
118+
119+ let global_state = ecx. machine . intptrcast . borrow ( ) ;
120+
121+ // In legacy mode, we have to support int2ptr transmutes,
122+ // so just pretend they do the same thing as a cast.
123+ if global_state. provenance_mode == ProvenanceMode :: Legacy {
124+ Self :: ptr_from_addr_cast ( ecx, addr)
125+ } else {
126+ Pointer :: new ( None , Size :: from_bytes ( addr) )
127+ }
128+ }
129+
130+ pub fn ptr_from_addr_cast (
131+ ecx : & MiriEvalContext < ' mir , ' tcx > ,
132+ addr : u64 ,
133+ ) -> Pointer < Option < Tag > > {
134+ trace ! ( "Casting 0x{:x} to a pointer" , addr) ;
135+
136+ let global_state = ecx. machine . intptrcast . borrow ( ) ;
137+
138+ if global_state. provenance_mode == ProvenanceMode :: Strict {
139+ Pointer :: new ( None , Size :: from_bytes ( addr) )
140+ } else if global_state. provenance_mode == ProvenanceMode :: Legacy {
141+ let alloc_id = Self :: alloc_id_from_addr ( ecx, addr) ;
142+
143+ Pointer :: new (
144+ alloc_id
145+ . map ( |alloc_id| Tag :: Concrete ( ConcreteTag { alloc_id, sb : SbTag :: Untagged } ) ) ,
146+ Size :: from_bytes ( addr) ,
147+ )
148+ } else {
149+ Pointer :: new ( Some ( Tag :: Wildcard ) , Size :: from_bytes ( addr) )
150+ }
81151 }
82152
83153 fn alloc_base_addr ( ecx : & MiriEvalContext < ' mir , ' tcx > , alloc_id : AllocId ) -> u64 {
@@ -136,14 +206,27 @@ impl<'mir, 'tcx> GlobalStateInner {
136206 dl. overflowing_offset ( base_addr, offset. bytes ( ) ) . 0
137207 }
138208
139- pub fn abs_ptr_to_rel ( ecx : & MiriEvalContext < ' mir , ' tcx > , ptr : Pointer < Tag > ) -> Size {
209+ pub fn abs_ptr_to_rel (
210+ ecx : & MiriEvalContext < ' mir , ' tcx > ,
211+ ptr : Pointer < Tag > ,
212+ ) -> Option < ( AllocId , Size ) > {
140213 let ( tag, addr) = ptr. into_parts ( ) ; // addr is absolute (Tag provenance)
141- let base_addr = GlobalStateInner :: alloc_base_addr ( ecx, tag. alloc_id ) ;
214+
215+ let alloc_id = if let Tag :: Concrete ( concrete) = tag {
216+ concrete. alloc_id
217+ } else {
218+ GlobalStateInner :: alloc_id_from_addr ( ecx, addr. bytes ( ) ) ?
219+ } ;
220+
221+ let base_addr = GlobalStateInner :: alloc_base_addr ( ecx, alloc_id) ;
142222
143223 // Wrapping "addr - base_addr"
144224 let dl = ecx. data_layout ( ) ;
145225 let neg_base_addr = ( base_addr as i64 ) . wrapping_neg ( ) ;
146- Size :: from_bytes ( dl. overflowing_signed_offset ( addr. bytes ( ) , neg_base_addr) . 0 )
226+ Some ( (
227+ alloc_id,
228+ Size :: from_bytes ( dl. overflowing_signed_offset ( addr. bytes ( ) , neg_base_addr) . 0 ) ,
229+ ) )
147230 }
148231
149232 /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
0 commit comments