Skip to content

Commit 352f3f3

Browse files
Auto merge of #149973 - ShoyuVanilla:static-ref-cycle, r=<try>
Prohibit cycles behind references while static initialization
2 parents 3f4dc1e + 1f232e5 commit 352f3f3

18 files changed

+295
-31
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,36 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
856856
interp_ok(())
857857
}
858858

859+
fn before_static_ref_eval(
860+
tcx: TyCtxtAt<'tcx>,
861+
machine: &Self,
862+
ptr: Pointer<Self::Provenance>,
863+
static_def_id: DefId,
864+
) -> InterpResult<'tcx> {
865+
// We are only interested in immediate references to statics here.
866+
// Indirect references should not be checked because:
867+
// - they may be references to some other legitimate static reference
868+
// (e.g. via a raw pointer), and
869+
// - if they originate from an illegal static reference, that illegal
870+
// reference must already appear in the body and will be checked there.
871+
if ptr.provenance.shared_ref() {
872+
return interp_ok(());
873+
}
874+
875+
// Check if this is the currently evaluated static.
876+
if Some(static_def_id) == machine.static_root_ids.map(|(_, def_id)| def_id.into()) {
877+
return Err(ConstEvalErrKind::RecursiveStatic).into();
878+
}
879+
880+
if !tcx.is_foreign_item(static_def_id) {
881+
// Fire the query to detect cycles. We cannot restrict this to only when
882+
// evaluating statics, since static reference cycles can also be formed
883+
// through consts, especially promoted ones.
884+
tcx.eval_static_initializer(static_def_id)?;
885+
}
886+
interp_ok(())
887+
}
888+
859889
fn cached_union_data_range<'e>(
860890
ecx: &'e mut InterpCx<'tcx, Self>,
861891
ty: Ty<'tcx>,

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,20 @@ pub trait Machine<'tcx>: Sized {
447447
interp_ok(())
448448
}
449449

450+
/// Hook for performing extra checks on any reference to static.
451+
/// This is not invoked for raw pointers such as `unsafe { &*&raw const STATIC }`.
452+
///
453+
/// Used to prevent statics from constructing uninhabitated values by referencing themselves
454+
/// as it is being initialized.
455+
fn before_static_ref_eval(
456+
_tcx: TyCtxtAt<'tcx>,
457+
_machine: &Self,
458+
_ptr: Pointer<Self::Provenance>,
459+
_static_def_id: DefId,
460+
) -> InterpResult<'tcx> {
461+
interp_ok(())
462+
}
463+
450464
/// Hook for performing extra checks on a memory write access.
451465
/// This is not invoked for ZST accesses, as no write actually happens.
452466
/// `ptr` will always be a pointer with the provenance in `prov` pointing to the beginning of

compiler/rustc_const_eval/src/interpret/step.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
//! The main entry point is the `step` method.
44
55
use std::iter;
6+
use std::ops::Deref;
67

78
use either::Either;
89
use rustc_abi::{FIRST_VARIANT, FieldIdx};
910
use rustc_data_structures::fx::FxHashSet;
1011
use rustc_index::IndexSlice;
12+
use rustc_middle::mir::interpret::{GlobalAlloc, Provenance, Scalar};
1113
use rustc_middle::ty::{self, Instance, Ty};
1214
use rustc_middle::{bug, mir, span_bug};
1315
use rustc_span::source_map::Spanned;
@@ -217,9 +219,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
217219
}
218220

219221
Ref(_, borrow_kind, place) => {
222+
let is_reborrow_of_ref = if let Some(local) = place.local_or_deref_local()
223+
&& place.is_indirect()
224+
{
225+
self.layout_of_local(self.frame(), local, None)?.ty.is_ref()
226+
} else {
227+
false
228+
};
229+
220230
let src = self.eval_place(place)?;
221231
let place = self.force_allocation(&src)?;
222232
let val = ImmTy::from_immediate(place.to_ref(self), dest.layout);
233+
234+
// Check whether this forms a cycle involving a static reference.
235+
// Ensure the place is not a reborrow from a raw pointer, since we do not want to
236+
// forbid static reference cycles that go through raw pointers.
237+
if is_reborrow_of_ref
238+
&& let Immediate::Scalar(Scalar::Ptr(ptr, _)) = val.deref()
239+
&& let Some(alloc_id) = ptr.provenance.get_alloc_id()
240+
&& let Some(GlobalAlloc::Static(def_id)) =
241+
self.tcx.try_get_global_alloc(alloc_id)
242+
{
243+
M::before_static_ref_eval(self.tcx, &self.machine, *ptr, def_id)?;
244+
}
245+
223246
// A fresh reference was created, make sure it gets retagged.
224247
let val = M::retag_ptr_value(
225248
self,
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
//@ check-pass
2-
31
struct S(pub &'static u32, pub u32);
42

53
const fn g(ss: &S) -> &u32 { &ss.1 }
64

75
static T: S = S(g(&T), 0);
6+
//~^ ERROR: encountered static that tried to access itself during initialization
87

98
fn main () { }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0080]: encountered static that tried to access itself during initialization
2+
--> $DIR/issue-47971.rs:5:19
3+
|
4+
LL | static T: S = S(g(&T), 0);
5+
| ^^ evaluation of `T` failed here
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0080`.
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
//@ check-pass
2-
31
struct Value {
42
values: &'static [&'static Value],
53
}
64

75
// This `static` recursively points to itself through a promoted (the slice).
86
static VALUE: Value = Value {
97
values: &[&VALUE],
8+
//~^ ERROR: cycle detected when evaluating initializer of static `VALUE`
109
};
1110

1211
fn main() {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0391]: cycle detected when evaluating initializer of static `VALUE`
2+
--> $DIR/cycle-static-promoted.rs:7:13
3+
|
4+
LL | values: &[&VALUE],
5+
| ^^^^^^^^^
6+
|
7+
note: ...which requires simplifying constant for the type system `VALUE::promoted[0]`...
8+
--> $DIR/cycle-static-promoted.rs:6:1
9+
|
10+
LL | static VALUE: Value = Value {
11+
| ^^^^^^^^^^^^^^^^^^^
12+
note: ...which requires const-evaluating + checking `VALUE::promoted[0]`...
13+
--> $DIR/cycle-static-promoted.rs:6:1
14+
|
15+
LL | static VALUE: Value = Value {
16+
| ^^^^^^^^^^^^^^^^^^^
17+
= note: ...which again requires evaluating initializer of static `VALUE`, completing the cycle
18+
= note: cycle used when running analysis passes on crate `cycle_static_promoted`
19+
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
20+
21+
error: aborting due to 1 previous error
22+
23+
For more information about this error, try `rustc --explain E0391`.
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
//@ check-pass
2-
31
struct Foo {
42
foo: Option<&'static Foo>
53
}
64

75
static FOO: Foo = Foo {
86
foo: Some(&FOO),
7+
//~^ ERROR: encountered static that tried to access itself during initialization
98
};
109

1110
fn main() {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0080]: encountered static that tried to access itself during initialization
2+
--> $DIR/static-cycle-error.rs:6:15
3+
|
4+
LL | foo: Some(&FOO),
5+
| ^^^^ evaluation of `FOO` failed here
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0080`.

tests/ui/statics/recursive_interior_mut.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ unsafe impl Sync for EmptyChunkFooter {}
1313

1414
static EMPTY_CHUNK: EmptyChunkFooter = EmptyChunkFooter(ChunkFooter {
1515
prev: Cell::new(unsafe {
16-
NonNull::new_unchecked(&EMPTY_CHUNK as *const EmptyChunkFooter as *mut ChunkFooter)
16+
NonNull::new_unchecked(&raw const EMPTY_CHUNK as *mut ChunkFooter)
1717
}),
1818
});
1919

0 commit comments

Comments
 (0)