Skip to content

Commit 083f1d7

Browse files
committed
Validate constants during const_eval_raw
1 parent ac19c3b commit 083f1d7

File tree

4 files changed

+62
-52
lines changed

4 files changed

+62
-52
lines changed

compiler/rustc_mir/src/const_eval/eval_queries.rs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -193,21 +193,7 @@ fn validate_and_turn_into_const<'tcx>(
193193
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
194194
let val = (|| {
195195
let mplace = ecx.raw_const_to_mplace(constant)?;
196-
197-
// FIXME do not validate promoteds until a decision on
198-
// https://github.com/rust-lang/rust/issues/67465 is made
199-
if cid.promoted.is_none() {
200-
let mut ref_tracking = RefTracking::new(mplace);
201-
while let Some((mplace, path)) = ref_tracking.todo.pop() {
202-
ecx.const_validate_operand(
203-
mplace.into(),
204-
path,
205-
&mut ref_tracking,
206-
/*may_ref_to_static*/ ecx.memory.extra.can_access_statics,
207-
)?;
208-
}
209-
}
210-
// Now that we validated, turn this into a proper constant.
196+
// Turn this into a proper constant.
211197
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
212198
// whether they become immediates.
213199
if is_static || cid.promoted.is_some() {
@@ -221,6 +207,7 @@ fn validate_and_turn_into_const<'tcx>(
221207
}
222208
})();
223209

210+
// FIXME: Can this ever be an error and not be a compiler bug or can we just ICE here?
224211
val.map_err(|error| {
225212
let err = ConstEvalErr::new(&ecx, error, None);
226213
err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| {
@@ -319,7 +306,6 @@ pub fn const_eval_raw_provider<'tcx>(
319306

320307
let res = ecx.load_mir(cid.instance.def, cid.promoted);
321308
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body))
322-
.map(|place| RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty })
323309
.map_err(|error| {
324310
let err = ConstEvalErr::new(&ecx, error, None);
325311
// errors in statics are always emitted as fatal errors
@@ -397,4 +383,37 @@ pub fn const_eval_raw_provider<'tcx>(
397383
err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant")
398384
}
399385
})
386+
.and_then(|mplace| {
387+
// Since evaluation had no errors, valiate the resulting constant:
388+
let validation = try {
389+
// FIXME do not validate promoteds until a decision on
390+
// https://github.com/rust-lang/rust/issues/67465 is made
391+
if cid.promoted.is_none() {
392+
let mut ref_tracking = RefTracking::new(mplace);
393+
while let Some((mplace, path)) = ref_tracking.todo.pop() {
394+
ecx.const_validate_operand(
395+
mplace.into(),
396+
path,
397+
&mut ref_tracking,
398+
/*may_ref_to_static*/ ecx.memory.extra.can_access_statics,
399+
)?;
400+
}
401+
}
402+
};
403+
if let Err(error) = validation {
404+
// Validation failed, report an error
405+
let err = ConstEvalErr::new(&ecx, error, None);
406+
Err(err.struct_error(
407+
ecx.tcx,
408+
"it is undefined behavior to use this value",
409+
|mut diag| {
410+
diag.note(note_on_undefined_behavior_error());
411+
diag.emit();
412+
},
413+
))
414+
} else {
415+
// Convert to raw constant
416+
Ok(RawConst { alloc_id: mplace.ptr.assert_ptr().alloc_id, ty: mplace.layout.ty })
417+
}
418+
})
400419
}

compiler/rustc_mir/src/interpret/validity.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -425,26 +425,28 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
425425
let alloc_kind = self.ecx.tcx.get_global_alloc(ptr.alloc_id);
426426
if let Some(GlobalAlloc::Static(did)) = alloc_kind {
427427
assert!(!self.ecx.tcx.is_thread_local_static(did));
428+
assert!(self.ecx.tcx.is_static(did));
428429
// See const_eval::machine::MemoryExtra::can_access_statics for why
429430
// this check is so important.
430431
// This check is reachable when the const just referenced the static,
431432
// but never read it (so we never entered `before_access_global`).
432433
// We also need to do it here instead of going on to avoid running
433434
// into the `before_access_global` check during validation.
434-
if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
435+
if !self.may_ref_to_static {
435436
throw_validation_failure!(self.path,
436437
{ "a {} pointing to a static variable", kind }
437438
);
438439
}
439-
// `extern static` cannot be validated as they have no body.
440-
// FIXME: Statics from other crates are also skipped.
441-
// They might be checked at a different type, but for now we
442-
// want to avoid recursing too deeply. We might miss const-invalid data,
440+
// We skip checking other statics. These statics must be sound by themselves,
441+
// and the only way to get broken statics here is by using unsafe code.
442+
// The reasons we don't check other statics is twofold. For one, in all sound
443+
// cases, the static was already validated on its own, and second, we trigger
444+
// cycle errors if we try to compute the value of the other static and that
445+
// static refers back to us.
446+
// We might miss const-invalid data,
443447
// but things are still sound otherwise (in particular re: consts
444448
// referring to statics).
445-
if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
446-
return Ok(());
447-
}
449+
return Ok(());
448450
}
449451
}
450452
// Proceed recursively even for ZST, no reason to skip them!

src/test/ui/consts/const-eval/double_check2.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
// check-pass
2+
3+
// This test exhibits undefined behavior, but it is impossible to prevent generally during
4+
// const eval, even if possible to prevent in the cases here. The reason it's impossible in general
5+
// is that we run into query cycles even *without* UB, just because we're checking for UB.
6+
// We do not detect it if you create references to statics
7+
// in ways that are UB.
8+
19
enum Foo {
210
A = 5,
311
B = 42,
@@ -13,11 +21,14 @@ union Union {
1321
u8: &'static u8,
1422
}
1523
static BAR: u8 = 5;
16-
static FOO: (&Foo, &Bar) = unsafe {( //~ undefined behavior
17-
Union { u8: &BAR }.foo,
18-
Union { u8: &BAR }.bar,
19-
)};
20-
static FOO2: (&Foo, &Bar) = unsafe {(std::mem::transmute(&BAR), std::mem::transmute(&BAR))};
21-
//~^ undefined behavior
24+
static FOO: (&Foo, &Bar) = unsafe {
25+
(
26+
// undefined behavior
27+
Union { u8: &BAR }.foo,
28+
Union { u8: &BAR }.bar,
29+
)
30+
};
31+
static FOO2: (&Foo, &Bar) = unsafe { (std::mem::transmute(&BAR), std::mem::transmute(&BAR)) };
32+
//^ undefined behavior
2233

2334
fn main() {}

src/test/ui/consts/const-eval/double_check2.stderr

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)