Skip to content

Commit 8f854d9

Browse files
committed
const heap: fix ICE on forgotten make_global
1 parent fd48b7b commit 8f854d9

File tree

7 files changed

+58
-67
lines changed

7 files changed

+58
-67
lines changed

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace};
1818
use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine};
1919
use crate::const_eval::CheckAlignment;
2020
use crate::interpret::{
21-
CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind,
21+
CtfeValidationMode, GlobalId, Immediate, InternError, InternKind, InterpCx, InterpErrorKind,
2222
InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, ReturnContinuation, create_static_alloc,
2323
intern_const_alloc_recursive, interp_ok, throw_exhaust,
2424
};
@@ -98,20 +98,25 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
9898

9999
match intern_result {
100100
Ok(()) => {}
101-
Err(InternResult::FoundDanglingPointer) => {
101+
Err(InternError::DanglingPointer) => {
102102
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
103103
ecx.tcx
104104
.dcx()
105105
.emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
106106
)));
107107
}
108-
Err(InternResult::FoundBadMutablePointer) => {
108+
Err(InternError::BadMutablePointer) => {
109109
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
110110
ecx.tcx
111111
.dcx()
112112
.emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
113113
)));
114114
}
115+
Err(InternError::ConstAllocNotGlobal) => {
116+
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
117+
ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }),
118+
)));
119+
}
115120
}
116121

117122
interp_ok(R::make_result(ret, ecx))

compiler/rustc_const_eval/src/interpret/intern.rs

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,9 @@ use rustc_middle::ty::layout::TyAndLayout;
2626
use rustc_span::def_id::LocalDefId;
2727
use tracing::{instrument, trace};
2828

29-
use super::{
30-
AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok,
31-
};
32-
use crate::const_eval;
29+
use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, interp_ok};
3330
use crate::const_eval::DummyMachine;
34-
use crate::errors::{ConstHeapPtrInFinal, NestedStaticInThreadLocal};
31+
use crate::{const_eval, errors};
3532

3633
pub trait CompileTimeMachine<'tcx, T> = Machine<
3734
'tcx,
@@ -63,7 +60,7 @@ pub enum DisallowInternReason {
6360
///
6461
/// This prevents us from interning `const_allocate` pointers that have not been made
6562
/// global through `const_make_global`.
66-
pub trait CanIntern {
63+
pub trait CanIntern: Copy {
6764
fn disallows_intern(&self) -> Option<DisallowInternReason>;
6865
}
6966

@@ -96,24 +93,22 @@ fn intern_shallow<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>(
9693
alloc_id: AllocId,
9794
mutability: Mutability,
9895
disambiguator: Option<&mut DisambiguatorState>,
99-
) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> {
96+
) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, InternError> {
10097
trace!("intern_shallow {:?}", alloc_id);
10198
// remove allocation
10299
// FIXME(#120456) - is `swap_remove` correct?
103100
let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
104-
return Err(());
101+
return Err(InternError::DanglingPointer);
105102
};
106103

107104
match kind {
108105
MemoryKind::Machine(x) if let Some(reason) = x.disallows_intern() => match reason {
109-
// Attempting to intern a `const_allocate`d pointer that was not made global via
110-
// `const_make_global`. This is an error, but if we return `Err` now, things
111-
// become inconsistent because we already removed the allocation from `alloc_map`.
112-
// So instead we just emit an error and then continue interning as usual.
113-
// We *could* do this check before removing the allocation from `alloc_map`, but then
114-
// it would be much harder to ensure that we only error once for each allocation.
115106
DisallowInternReason::ConstHeap => {
116-
ecx.tcx.dcx().emit_err(ConstHeapPtrInFinal { span: ecx.tcx.span });
107+
// Attempting to intern a `const_allocate`d pointer that was not made global via
108+
// `const_make_global`. We want to error here, but we have to first put the
109+
// allocation back into the `alloc_map` to keep things in a consistent state.
110+
ecx.memory.alloc_map.insert(alloc_id, (kind, alloc));
111+
return Err(InternError::ConstAllocNotGlobal);
117112
}
118113
},
119114
MemoryKind::Machine(_) | MemoryKind::Stack | MemoryKind::CallerLocation => {}
@@ -170,7 +165,7 @@ fn intern_as_new_static<'tcx>(
170165
tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
171166

172167
if tcx.is_thread_local_static(static_id.into()) {
173-
tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
168+
tcx.dcx().emit_err(errors::NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
174169
}
175170

176171
// These do not inherit the codegen attrs of the parent static allocation, since
@@ -196,9 +191,10 @@ pub enum InternKind {
196191
}
197192

198193
#[derive(Debug)]
199-
pub enum InternResult {
200-
FoundBadMutablePointer,
201-
FoundDanglingPointer,
194+
pub enum InternError {
195+
BadMutablePointer,
196+
DanglingPointer,
197+
ConstAllocNotGlobal,
202198
}
203199

204200
/// Intern `ret` and everything it references.
@@ -212,7 +208,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
212208
ecx: &mut InterpCx<'tcx, M>,
213209
intern_kind: InternKind,
214210
ret: &MPlaceTy<'tcx>,
215-
) -> Result<(), InternResult> {
211+
) -> Result<(), InternError> {
216212
let mut disambiguator = DisambiguatorState::new();
217213

218214
// We are interning recursively, and for mutability we are distinguishing the "root" allocation
@@ -269,6 +265,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
269265
// We want to first report "dangling" and then "mutable", so we need to delay reporting these
270266
// errors.
271267
let mut result = Ok(());
268+
let mut found_bad_mutable_ptr = false;
272269

273270
// Keep interning as long as there are things to intern.
274271
// We show errors if there are dangling pointers, or mutable pointers in immutable contexts
@@ -323,18 +320,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
323320
// when there is memory there that someone might expect to be mutable, but we make it immutable.
324321
let dangling = !is_already_global && !ecx.memory.alloc_map.contains_key(&alloc_id);
325322
if !dangling {
326-
// Found a mutable pointer inside a const where inner allocations should be
327-
// immutable.
328-
if !ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
329-
span_bug!(
330-
ecx.tcx.span,
331-
"the static const safety checks accepted a mutable pointer they should not have accepted"
332-
);
333-
}
334-
// Prefer dangling pointer errors over mutable pointer errors
335-
if result.is_ok() {
336-
result = Err(InternResult::FoundBadMutablePointer);
337-
}
323+
found_bad_mutable_ptr = true;
338324
}
339325
}
340326
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
@@ -355,12 +341,25 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
355341
just_interned.insert(alloc_id);
356342
match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) {
357343
Ok(nested) => todo.extend(nested),
358-
Err(()) => {
359-
ecx.tcx.dcx().delayed_bug("found dangling pointer during const interning");
360-
result = Err(InternResult::FoundDanglingPointer);
344+
Err(err) => {
345+
ecx.tcx.dcx().delayed_bug("error during const interning");
346+
result = Err(err);
361347
}
362348
}
363349
}
350+
if found_bad_mutable_ptr && result.is_ok() {
351+
// We found a mutable pointer inside a const where inner allocations should be immutable,
352+
// and there was no other error. This should usually never happen! However, this can happen
353+
// in unleash-miri mode, so report it as a normal error then.
354+
if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
355+
result = Err(InternError::BadMutablePointer);
356+
} else {
357+
span_bug!(
358+
ecx.tcx.span,
359+
"the static const safety checks accepted a mutable pointer they should not have accepted"
360+
);
361+
}
362+
}
364363
result
365364
}
366365

@@ -375,10 +374,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T: CanIntern, M: CompileTimeMachin
375374
return interp_ok(());
376375
}
377376
// Move allocation to `tcx`.
378-
if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None)
379-
.map_err(|()| err_ub!(DeadLocal))?
380-
.next()
381-
{
377+
if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None).unwrap().next() {
382378
// We are not doing recursive interning, so we don't currently support provenance.
383379
// (If this assertion ever triggers, we should just implement a
384380
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.

compiler/rustc_const_eval/src/interpret/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub use self::call::FnArg;
2626
pub use self::eval_context::{InterpCx, format_interp_error};
2727
use self::eval_context::{from_known_layout, mir_assign_valid_types};
2828
pub use self::intern::{
29-
HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop,
29+
HasStaticRootDefId, InternError, InternKind, intern_const_alloc_for_constprop,
3030
intern_const_alloc_recursive,
3131
};
3232
pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine};

compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
558558
{
559559
// Everything should be already interned.
560560
let Some(global_alloc) = self.ecx.tcx.try_get_global_alloc(alloc_id) else {
561-
assert!(self.ecx.memory.alloc_map.get(alloc_id).is_none());
561+
if self.ecx.memory.alloc_map.contains_key(&alloc_id) {
562+
// This can happen when interning didn't complete due to, e.g.
563+
// missing `make_global`. This must mean other errors are already
564+
// being reported.
565+
self.ecx.tcx.dcx().delayed_bug(
566+
"interning did not complete, there should be an error",
567+
);
568+
return interp_ok(());
569+
}
562570
// We can't have *any* references to non-existing allocations in const-eval
563571
// as the rest of rustc isn't happy with them... so we throw an error, even
564572
// though for zero-sized references this isn't really UB.

tests/crashes/const_mut_ref_check_bypass.rs

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
// We unleash Miri here since this test demonstrates code that bypasses the checks against interning
2-
// mutable pointers, which currently ICEs. Unleashing Miri silences the ICE.
3-
//@ compile-flags: -Zunleash-the-miri-inside-of-you
1+
// Ensure that we reject interning `const_allocate`d allocations in the final value of constants
2+
// if they have not been made global through `const_make_global`. This covers the case where the
3+
// pointer is even still mutable, which used to ICE.
44
#![feature(core_intrinsics)]
55
#![feature(const_heap)]
66
use std::intrinsics;
77

88
const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 };
9-
//~^ error: mutable pointer in final value of constant
10-
//~| error: encountered `const_allocate` pointer in final value that was not made global
9+
//~^ error: encountered `const_allocate` pointer in final value that was not made global
1110

1211
fn main() {}

tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,5 @@ LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32
66
|
77
= note: use `const_make_global` to make allocated pointers immutable before returning
88

9-
error: encountered mutable pointer in final value of constant
10-
--> $DIR/ptr_not_made_global_mut.rs:8:1
11-
|
12-
LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 };
13-
| ^^^^^^^^^^^^^^^^^^^
14-
15-
error: aborting due to 2 previous errors
9+
error: aborting due to 1 previous error
1610

0 commit comments

Comments
 (0)