Skip to content

Commit b1cc426

Browse files
committed
add const_leak and reject interning non-leaked const_allocate ptrs
1 parent 8df4a58 commit b1cc426

21 files changed

+204
-16
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ const_eval_const_context = {$kind ->
5656
*[other] {""}
5757
}
5858
59+
const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global
60+
.note = use `const_make_global` to make allocated pointers immutable before returning
61+
62+
const_eval_const_make_global_invalid_pointer = invalid pointer passed to `const_make_global`
63+
5964
const_eval_copy_nonoverlapping_overlapping =
6065
`copy_nonoverlapping` called on overlapping ranges
6166
@@ -338,6 +343,7 @@ const_eval_realloc_or_alloc_with_offset =
338343
{$kind ->
339344
[dealloc] deallocating
340345
[realloc] reallocating
346+
[leak] leaking
341347
*[other] {""}
342348
} {$ptr} which does not point to the beginning of an object
343349

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
9393
// Since evaluation had no errors, validate the resulting constant.
9494
const_validate_mplace(ecx, &ret, cid)?;
9595

96-
// Only report this after validation, as validaiton produces much better diagnostics.
96+
// Only report this after validation, as validation produces much better diagnostics.
9797
// FIXME: ensure validation always reports this and stop making interning care about it.
9898

9999
match intern_result {

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,37 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
457457
)?;
458458
}
459459
}
460+
461+
sym::const_make_global => {
462+
let ptr = ecx.read_pointer(&args[0])?;
463+
let size = ecx.read_scalar(&args[1])?.to_target_usize(ecx)?;
464+
let align = ecx.read_scalar(&args[2])?.to_target_usize(ecx)?;
465+
466+
let _size: Size = Size::from_bytes(size);
467+
let _align = match Align::from_bytes(align) {
468+
Ok(a) => a,
469+
Err(err) => throw_ub_custom!(
470+
fluent::const_eval_invalid_align_details,
471+
name = "const_make_global",
472+
err_kind = err.diag_ident(),
473+
align = err.align()
474+
),
475+
};
476+
477+
let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr, 0)?;
478+
let is_allocated_in_another_const = matches!(
479+
ecx.tcx.try_get_global_alloc(alloc_id),
480+
Some(interpret::GlobalAlloc::Memory(_))
481+
);
482+
483+
if !is_allocated_in_another_const {
484+
ecx.leak_const_heap_ptr(ptr)?;
485+
ecx.write_pointer(ptr, dest)?;
486+
} else {
487+
throw_ub_custom!(fluent::const_eval_const_make_global_invalid_pointer);
488+
}
489+
}
490+
460491
// The intrinsic represents whether the value is known to the optimizer (LLVM).
461492
// We're not doing any optimizations here, so there is no optimizer that could know the value.
462493
// (We know the value here in the machine of course, but this is the runtime of that code,

compiler/rustc_const_eval/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ pub(crate) struct MutablePtrInFinal {
4343
pub kind: InternKind,
4444
}
4545

46+
#[derive(Diagnostic)]
47+
#[diag(const_eval_const_heap_ptr_in_final)]
48+
#[note]
49+
pub(crate) struct ConstHeapPtrInFinal {
50+
#[primary_span]
51+
pub span: Span,
52+
}
53+
4654
#[derive(Diagnostic)]
4755
#[diag(const_eval_unstable_in_stable_exposed)]
4856
pub(crate) struct UnstableInStableExposed {

compiler/rustc_const_eval/src/interpret/intern.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use super::{
3131
};
3232
use crate::const_eval;
3333
use crate::const_eval::DummyMachine;
34-
use crate::errors::NestedStaticInThreadLocal;
34+
use crate::errors::{ConstHeapPtrInFinal, NestedStaticInThreadLocal};
3535

3636
pub trait CompileTimeMachine<'tcx, T> = Machine<
3737
'tcx,
@@ -55,14 +55,36 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> {
5555
}
5656
}
5757

58+
pub enum DisallowInternReason {
59+
ConstHeap,
60+
}
61+
62+
pub trait CanIntern {
63+
fn disallows_intern(&self) -> Option<DisallowInternReason>;
64+
}
65+
66+
impl CanIntern for const_eval::MemoryKind {
67+
fn disallows_intern(&self) -> Option<DisallowInternReason> {
68+
match self {
69+
const_eval::MemoryKind::Heap => Some(DisallowInternReason::ConstHeap),
70+
}
71+
}
72+
}
73+
74+
impl CanIntern for ! {
75+
fn disallows_intern(&self) -> Option<DisallowInternReason> {
76+
*self
77+
}
78+
}
79+
5880
/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory.
5981
///
6082
/// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the
6183
/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be*
6284
/// already mutable (as a sanity check).
6385
///
6486
/// Returns an iterator over all relocations referred to by this allocation.
65-
fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
87+
fn intern_shallow<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>(
6688
ecx: &mut InterpCx<'tcx, M>,
6789
alloc_id: AllocId,
6890
mutability: Mutability,
@@ -71,9 +93,22 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
7193
trace!("intern_shallow {:?}", alloc_id);
7294
// remove allocation
7395
// FIXME(#120456) - is `swap_remove` correct?
74-
let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
96+
let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
7597
return Err(());
7698
};
99+
100+
match kind {
101+
MemoryKind::Machine(x) if let Some(reason) = x.disallows_intern() => match reason {
102+
// attempting to intern a `const_allocate`d pointer that was not made global via
103+
// `const_make_global`. We emit an error here but don't return an `Err` as if we
104+
// did the caller assumes we found a dangling pointer.
105+
DisallowInternReason::ConstHeap => {
106+
ecx.tcx.dcx().emit_err(ConstHeapPtrInFinal { span: ecx.tcx.span });
107+
}
108+
},
109+
MemoryKind::Machine(_) | MemoryKind::Stack | MemoryKind::CallerLocation => {}
110+
}
111+
77112
// Set allocation mutability as appropriate. This is used by LLVM to put things into
78113
// read-only memory, and also by Miri when evaluating other globals that
79114
// access this one.
@@ -99,7 +134,7 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
99134
} else {
100135
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
101136
}
102-
Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov))
137+
Ok(alloc.inner().provenance().ptrs().iter().map(|&(_, prov)| prov))
103138
}
104139

105140
/// Creates a new `DefId` and feeds all the right queries to make this `DefId`
@@ -321,7 +356,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
321356

322357
/// Intern `ret`. This function assumes that `ret` references no other allocation.
323358
#[instrument(level = "debug", skip(ecx))]
324-
pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
359+
pub fn intern_const_alloc_for_constprop<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>(
325360
ecx: &mut InterpCx<'tcx, M>,
326361
alloc_id: AllocId,
327362
) -> InterpResult<'tcx, ()> {

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,24 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
311311
interp_ok(new_ptr)
312312
}
313313

314+
pub fn leak_const_heap_ptr(
315+
&mut self,
316+
ptr: Pointer<Option<CtfeProvenance>>,
317+
) -> InterpResult<'tcx>
318+
where
319+
M: Machine<'tcx, Provenance = CtfeProvenance, AllocExtra = (), Bytes = Box<[u8]>>,
320+
{
321+
let (alloc_id, _, _) = self.ptr_get_alloc_id(ptr, 0)?;
322+
let Some((_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else {
323+
return Err(err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccess)))
324+
.into();
325+
};
326+
alloc.mutability = Mutability::Not;
327+
let alloc = self.tcx.mk_const_alloc(alloc);
328+
self.tcx.set_alloc_id_memory(alloc_id, alloc);
329+
interp_ok(())
330+
}
331+
314332
#[instrument(skip(self), level = "debug")]
315333
pub fn deallocate_ptr(
316334
&mut self,

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,12 @@ pub(crate) fn check_intrinsic_type(
415415
vec![Ty::new_mut_ptr(tcx, tcx.types.u8), tcx.types.usize, tcx.types.usize],
416416
tcx.types.unit,
417417
),
418+
sym::const_make_global => (
419+
0,
420+
0,
421+
vec![Ty::new_mut_ptr(tcx, tcx.types.u8), tcx.types.usize, tcx.types.usize],
422+
Ty::new_imm_ptr(tcx, tcx.types.u8),
423+
),
418424

419425
sym::ptr_offset_from => (
420426
1,

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ symbols! {
714714
const_indexing,
715715
const_let,
716716
const_loop,
717+
const_make_global,
717718
const_mut_refs,
718719
const_panic,
719720
const_panic_fmt,

library/alloc/src/boxed/thin.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use core::error::Error;
66
use core::fmt::{self, Debug, Display, Formatter};
77
#[cfg(not(no_global_oom_handling))]
8-
use core::intrinsics::const_allocate;
8+
use core::intrinsics::{const_allocate, const_make_global};
99
use core::marker::PhantomData;
1010
#[cfg(not(no_global_oom_handling))]
1111
use core::marker::Unsize;
@@ -342,7 +342,11 @@ impl<H> WithHeader<H> {
342342
metadata_ptr.write(ptr::metadata::<Dyn>(ptr::dangling::<T>() as *const Dyn));
343343

344344
// SAFETY: we have just written the metadata.
345-
&*(metadata_ptr)
345+
const_make_global(alloc, alloc_size, alloc_align)
346+
.add(metadata_offset)
347+
.cast::<<Dyn as Pointee>::Metadata>()
348+
.as_ref()
349+
.unwrap()
346350
}
347351
};
348352

library/core/src/intrinsics/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,15 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
25332533
// Runtime NOP
25342534
}
25352535

2536+
#[rustc_const_unstable(feature = "const_heap", issue = "79597")]
2537+
#[rustc_nounwind]
2538+
#[rustc_intrinsic]
2539+
#[miri::intrinsic_fallback_is_spec]
2540+
pub const unsafe fn const_make_global(ptr: *mut u8, _size: usize, _align: usize) -> *const u8 {
2541+
// const eval overrides this function.
2542+
ptr
2543+
}
2544+
25362545
/// Returns whether we should perform contract-checking at runtime.
25372546
///
25382547
/// This is meant to be similar to the ub_checks intrinsic, in terms

0 commit comments

Comments
 (0)