Skip to content

Commit 1ac8fc7

Browse files
authored
Rollup merge of rust-lang#67337 - oli-obk:no_mut_static_ref_from_const, r=RalfJung
Ensure that evaluating or validating a constant never reads from a static r? @RalfJung as per rust-lang#66302 (comment) This does not yet address the fact that evaluation of a constant can read from a static (under unleash-miri)
2 parents a4cd03d + 87fea04 commit 1ac8fc7

15 files changed

+301
-28
lines changed

src/librustc_mir/const_eval.rs

+41-12
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,15 @@ fn mk_eval_cx<'mir, 'tcx>(
4545
tcx: TyCtxt<'tcx>,
4646
span: Span,
4747
param_env: ty::ParamEnv<'tcx>,
48+
can_access_statics: bool,
4849
) -> CompileTimeEvalContext<'mir, 'tcx> {
4950
debug!("mk_eval_cx: {:?}", param_env);
50-
InterpCx::new(tcx.at(span), param_env, CompileTimeInterpreter::new(), Default::default())
51+
InterpCx::new(
52+
tcx.at(span),
53+
param_env,
54+
CompileTimeInterpreter::new(),
55+
MemoryExtra { can_access_statics },
56+
)
5157
}
5258

5359
fn op_to_const<'tcx>(
@@ -176,6 +182,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
176182
#[derive(Clone, Debug)]
177183
pub enum ConstEvalError {
178184
NeedsRfc(String),
185+
ConstAccessesStatic,
179186
}
180187

181188
impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalError {
@@ -195,6 +202,7 @@ impl fmt::Display for ConstEvalError {
195202
msg
196203
)
197204
}
205+
ConstAccessesStatic => write!(f, "constant accesses static"),
198206
}
199207
}
200208
}
@@ -204,6 +212,7 @@ impl Error for ConstEvalError {
204212
use self::ConstEvalError::*;
205213
match *self {
206214
NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
215+
ConstAccessesStatic => "constant accesses static",
207216
}
208217
}
209218

@@ -224,6 +233,12 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
224233
pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>,
225234
}
226235

236+
#[derive(Copy, Clone, Debug)]
237+
pub struct MemoryExtra {
238+
/// Whether this machine may read from statics
239+
can_access_statics: bool,
240+
}
241+
227242
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
228243
fn new() -> Self {
229244
CompileTimeInterpreter {
@@ -311,7 +326,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
311326
type ExtraFnVal = !;
312327

313328
type FrameExtra = ();
314-
type MemoryExtra = ();
329+
type MemoryExtra = MemoryExtra;
315330
type AllocExtra = ();
316331

317332
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
@@ -473,7 +488,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
473488

474489
#[inline(always)]
475490
fn init_allocation_extra<'b>(
476-
_memory_extra: &(),
491+
_memory_extra: &MemoryExtra,
477492
_id: AllocId,
478493
alloc: Cow<'b, Allocation>,
479494
_kind: Option<MemoryKind<!>>,
@@ -484,7 +499,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
484499

485500
#[inline(always)]
486501
fn tag_static_base_pointer(
487-
_memory_extra: &(),
502+
_memory_extra: &MemoryExtra,
488503
_id: AllocId,
489504
) -> Self::PointerTag {
490505
()
@@ -527,6 +542,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
527542
fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
528543
Ok(())
529544
}
545+
546+
fn before_access_static(
547+
memory_extra: &MemoryExtra,
548+
_allocation: &Allocation,
549+
) -> InterpResult<'tcx> {
550+
if memory_extra.can_access_statics {
551+
Ok(())
552+
} else {
553+
Err(ConstEvalError::ConstAccessesStatic.into())
554+
}
555+
}
530556
}
531557

532558
/// Extracts a field of a (variant of a) const.
@@ -540,7 +566,7 @@ pub fn const_field<'tcx>(
540566
value: &'tcx ty::Const<'tcx>,
541567
) -> &'tcx ty::Const<'tcx> {
542568
trace!("const_field: {:?}, {:?}", field, value);
543-
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env);
569+
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
544570
// get the operand again
545571
let op = ecx.eval_const_to_op(value, None).unwrap();
546572
// downcast
@@ -560,7 +586,7 @@ pub fn const_caller_location<'tcx>(
560586
(file, line, col): (Symbol, u32, u32),
561587
) -> &'tcx ty::Const<'tcx> {
562588
trace!("const_caller_location: {}:{}:{}", file, line, col);
563-
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all());
589+
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);
564590

565591
let loc_ty = tcx.caller_location_ty();
566592
let loc_place = ecx.alloc_caller_location(file, line, col);
@@ -581,7 +607,7 @@ pub fn const_variant_index<'tcx>(
581607
val: &'tcx ty::Const<'tcx>,
582608
) -> VariantIdx {
583609
trace!("const_variant_index: {:?}", val);
584-
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env);
610+
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
585611
let op = ecx.eval_const_to_op(val, None).unwrap();
586612
ecx.read_discriminant(op).unwrap().1
587613
}
@@ -610,7 +636,9 @@ fn validate_and_turn_into_const<'tcx>(
610636
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
611637
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
612638
let cid = key.value;
613-
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env);
639+
let def_id = cid.instance.def.def_id();
640+
let is_static = tcx.is_static(def_id);
641+
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
614642
let val = (|| {
615643
let mplace = ecx.raw_const_to_mplace(constant)?;
616644
let mut ref_tracking = RefTracking::new(mplace);
@@ -624,8 +652,7 @@ fn validate_and_turn_into_const<'tcx>(
624652
// Now that we validated, turn this into a proper constant.
625653
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
626654
// whether they become immediates.
627-
let def_id = cid.instance.def.def_id();
628-
if tcx.is_static(def_id) || cid.promoted.is_some() {
655+
if is_static || cid.promoted.is_some() {
629656
let ptr = mplace.ptr.to_ptr()?;
630657
Ok(tcx.mk_const(ty::Const {
631658
val: ty::ConstKind::Value(ConstValue::ByRef {
@@ -732,12 +759,14 @@ pub fn const_eval_raw_provider<'tcx>(
732759
return Err(ErrorHandled::Reported);
733760
}
734761

762+
let is_static = tcx.is_static(def_id);
763+
735764
let span = tcx.def_span(cid.instance.def_id());
736765
let mut ecx = InterpCx::new(
737766
tcx.at(span),
738767
key.param_env,
739768
CompileTimeInterpreter::new(),
740-
Default::default()
769+
MemoryExtra { can_access_statics: is_static },
741770
);
742771

743772
let res = ecx.load_mir(cid.instance.def, cid.promoted);
@@ -751,7 +780,7 @@ pub fn const_eval_raw_provider<'tcx>(
751780
}).map_err(|error| {
752781
let err = error_to_const_error(&ecx, error);
753782
// errors in statics are always emitted as fatal errors
754-
if tcx.is_static(def_id) {
783+
if is_static {
755784
// Ensure that if the above error was either `TooGeneric` or `Reported`
756785
// an error must be reported.
757786
let v = err.report_as_error(ecx.tcx, "could not evaluate static initializer");

src/librustc_mir/interpret/intern.rs

+16-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ pub trait CompileTimeMachine<'mir, 'tcx> = Machine<
2020
PointerTag = (),
2121
ExtraFnVal = !,
2222
FrameExtra = (),
23-
MemoryExtra = (),
2423
AllocExtra = (),
2524
MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>,
2625
>;
@@ -320,12 +319,20 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
320319
// We can't call the `intern_shallow` method here, as its logic is tailored to safe
321320
// references and a `leftover_allocations` set (where we only have a todo-list here).
322321
// So we hand-roll the interning logic here again.
323-
if base_intern_mode != InternMode::Static {
324-
// If it's not a static, it *must* be immutable.
325-
// We cannot have mutable memory inside a constant.
326-
// FIXME: ideally we would assert that they already are immutable, to double-
327-
// check our static checks.
328-
alloc.mutability = Mutability::Not;
322+
match base_intern_mode {
323+
InternMode::Static => {}
324+
InternMode::Const | InternMode::ConstBase => {
325+
// If it's not a static, it *must* be immutable.
326+
// We cannot have mutable memory inside a constant.
327+
// We use `delay_span_bug` here, because this can be reached in the presence
328+
// of fancy transmutes.
329+
if alloc.mutability == Mutability::Mut {
330+
// For better errors later, mark the allocation as immutable
331+
// (on top of the delayed ICE).
332+
alloc.mutability = Mutability::Not;
333+
ecx.tcx.sess.delay_span_bug(ecx.tcx.span, "mutable allocation in constant");
334+
}
335+
}
329336
}
330337
let alloc = tcx.intern_const_alloc(alloc);
331338
tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
@@ -337,6 +344,8 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
337344
} else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) {
338345
// dangling pointer
339346
throw_unsup!(ValidationFailure("encountered dangling pointer in final constant".into()))
347+
} else if ecx.tcx.alloc_map.lock().get(alloc_id).is_none() {
348+
span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id);
340349
}
341350
}
342351
Ok(())

src/librustc_mir/interpret/machine.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,10 @@ pub trait Machine<'mir, 'tcx>: Sized {
212212
}
213213

214214
/// Called before a `StaticKind::Static` value is accessed.
215-
fn before_access_static(_allocation: &Allocation) -> InterpResult<'tcx> {
215+
fn before_access_static(
216+
_memory_extra: &Self::MemoryExtra,
217+
_allocation: &Allocation,
218+
) -> InterpResult<'tcx> {
216219
Ok(())
217220
}
218221

src/librustc_mir/interpret/memory.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M>
116116
// carefully copy only the reachable parts.
117117
impl<'mir, 'tcx, M> Clone for Memory<'mir, 'tcx, M>
118118
where
119-
M: Machine<'mir, 'tcx, PointerTag = (), AllocExtra = (), MemoryExtra = ()>,
119+
M: Machine<'mir, 'tcx, PointerTag = (), AllocExtra = ()>,
120+
M::MemoryExtra: Copy,
120121
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>,
121122
{
122123
fn clone(&self) -> Self {
123124
Memory {
124125
alloc_map: self.alloc_map.clone(),
125126
extra_fn_ptr_map: self.extra_fn_ptr_map.clone(),
126127
dead_alloc_map: self.dead_alloc_map.clone(),
127-
extra: (),
128+
extra: self.extra,
128129
tcx: self.tcx,
129130
}
130131
}
@@ -455,7 +456,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
455456
let id = raw_const.alloc_id;
456457
let allocation = tcx.alloc_map.lock().unwrap_memory(id);
457458

458-
M::before_access_static(allocation)?;
459+
M::before_access_static(memory_extra, allocation)?;
459460
Cow::Borrowed(allocation)
460461
}
461462
}

src/librustc_mir/transform/const_prop.rs

+1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
224224
}
225225

226226
fn before_access_static(
227+
_memory_extra: &(),
227228
allocation: &Allocation<Self::PointerTag, Self::AllocExtra>,
228229
) -> InterpResult<'tcx> {
229230
// if the static allocation is mutable or if it has relocations (it may be legal to mutate
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// compile-flags: -Zunleash-the-miri-inside-of-you
2+
3+
#![allow(dead_code)]
4+
5+
const TEST: &u8 = &MY_STATIC;
6+
//~^ skipping const checks
7+
//~| it is undefined behavior to use this value
8+
9+
static MY_STATIC: u8 = 4;
10+
11+
fn main() {
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: skipping const checks
2+
--> $DIR/const-points-to-static.rs:5:20
3+
|
4+
LL | const TEST: &u8 = &MY_STATIC;
5+
| ^^^^^^^^^
6+
7+
error[E0080]: it is undefined behavior to use this value
8+
--> $DIR/const-points-to-static.rs:5:1
9+
|
10+
LL | const TEST: &u8 = &MY_STATIC;
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
12+
|
13+
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0080`.

src/test/ui/consts/const-prop-read-static-in-const.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// compile-flags: -Zunleash-the-miri-inside-of-you
2-
// run-pass
32

43
#![allow(dead_code)]
54

6-
const TEST: u8 = MY_STATIC;
5+
const TEST: u8 = MY_STATIC; //~ ERROR any use of this value will cause an error
76
//~^ skipping const checks
87

98
static MY_STATIC: u8 = 4;
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
warning: skipping const checks
2-
--> $DIR/const-prop-read-static-in-const.rs:6:18
2+
--> $DIR/const-prop-read-static-in-const.rs:5:18
33
|
44
LL | const TEST: u8 = MY_STATIC;
55
| ^^^^^^^^^
66

7+
error: any use of this value will cause an error
8+
--> $DIR/const-prop-read-static-in-const.rs:5:18
9+
|
10+
LL | const TEST: u8 = MY_STATIC;
11+
| -----------------^^^^^^^^^-
12+
| |
13+
| constant accesses static
14+
|
15+
= note: `#[deny(const_err)]` on by default
16+
17+
error: aborting due to previous error
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// compile-flags: -Zunleash-the-miri-inside-of-you
2+
#![warn(const_err)]
3+
4+
#![feature(const_raw_ptr_deref)]
5+
6+
use std::sync::atomic::AtomicUsize;
7+
use std::sync::atomic::Ordering;
8+
9+
const BOO: &usize = { //~ ERROR undefined behavior to use this value
10+
static FOO: AtomicUsize = AtomicUsize::new(0);
11+
unsafe { &*(&FOO as *const _ as *const usize) }
12+
//~^ WARN skipping const checks
13+
};
14+
15+
const FOO: usize = {
16+
static FOO: AtomicUsize = AtomicUsize::new(0);
17+
FOO.fetch_add(1, Ordering::Relaxed) //~ WARN any use of this value will cause an error
18+
//~^ WARN skipping const checks
19+
//~| WARN skipping const checks
20+
};
21+
22+
const BAR: usize = {
23+
static FOO: AtomicUsize = AtomicUsize::new(0);
24+
unsafe { *(&FOO as *const _ as *const usize) } //~ WARN any use of this value will cause an err
25+
//~^ WARN skipping const checks
26+
};
27+
28+
static mut MUTABLE: u32 = 0;
29+
const BAD: u32 = unsafe { MUTABLE }; //~ WARN any use of this value will cause an error
30+
//~^ WARN skipping const checks
31+
32+
// ok some day perhaps
33+
const BOO_OK: &usize = { //~ ERROR it is undefined behavior to use this value
34+
static FOO: usize = 0;
35+
&FOO
36+
//~^ WARN skipping const checks
37+
};
38+
fn main() {}

0 commit comments

Comments
 (0)