Skip to content

Commit 0adc84b

Browse files
committed
Don't allow constant eval to access generator layouts
1 parent bb90f81 commit 0adc84b

File tree

15 files changed

+189
-7
lines changed

15 files changed

+189
-7
lines changed

compiler/rustc_const_eval/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ const_eval_frame_note_inner = inside {$where_ ->
112112
*[other] {""}
113113
}
114114
115+
const_eval_generator_layout_indirect = because of contained type `{$ty}`
116+
115117
const_eval_in_bounds_test = out-of-bounds pointer use
116118
const_eval_incompatible_calling_conventions =
117119
calling a function with calling convention {$callee_conv} using calling convention {$caller_conv}
@@ -431,6 +433,8 @@ const_eval_validation_expected_str = expected a string
431433
const_eval_validation_front_matter_invalid_value = constructing invalid value
432434
const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path}
433435
436+
const_eval_validation_generator_layout_access = cannot compute layout of `{$ty}`
437+
434438
const_eval_validation_invalid_bool = {$front_matter}: encountered {$value}, but expected a boolean
435439
const_eval_validation_invalid_box_meta = {$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object
436440
const_eval_validation_invalid_box_slice_meta = {$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object

compiler/rustc_const_eval/src/const_eval/machine.rs

+3
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
357357

358358
const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
359359

360+
// We don't allow access to generator layout at compile time.
361+
const ACCESS_GENERATOR_LAYOUT: bool = false;
362+
360363
#[inline(always)]
361364
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
362365
ecx.machine.check_alignment

compiler/rustc_const_eval/src/errors.rs

+15
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
674674
InvalidBool { .. } => const_eval_validation_invalid_bool,
675675
InvalidChar { .. } => const_eval_validation_invalid_char,
676676
InvalidFnPtr { .. } => const_eval_validation_invalid_fn_ptr,
677+
GeneratorLayoutAccess { .. } => const_eval_validation_generator_layout_access,
677678
}
678679
}
679680

@@ -773,6 +774,20 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
773774
DanglingPtrNoProvenance { pointer, .. } => {
774775
err.set_arg("pointer", pointer);
775776
}
777+
GeneratorLayoutAccess { ty, generators } => {
778+
err.set_arg("ty", ty);
779+
for generator in generators {
780+
if generator != ty {
781+
let message = handler.eagerly_translate_to_string(
782+
fluent::const_eval_generator_layout_indirect,
783+
[("ty".into(), DiagnosticArgValue::Str(generator.to_string().into()))]
784+
.iter()
785+
.map(|(a, b)| (a, b)),
786+
);
787+
err.help(message);
788+
}
789+
}
790+
}
776791
NullPtr { .. }
777792
| PtrToStatic { .. }
778793
| PtrToMut { .. }

compiler/rustc_const_eval/src/interpret/machine.rs

+3
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
130130
/// Should the machine panic on allocation failures?
131131
const PANIC_ON_ALLOC_FAIL: bool;
132132

133+
/// Should the machine allow access to generator layout.
134+
const ACCESS_GENERATOR_LAYOUT: bool;
135+
133136
/// Whether memory accesses should be alignment-checked.
134137
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment;
135138

compiler/rustc_const_eval/src/interpret/step.rs

+5
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
267267

268268
NullaryOp(ref null_op, ty) => {
269269
let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?;
270+
271+
// Ensure we don't need the layout of any generators if applicable.
272+
// This prevents typeck from depending on MIR optimizations.
273+
self.validate_generator_layout_access(ty)?;
274+
270275
let layout = self.layout_of(ty)?;
271276
if let mir::NullOp::SizeOf | mir::NullOp::AlignOf = null_op && layout.is_unsized() {
272277
span_bug!(

compiler/rustc_const_eval/src/interpret/validity.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use rustc_middle::mir::interpret::{
1616
ExpectedKind, InterpError, InvalidMetaKind, PointerKind, ValidationErrorInfo,
1717
ValidationErrorKind, ValidationErrorKind::*,
1818
};
19-
use rustc_middle::ty;
2019
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
20+
use rustc_middle::ty::{self, Ty};
2121
use rustc_span::symbol::{sym, Symbol};
2222
use rustc_target::abi::{
2323
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
@@ -950,4 +950,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
950950
// recurse through references which, for now, we don't want here, either.
951951
self.validate_operand_internal(op, vec![], None, None)
952952
}
953+
954+
/// This function checks if the given `ty`'s layout depends on generators.
955+
#[inline(always)]
956+
pub fn validate_generator_layout_access(&self, ty: Ty<'tcx>) -> InterpResult<'tcx> {
957+
if !M::ACCESS_GENERATOR_LAYOUT {
958+
let generators = self.tcx.layout_generators(ty);
959+
if !generators.is_empty() {
960+
throw_validation_failure!(Vec::new(), GeneratorLayoutAccess { ty, generators });
961+
}
962+
}
963+
Ok(())
964+
}
953965
}

compiler/rustc_middle/src/mir/interpret/error.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
22

33
use crate::mir::interpret::ConstValue;
44
use crate::query::TyCtxtAt;
5-
use crate::ty::{layout, tls, Ty, ValTree};
5+
use crate::ty::{layout, tls, List, Ty, ValTree};
66

77
use rustc_data_structures::sync::Lock;
88
use rustc_errors::{
@@ -404,6 +404,7 @@ pub enum ValidationErrorKind<'tcx> {
404404
InvalidBool { value: String },
405405
InvalidChar { value: String },
406406
InvalidFnPtr { value: String },
407+
GeneratorLayoutAccess { ty: Ty<'tcx>, generators: &'tcx List<Ty<'tcx>> },
407408
}
408409

409410
/// Error information for when the program did something that might (or might not) be correct

compiler/rustc_middle/src/query/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,15 @@ rustc_queries! {
14051405
desc { "computing layout of `{}`", key.value }
14061406
}
14071407

1408+
/// Computes the generators present in the layout of a type.
1409+
/// This expects a normalized input type with regions erased.
1410+
query layout_generators(
1411+
key: Ty<'tcx>
1412+
) -> &'tcx ty::List<Ty<'tcx>> {
1413+
depth_limit
1414+
desc { "calculating generators in layout of `{}`", key }
1415+
}
1416+
14081417
/// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
14091418
///
14101419
/// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance`

compiler/rustc_mir_transform/src/const_prop.rs

+1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ impl ConstPropMachine<'_, '_> {
147147
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> {
148148
compile_time_machine!(<'mir, 'tcx>);
149149
const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`)
150+
const ACCESS_GENERATOR_LAYOUT: bool = true;
150151

151152
type MemoryKind = !;
152153

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

+1
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
512512
rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>);
513513
type MemoryKind = !;
514514
const PANIC_ON_ALLOC_FAIL: bool = true;
515+
const ACCESS_GENERATOR_LAYOUT: bool = true;
515516

516517
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
517518
unimplemented!()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use rustc_middle::query::Providers;
2+
use rustc_middle::ty::{self, Ty, TyCtxt};
3+
4+
pub fn provide(providers: &mut Providers) {
5+
*providers = Providers { layout_generators, ..*providers };
6+
}
7+
8+
/// Computes the generators present in the layout of a type.
9+
/// This expects a normalized input type with regions erased.
10+
fn layout_generators<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx ty::List<Ty<'tcx>> {
11+
let mut generators = Vec::new();
12+
13+
let inner = |generators: &mut Vec<_>, ty: Ty<'tcx>| {
14+
let list = tcx.layout_generators(ty);
15+
for generator in list.iter() {
16+
generators.push(generator);
17+
}
18+
};
19+
20+
match *ty.kind() {
21+
// These can't contain generators in their layout
22+
ty::Bool
23+
| ty::Char
24+
| ty::Int(_)
25+
| ty::Uint(_)
26+
| ty::Float(_)
27+
| ty::FnPtr(_)
28+
| ty::FnDef(..)
29+
| ty::Never
30+
| ty::Ref(..)
31+
| ty::RawPtr(..)
32+
| ty::Str => {}
33+
34+
ty::Array(element, _) => {
35+
inner(&mut generators, element);
36+
}
37+
38+
ty::Generator(..) => {
39+
generators.push(ty);
40+
}
41+
42+
ty::Closure(_, ref args) => {
43+
let tys = args.as_closure().upvar_tys();
44+
tys.iter().for_each(|ty| inner(&mut generators, ty));
45+
}
46+
47+
ty::Tuple(tys) => {
48+
tys.iter().for_each(|ty| inner(&mut generators, ty));
49+
}
50+
51+
ty::Adt(def, args) => {
52+
def.variants().iter().for_each(|v| {
53+
v.fields.iter().for_each(|field| {
54+
let ty = field.ty(tcx, args);
55+
let ty = tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty);
56+
inner(&mut generators, ty)
57+
})
58+
});
59+
}
60+
61+
ty::Slice(..) | ty::Dynamic(..) | ty::Foreign(..) => {
62+
bug!("these are unsized")
63+
}
64+
65+
ty::Alias(..)
66+
| ty::Bound(..)
67+
| ty::GeneratorWitness(..)
68+
| ty::GeneratorWitnessMIR(..)
69+
| ty::Infer(_)
70+
| ty::Error(_)
71+
| ty::Placeholder(..)
72+
| ty::Param(_) => {
73+
bug!("unexpected type")
74+
}
75+
}
76+
77+
tcx.mk_type_list(&generators)
78+
}

compiler/rustc_ty_utils/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mod errors;
3232
mod implied_bounds;
3333
pub mod instance;
3434
mod layout;
35+
mod layout_generators;
3536
mod layout_sanity_check;
3637
mod needs_drop;
3738
mod opaque_types;
@@ -48,6 +49,7 @@ pub fn provide(providers: &mut Providers) {
4849
consts::provide(providers);
4950
implied_bounds::provide(providers);
5051
layout::provide(providers);
52+
layout_generators::provide(providers);
5153
needs_drop::provide(providers);
5254
opaque_types::provide(providers);
5355
representability::provide(providers);

src/tools/miri/src/machine.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
884884
const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
885885

886886
const PANIC_ON_ALLOC_FAIL: bool = false;
887+
const ACCESS_GENERATOR_LAYOUT: bool = true;
887888

888889
#[inline(always)]
889890
fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> CheckAlignment {
@@ -1410,17 +1411,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
14101411
ecx: &mut InterpCx<'mir, 'tcx, Self>,
14111412
frame: usize,
14121413
local: mir::Local,
1413-
mplace: &MPlaceTy<'tcx, Provenance>
1414+
mplace: &MPlaceTy<'tcx, Provenance>,
14141415
) -> InterpResult<'tcx> {
14151416
let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr.provenance else {
14161417
panic!("after_local_allocated should only be called on fresh allocations");
14171418
};
14181419
let local_decl = &ecx.active_thread_stack()[frame].body.local_decls[local];
14191420
let span = local_decl.source_info.span;
1420-
ecx.machine
1421-
.allocation_spans
1422-
.borrow_mut()
1423-
.insert(alloc_id, (span, None));
1421+
ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
14241422
Ok(())
14251423
}
14261424
}

tests/ui/generator/const-eval-size.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// error-pattern: evaluation of constant value failed
2+
3+
#![feature(generators)]
4+
#![feature(generator_trait)]
5+
#![feature(type_alias_impl_trait)]
6+
#![allow(dead_code)]
7+
8+
type Gen = impl std::ops::Generator;
9+
10+
const A: usize = std::mem::size_of::<Gen>();
11+
12+
const B: usize = std::mem::size_of::<Option<Gen>>();
13+
14+
fn gen() -> Gen {
15+
move || {
16+
yield;
17+
}
18+
}
19+
20+
fn main() {}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
3+
|
4+
= note: cannot compute layout of `[generator@$DIR/const-eval-size.rs:15:5: 15:12]`
5+
|
6+
note: inside `std::mem::size_of::<[generator@$DIR/const-eval-size.rs:15:5: 15:12]>`
7+
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
8+
note: inside `A`
9+
--> $DIR/const-eval-size.rs:10:18
10+
|
11+
LL | const A: usize = std::mem::size_of::<Gen>();
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
14+
error[E0080]: evaluation of constant value failed
15+
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
16+
|
17+
= note: cannot compute layout of `Option<[generator@$DIR/const-eval-size.rs:15:5: 15:12]>`
18+
|
19+
note: inside `std::mem::size_of::<Option<[generator@$DIR/const-eval-size.rs:15:5: 15:12]>>`
20+
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
21+
note: inside `B`
22+
--> $DIR/const-eval-size.rs:12:18
23+
|
24+
LL | const B: usize = std::mem::size_of::<Option<Gen>>();
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
= help: because of contained type `[generator@$DIR/const-eval-size.rs:15:5: 15:12]`
27+
28+
error: aborting due to 2 previous errors
29+
30+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)