Skip to content

Commit ad6b9c7

Browse files
committed
Dynamically prevent constants from accessing statics
1 parent 640e288 commit ad6b9c7

13 files changed

+203
-22
lines changed

src/librustc_mir/const_eval.rs

+40-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>(
@@ -224,6 +230,12 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
224230
pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>,
225231
}
226232

233+
#[derive(Copy, Clone, Debug)]
234+
pub struct MemoryExtra {
235+
/// Whether this machine may read from statics
236+
can_access_statics: bool,
237+
}
238+
227239
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
228240
fn new() -> Self {
229241
CompileTimeInterpreter {
@@ -311,7 +323,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
311323
type ExtraFnVal = !;
312324

313325
type FrameExtra = ();
314-
type MemoryExtra = ();
326+
type MemoryExtra = MemoryExtra;
315327
type AllocExtra = ();
316328

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

474486
#[inline(always)]
475487
fn init_allocation_extra<'b>(
476-
_memory_extra: &(),
488+
_memory_extra: &MemoryExtra,
477489
_id: AllocId,
478490
alloc: Cow<'b, Allocation>,
479491
_kind: Option<MemoryKind<!>>,
@@ -484,7 +496,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
484496

485497
#[inline(always)]
486498
fn tag_static_base_pointer(
487-
_memory_extra: &(),
499+
_memory_extra: &MemoryExtra,
488500
_id: AllocId,
489501
) -> Self::PointerTag {
490502
()
@@ -527,6 +539,19 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
527539
fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
528540
Ok(())
529541
}
542+
543+
fn before_access_static(
544+
memory_extra: &MemoryExtra,
545+
_allocation: &Allocation,
546+
) -> InterpResult<'tcx> {
547+
if memory_extra.can_access_statics {
548+
Ok(())
549+
} else {
550+
Err(ConstEvalError::NeedsRfc(
551+
"constants accessing static items".to_string(),
552+
).into())
553+
}
554+
}
530555
}
531556

532557
/// Extracts a field of a (variant of a) const.
@@ -540,7 +565,7 @@ pub fn const_field<'tcx>(
540565
value: &'tcx ty::Const<'tcx>,
541566
) -> &'tcx ty::Const<'tcx> {
542567
trace!("const_field: {:?}, {:?}", field, value);
543-
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env);
568+
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
544569
// get the operand again
545570
let op = ecx.eval_const_to_op(value, None).unwrap();
546571
// downcast
@@ -560,7 +585,7 @@ pub fn const_caller_location<'tcx>(
560585
(file, line, col): (Symbol, u32, u32),
561586
) -> &'tcx ty::Const<'tcx> {
562587
trace!("const_caller_location: {}:{}:{}", file, line, col);
563-
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all());
588+
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);
564589

565590
let loc_ty = tcx.caller_location_ty();
566591
let loc_place = ecx.alloc_caller_location(file, line, col);
@@ -581,7 +606,7 @@ pub fn const_variant_index<'tcx>(
581606
val: &'tcx ty::Const<'tcx>,
582607
) -> VariantIdx {
583608
trace!("const_variant_index: {:?}", val);
584-
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env);
609+
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
585610
let op = ecx.eval_const_to_op(val, None).unwrap();
586611
ecx.read_discriminant(op).unwrap().1
587612
}
@@ -610,7 +635,9 @@ fn validate_and_turn_into_const<'tcx>(
610635
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
611636
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
612637
let cid = key.value;
613-
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env);
638+
let def_id = cid.instance.def.def_id();
639+
let is_static = tcx.is_static(def_id);
640+
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
614641
let val = (|| {
615642
let mplace = ecx.raw_const_to_mplace(constant)?;
616643
let mut ref_tracking = RefTracking::new(mplace);
@@ -624,8 +651,7 @@ fn validate_and_turn_into_const<'tcx>(
624651
// Now that we validated, turn this into a proper constant.
625652
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
626653
// whether they become immediates.
627-
let def_id = cid.instance.def.def_id();
628-
if tcx.is_static(def_id) || cid.promoted.is_some() {
654+
if is_static || cid.promoted.is_some() {
629655
let ptr = mplace.ptr.to_ptr()?;
630656
Ok(tcx.mk_const(ty::Const {
631657
val: ty::ConstKind::Value(ConstValue::ByRef {
@@ -732,12 +758,14 @@ pub fn const_eval_raw_provider<'tcx>(
732758
return Err(ErrorHandled::Reported);
733759
}
734760

761+
let is_static = tcx.is_static(def_id);
762+
735763
let span = tcx.def_span(cid.instance.def_id());
736764
let mut ecx = InterpCx::new(
737765
tcx.at(span),
738766
key.param_env,
739767
CompileTimeInterpreter::new(),
740-
Default::default()
768+
MemoryExtra { can_access_statics: is_static },
741769
);
742770

743771
let res = ecx.load_mir(cid.instance.def, cid.promoted);
@@ -751,7 +779,7 @@ pub fn const_eval_raw_provider<'tcx>(
751779
}).map_err(|error| {
752780
let err = error_to_const_error(&ecx, error);
753781
// errors in statics are always emitted as fatal errors
754-
if tcx.is_static(def_id) {
782+
if is_static {
755783
// Ensure that if the above error was either `TooGeneric` or `Reported`
756784
// an error must be reported.
757785
let v = err.report_as_error(ecx.tcx, "could not evaluate static initializer");

src/librustc_mir/interpret/intern.rs

-1
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
>;

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+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "constants accessing static items" needs an rfc before being allowed inside constants
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+
| "constants accessing static items" needs an rfc before being allowed inside constants
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+
#![allow(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) // FIXME: this should 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) } // FIXME: this should error
25+
//~^ WARN skipping const checks
26+
};
27+
28+
static mut MUTABLE: u32 = 0;
29+
const BAD: u32 = unsafe { MUTABLE }; // FIXME: this should 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() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
warning: skipping const checks
2+
--> $DIR/const_refers_to_static.rs:11:18
3+
|
4+
LL | unsafe { &*(&FOO as *const _ as *const usize) }
5+
| ^^^
6+
7+
warning: skipping const checks
8+
--> $DIR/const_refers_to_static.rs:17:5
9+
|
10+
LL | FOO.fetch_add(1, Ordering::Relaxed) // FIXME: this should error
11+
| ^^^
12+
13+
warning: skipping const checks
14+
--> $DIR/const_refers_to_static.rs:17:5
15+
|
16+
LL | FOO.fetch_add(1, Ordering::Relaxed) // FIXME: this should error
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
warning: skipping const checks
20+
--> $DIR/const_refers_to_static.rs:24:17
21+
|
22+
LL | unsafe { *(&FOO as *const _ as *const usize) } // FIXME: this should error
23+
| ^^^
24+
25+
warning: skipping const checks
26+
--> $DIR/const_refers_to_static.rs:29:27
27+
|
28+
LL | const BAD: u32 = unsafe { MUTABLE }; // FIXME: this should error
29+
| ^^^^^^^
30+
31+
warning: skipping const checks
32+
--> $DIR/const_refers_to_static.rs:35:6
33+
|
34+
LL | &FOO
35+
| ^^^
36+
37+
error[E0080]: it is undefined behavior to use this value
38+
--> $DIR/const_refers_to_static.rs:9:1
39+
|
40+
LL | / const BOO: &usize = {
41+
LL | | static FOO: AtomicUsize = AtomicUsize::new(0);
42+
LL | | unsafe { &*(&FOO as *const _ as *const usize) }
43+
LL | |
44+
LL | | };
45+
| |__^ "constants accessing static items" needs an rfc before being allowed inside constants
46+
|
47+
= 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.
48+
49+
error[E0080]: it is undefined behavior to use this value
50+
--> $DIR/const_refers_to_static.rs:33:1
51+
|
52+
LL | / const BOO_OK: &usize = {
53+
LL | | static FOO: usize = 0;
54+
LL | | &FOO
55+
LL | |
56+
LL | | };
57+
| |__^ "constants accessing static items" needs an rfc before being allowed inside constants
58+
|
59+
= 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.
60+
61+
error: aborting due to 2 previous errors
62+
63+
For more information about this error, try `rustc --explain E0080`.

src/test/ui/issues/issue-52060.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
static A: &'static [u32] = &[1];
44
static B: [u32; 1] = [0; A.len()];
55
//~^ ERROR [E0013]
6+
//~| ERROR evaluation of constant value failed
67

78
fn main() {}

src/test/ui/issues/issue-52060.stderr

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ error[E0013]: constants cannot refer to statics, use a constant instead
44
LL | static B: [u32; 1] = [0; A.len()];
55
| ^
66

7-
error: aborting due to previous error
7+
error[E0080]: evaluation of constant value failed
8+
--> $DIR/issue-52060.rs:4:26
9+
|
10+
LL | static B: [u32; 1] = [0; A.len()];
11+
| ^ "constants accessing static items" needs an rfc before being allowed inside constants
12+
13+
error: aborting due to 2 previous errors
814

9-
For more information about this error, try `rustc --explain E0013`.
15+
Some errors have detailed explanations: E0013, E0080.
16+
For more information about an error, try `rustc --explain E0013`.

0 commit comments

Comments
 (0)