Skip to content

Commit 67537c4

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

File tree

17 files changed

+168
-18
lines changed

17 files changed

+168
-18
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ 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 leaked
60+
.note = use `const_leak` to leak allocated pointers before returning
61+
5962
const_eval_copy_nonoverlapping_overlapping =
6063
`copy_nonoverlapping` called on overlapping ranges
6164
@@ -338,6 +341,7 @@ const_eval_realloc_or_alloc_with_offset =
338341
{$kind ->
339342
[dealloc] deallocating
340343
[realloc] reallocating
344+
[leak] leaking
341345
*[other] {""}
342346
} {$ptr} which does not point to the beginning of an object
343347

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: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,14 @@ pub type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>;
170170
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
171171
pub enum MemoryKind {
172172
Heap,
173+
HeapLeaked,
173174
}
174175

175176
impl fmt::Display for MemoryKind {
176177
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177178
match self {
178179
MemoryKind::Heap => write!(f, "heap allocation"),
180+
MemoryKind::HeapLeaked => write!(f, "leaked heap allocation"),
179181
}
180182
}
181183
}
@@ -185,6 +187,17 @@ impl interpret::MayLeak for MemoryKind {
185187
fn may_leak(self) -> bool {
186188
match self {
187189
MemoryKind::Heap => false,
190+
MemoryKind::HeapLeaked => true,
191+
}
192+
}
193+
}
194+
195+
impl interpret::IsConstHeap for MemoryKind {
196+
#[inline(always)]
197+
fn is_const_heap(&self) -> bool {
198+
match self {
199+
MemoryKind::Heap => true,
200+
MemoryKind::HeapLeaked => false,
188201
}
189202
}
190203
}
@@ -197,6 +210,13 @@ impl interpret::MayLeak for ! {
197210
}
198211
}
199212

213+
impl interpret::IsConstHeap for ! {
214+
#[inline(always)]
215+
fn is_const_heap(&self) -> bool {
216+
*self
217+
}
218+
}
219+
200220
impl<'tcx> CompileTimeInterpCx<'tcx> {
201221
fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
202222
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
@@ -457,6 +477,46 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
457477
)?;
458478
}
459479
}
480+
481+
sym::const_leak => {
482+
let ptr = ecx.read_pointer(&args[0])?;
483+
let size = ecx.read_scalar(&args[1])?.to_target_usize(ecx)?;
484+
let align = ecx.read_scalar(&args[2])?.to_target_usize(ecx)?;
485+
486+
let size = Size::from_bytes(size);
487+
let align = match Align::from_bytes(align) {
488+
Ok(a) => a,
489+
Err(err) => throw_ub_custom!(
490+
fluent::const_eval_invalid_align_details,
491+
name = "const_leak",
492+
err_kind = err.diag_ident(),
493+
align = err.align()
494+
),
495+
};
496+
497+
// If an allocation is created in an another const,
498+
// we don't reallocate it.
499+
let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr, 0)?;
500+
let is_allocated_in_another_const = matches!(
501+
ecx.tcx.try_get_global_alloc(alloc_id),
502+
Some(interpret::GlobalAlloc::Memory(_))
503+
);
504+
505+
if is_allocated_in_another_const {
506+
// just return the pointer that was passed in
507+
ecx.write_pointer(ptr, dest)?;
508+
} else {
509+
let ptr = ecx.leak_const_heap_ptr(
510+
ptr,
511+
size,
512+
align,
513+
interpret::MemoryKind::Machine(MemoryKind::Heap),
514+
interpret::MemoryKind::Machine(MemoryKind::HeapLeaked),
515+
)?;
516+
ecx.write_pointer(ptr, dest)?;
517+
}
518+
}
519+
460520
// The intrinsic represents whether the value is known to the optimizer (LLVM).
461521
// We're not doing any optimizations here, so there is no optimizer that could know the value.
462522
// (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: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ use rustc_span::def_id::LocalDefId;
2727
use tracing::{instrument, trace};
2828

2929
use super::{
30-
AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok,
30+
AllocId, Allocation, InterpCx, IsConstHeap, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub,
31+
interp_ok,
3132
};
3233
use crate::const_eval;
3334
use crate::const_eval::DummyMachine;
34-
use crate::errors::NestedStaticInThreadLocal;
35+
use crate::errors::{ConstHeapPtrInFinal, NestedStaticInThreadLocal};
3536

3637
pub trait CompileTimeMachine<'tcx, T> = Machine<
3738
'tcx,
@@ -62,7 +63,7 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> {
6263
/// already mutable (as a sanity check).
6364
///
6465
/// Returns an iterator over all relocations referred to by this allocation.
65-
fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
66+
fn intern_shallow<'tcx, T: IsConstHeap, M: CompileTimeMachine<'tcx, T>>(
6667
ecx: &mut InterpCx<'tcx, M>,
6768
alloc_id: AllocId,
6869
mutability: Mutability,
@@ -71,9 +72,16 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
7172
trace!("intern_shallow {:?}", alloc_id);
7273
// remove allocation
7374
// FIXME(#120456) - is `swap_remove` correct?
74-
let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
75+
let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
7576
return Err(());
7677
};
78+
79+
if matches!(kind, MemoryKind::Machine(x) if x.is_const_heap()) {
80+
// emit an error but don't return an `Err` as if we did the caller assumes we found
81+
// a dangling pointer.
82+
ecx.tcx.dcx().emit_err(ConstHeapPtrInFinal { span: ecx.tcx.span });
83+
}
84+
7785
// Set allocation mutability as appropriate. This is used by LLVM to put things into
7886
// read-only memory, and also by Miri when evaluating other globals that
7987
// access this one.
@@ -321,7 +329,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
321329

322330
/// Intern `ret`. This function assumes that `ret` references no other allocation.
323331
#[instrument(level = "debug", skip(ecx))]
324-
pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
332+
pub fn intern_const_alloc_for_constprop<'tcx, T: IsConstHeap, M: CompileTimeMachine<'tcx, T>>(
325333
ecx: &mut InterpCx<'tcx, M>,
326334
alloc_id: AllocId,
327335
) -> InterpResult<'tcx, ()> {

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ pub trait MayLeak: Copy {
4646
fn may_leak(self) -> bool;
4747
}
4848

49+
/// Whether this kind of memory is const heap (allocation that did not call `const_leak`)
50+
pub trait IsConstHeap {
51+
fn is_const_heap(&self) -> bool;
52+
}
53+
4954
/// The functionality needed by memory to manage its allocations
5055
pub trait AllocMap<K: Hash + Eq, V> {
5156
/// Tests if the map contains the given key.

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
274274
M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind))
275275
}
276276

277-
/// If this grows the allocation, `init_growth` determines
278-
/// whether the additional space will be initialized.
279-
pub fn reallocate_ptr(
277+
fn reallocate_ptr_inner(
280278
&mut self,
281279
ptr: Pointer<Option<M::Provenance>>,
282280
old_size_and_align: Option<(Size, Align)>,
283281
new_size: Size,
284282
new_align: Align,
285283
kind: MemoryKind<M::MemoryKind>,
284+
new_kind: MemoryKind<M::MemoryKind>,
286285
init_growth: AllocInit,
287286
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
288287
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?;
@@ -299,7 +298,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
299298
// If requested, we zero-init the entire allocation, to ensure that a growing
300299
// allocation has its new bytes properly set. For the part that is copied,
301300
// `mem_copy` below will de-initialize things as necessary.
302-
let new_ptr = self.allocate_ptr(new_size, new_align, kind, init_growth)?;
301+
let new_ptr = self.allocate_ptr(new_size, new_align, new_kind, init_growth)?;
303302
let old_size = match old_size_and_align {
304303
Some((size, _align)) => size,
305304
None => self.get_alloc_raw(alloc_id)?.size(),
@@ -311,6 +310,47 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
311310
interp_ok(new_ptr)
312311
}
313312

313+
/// If this grows the allocation, `init_growth` determines
314+
/// whether the additional space will be initialized.
315+
pub fn reallocate_ptr(
316+
&mut self,
317+
ptr: Pointer<Option<M::Provenance>>,
318+
old_size_and_align: Option<(Size, Align)>,
319+
new_size: Size,
320+
new_align: Align,
321+
kind: MemoryKind<M::MemoryKind>,
322+
init_growth: AllocInit,
323+
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
324+
self.reallocate_ptr_inner(
325+
ptr,
326+
old_size_and_align,
327+
new_size,
328+
new_align,
329+
kind,
330+
kind,
331+
init_growth,
332+
)
333+
}
334+
335+
pub fn leak_const_heap_ptr(
336+
&mut self,
337+
ptr: Pointer<Option<M::Provenance>>,
338+
size: Size,
339+
align: Align,
340+
kind: MemoryKind<M::MemoryKind>,
341+
new_kind: MemoryKind<M::MemoryKind>,
342+
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
343+
self.reallocate_ptr_inner(
344+
ptr,
345+
Some((size, align)),
346+
size,
347+
align,
348+
kind,
349+
new_kind,
350+
AllocInit::Uninit,
351+
)
352+
}
353+
314354
#[instrument(skip(self), level = "debug")]
315355
pub fn deallocate_ptr(
316356
&mut self,

compiler/rustc_const_eval/src/interpret/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ pub use self::intern::{
2929
HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop,
3030
intern_const_alloc_recursive,
3131
};
32-
pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine};
32+
pub use self::machine::{
33+
AllocMap, IsConstHeap, Machine, MayLeak, ReturnAction, compile_time_machine,
34+
};
3335
pub use self::memory::{AllocInfo, AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
3436
use self::operand::Operand;
3537
pub use self::operand::{ImmTy, Immediate, OpTy};

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_leak => (
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
@@ -712,6 +712,7 @@ symbols! {
712712
const_impl_trait,
713713
const_in_array_repeat_expressions,
714714
const_indexing,
715+
const_leak,
715716
const_let,
716717
const_loop,
717718
const_mut_refs,

0 commit comments

Comments
 (0)