Skip to content

Commit 3f9be40

Browse files
committed
fix handling of base address for TypeId allocations
1 parent 9982d64 commit 3f9be40

File tree

5 files changed

+55
-43
lines changed

5 files changed

+55
-43
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -279,23 +279,15 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
279279
fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> {
280280
interp_ok(match (a, b) {
281281
// Comparisons between integers are always known.
282-
(Scalar::Int { .. }, Scalar::Int { .. }) => {
283-
if a == b {
284-
1
285-
} else {
286-
0
287-
}
288-
}
289-
// Comparisons of abstract pointers with null pointers are known if the pointer
290-
// is in bounds, because if they are in bounds, the pointer can't be null.
291-
// Inequality with integers other than null can never be known for sure.
292-
(Scalar::Int(int), ptr @ Scalar::Ptr(..))
293-
| (ptr @ Scalar::Ptr(..), Scalar::Int(int))
282+
(Scalar::Int(a), Scalar::Int(b)) => (a == b) as u8,
283+
// Comparisons of null with an arbitrary scalar can be known if `scalar_may_be_null`
284+
// indicates that the scalar can definitely *not* be null.
285+
(Scalar::Int(int), ptr) | (ptr, Scalar::Int(int))
294286
if int.is_null() && !self.scalar_may_be_null(ptr)? =>
295287
{
296288
0
297289
}
298-
// Equality with integers can never be known for sure.
290+
// Other ways of comparing integers and pointers can never be known for sure.
299291
(Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => 2,
300292
// FIXME: return a `1` for when both sides are the same pointer, *except* that
301293
// some things (like functions and vtables) do not have stable addresses

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,10 @@ pub enum AllocKind {
6767
LiveData,
6868
/// A function allocation (that fn ptrs point to).
6969
Function,
70-
/// A "virtual" allocation, used for vtables and TypeId.
71-
Virtual,
70+
/// A vtable allocation.
71+
VTable,
72+
/// A TypeId allocation.
73+
TypeId,
7274
/// A dead allocation.
7375
Dead,
7476
}
@@ -952,7 +954,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
952954
let kind = match global_alloc {
953955
GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
954956
GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"),
955-
GlobalAlloc::VTable { .. } | GlobalAlloc::TypeId { .. } => AllocKind::Virtual,
957+
GlobalAlloc::VTable { .. } => AllocKind::VTable,
958+
GlobalAlloc::TypeId { .. } => AllocKind::TypeId,
956959
};
957960
return AllocInfo::new(size, align, kind, mutbl);
958961
}
@@ -1617,6 +1620,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
16171620
match self.ptr_try_get_alloc_id(ptr, 0) {
16181621
Ok((alloc_id, offset, _)) => {
16191622
let info = self.get_alloc_info(alloc_id);
1623+
if matches!(info.kind, AllocKind::TypeId) {
1624+
// We *could* actually precisely answer this question since here,
1625+
// the offset *is* the integer value. But the entire point of making
1626+
// this a pointer is not to leak the integer value, so we say everything
1627+
// might be null.
1628+
return interp_ok(true);
1629+
}
16201630
// If the pointer is in-bounds (including "at the end"), it is definitely not null.
16211631
if offset <= info.size {
16221632
return interp_ok(false);

src/tools/miri/src/alloc_addresses/mod.rs

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -116,21 +116,26 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
116116
let this = self.eval_context_ref();
117117
let info = this.get_alloc_info(alloc_id);
118118

119-
// Miri's address assignment leaks state across thread boundaries, which is incompatible
120-
// with GenMC execution. So we instead let GenMC assign addresses to allocations.
121-
if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
122-
let addr = genmc_ctx.handle_alloc(&this.machine, info.size, info.align, memory_kind)?;
123-
return interp_ok(addr);
124-
}
125-
126-
let mut rng = this.machine.rng.borrow_mut();
127119
// This is either called immediately after allocation (and then cached), or when
128120
// adjusting `tcx` pointers (which never get freed). So assert that we are looking
129121
// at a live allocation. This also ensures that we never re-assign an address to an
130122
// allocation that previously had an address, but then was freed and the address
131123
// information was removed.
132124
assert!(!matches!(info.kind, AllocKind::Dead));
133125

126+
// TypeId allocations always have a "base address" of 0 (i.e., the relative offset is the
127+
// hash fragment and therefore equal to the actual integer value).
128+
if matches!(info.kind, AllocKind::TypeId) {
129+
return interp_ok(0);
130+
}
131+
132+
// Miri's address assignment leaks state across thread boundaries, which is incompatible
133+
// with GenMC execution. So we instead let GenMC assign addresses to allocations.
134+
if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
135+
let addr = genmc_ctx.handle_alloc(&this.machine, info.size, info.align, memory_kind)?;
136+
return interp_ok(addr);
137+
}
138+
134139
// This allocation does not have a base address yet, pick or reuse one.
135140
if !this.machine.native_lib.is_empty() {
136141
// In native lib mode, we use the "real" address of the bytes for this allocation.
@@ -157,7 +162,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
157162
this.get_alloc_bytes_unchecked_raw(alloc_id)?
158163
}
159164
}
160-
AllocKind::Function | AllocKind::Virtual => {
165+
AllocKind::Function | AllocKind::VTable => {
161166
// Allocate some dummy memory to get a unique address for this function/vtable.
162167
let alloc_bytes = MiriAllocBytes::from_bytes(
163168
&[0u8; 1],
@@ -169,12 +174,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
169174
std::mem::forget(alloc_bytes);
170175
ptr
171176
}
172-
AllocKind::Dead => unreachable!(),
177+
AllocKind::TypeId | AllocKind::Dead => unreachable!(),
173178
};
174179
// We don't have to expose this pointer yet, we do that in `prepare_for_native_call`.
175180
return interp_ok(base_ptr.addr().to_u64());
176181
}
177182
// We are not in native lib mode, so we control the addresses ourselves.
183+
let mut rng = this.machine.rng.borrow_mut();
178184
if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
179185
&mut *rng,
180186
info.size,
@@ -295,21 +301,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
295301
// Store address in cache.
296302
global_state.base_addr.try_insert(alloc_id, base_addr).unwrap();
297303

298-
// Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it sorted.
299-
// We have a fast-path for the common case that this address is bigger than all previous ones.
300-
let pos = if global_state
301-
.int_to_ptr_map
302-
.last()
303-
.is_some_and(|(last_addr, _)| *last_addr < base_addr)
304-
{
305-
global_state.int_to_ptr_map.len()
306-
} else {
307-
global_state
304+
// Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it
305+
// sorted. We have a fast-path for the common case that this address is bigger than
306+
// all previous ones. We skip this for allocations at address 0; those can't be
307+
// real, they must be TypeId "fake allocations".
308+
if base_addr != 0 {
309+
let pos = if global_state
308310
.int_to_ptr_map
309-
.binary_search_by_key(&base_addr, |(addr, _)| *addr)
310-
.unwrap_err()
311-
};
312-
global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id));
311+
.last()
312+
.is_some_and(|(last_addr, _)| *last_addr < base_addr)
313+
{
314+
global_state.int_to_ptr_map.len()
315+
} else {
316+
global_state
317+
.int_to_ptr_map
318+
.binary_search_by_key(&base_addr, |(addr, _)| *addr)
319+
.unwrap_err()
320+
};
321+
global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id));
322+
}
313323

314324
interp_ok(base_addr)
315325
}

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
650650
dcx.log_protector();
651651
}
652652
},
653-
AllocKind::Function | AllocKind::Virtual | AllocKind::Dead => {
653+
AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
654654
// No stacked borrows on these allocations.
655655
}
656656
}
@@ -1021,7 +1021,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10211021
trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
10221022
alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag);
10231023
}
1024-
AllocKind::Function | AllocKind::Virtual | AllocKind::Dead => {
1024+
AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
10251025
// No stacked borrows on these allocations.
10261026
}
10271027
}

src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
673673
trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
674674
alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
675675
}
676-
AllocKind::Function | AllocKind::Virtual | AllocKind::Dead => {
676+
AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
677677
// No tree borrows on these allocations.
678678
}
679679
}

0 commit comments

Comments
 (0)