Skip to content

Commit 1b4b0e9

Browse files
authored
Rollup merge of #125834 - workingjubilee:weaken-thir-unsafeck-for-addr-of-static-mut, r=compiler-errors
treat `&raw (const|mut) UNSAFE_STATIC` implied deref as safe Fixes #125833 As reported in that and related issues, `static mut STATIC_MUT: T` is very often used in embedded code, and is in many ways equivalent to `static STATIC_CELL: SyncUnsafeCell<T>`. The Rust expression of `&raw mut STATIC_MUT` and `SyncUnsafeCell::get(&STATIC_CELL)` are approximately equal, and both evaluate to `*mut T`. The library function is safe because it has *declared itself* to be safe. However, the raw ref operator is unsafe because all uses of `static mut` are considered unsafe, even though the static's value is not used by this expression (unlike, for example, `&STATIC_MUT`). We can fix this unnatural difference by simply adding the proper exclusion for the safety check inside the THIR unsafeck, so that we do not declare it unsafe if it is not. While the primary concern here is `static mut`, this change is made for all instances of an "unsafe static", which includes a static declared inside `extern "abi" {}`. Hypothetically, we could go as far as generalizing this to all instances of `&raw (const|mut) *ptr`, but today we do not, as we have not actually considered the range of possible expressions that use a similar encoding. We do not even extend this to thread-local equivalents, because they have less clear semantics.
2 parents 49649bf + b3cd9b5 commit 1b4b0e9

File tree

13 files changed

+133
-13
lines changed

13 files changed

+133
-13
lines changed

compiler/rustc_mir_build/src/check_unsafety.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,24 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
466466
}
467467
}
468468
}
469+
ExprKind::AddressOf { arg, .. } => {
470+
if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
471+
// THIR desugars UNSAFE_STATIC into *UNSAFE_STATIC_REF, where
472+
// UNSAFE_STATIC_REF holds the addr of the UNSAFE_STATIC, so: take two steps
473+
&& let ExprKind::Deref { arg } = self.thir[arg].kind
474+
// FIXME(workingjubiee): we lack a clear reason to reject ThreadLocalRef here,
475+
// but we also have no conclusive reason to allow it either!
476+
&& let ExprKind::StaticRef { .. } = self.thir[arg].kind
477+
{
478+
// A raw ref to a place expr, even an "unsafe static", is okay!
479+
// We short-circuit to not recursively traverse this expression.
480+
return;
481+
// note: const_mut_refs enables this code, and it currently remains unsafe:
482+
// static mut BYTE: u8 = 0;
483+
// static mut BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(BYTE) };
484+
// static mut DEREF_BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(*BYTE_PTR) };
485+
}
486+
}
469487
ExprKind::Deref { arg } => {
470488
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
471489
self.thir[arg].kind

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -939,9 +939,11 @@ impl<'tcx> Cx<'tcx> {
939939
}
940940
}
941941

942-
// We encode uses of statics as a `*&STATIC` where the `&STATIC` part is
943-
// a constant reference (or constant raw pointer for `static mut`) in MIR
942+
// A source Rust `path::to::STATIC` is a place expr like *&ident is.
943+
// In THIR, we make them exactly equivalent by inserting the implied *& or *&raw,
944+
// but distinguish between &STATIC and &THREAD_LOCAL as they have different semantics
944945
Res::Def(DefKind::Static { .. }, id) => {
946+
// this is &raw for extern static or static mut, and & for other statics
945947
let ty = self.tcx.static_ptr_ty(id);
946948
let temp_lifetime = self
947949
.rvalue_scopes

library/panic_unwind/src/seh.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,10 @@ mod imp {
157157
// going to be cross-lang LTOed anyway. However, using expose is shorter and
158158
// requires less unsafe.
159159
let addr: usize = ptr.expose_provenance();
160+
#[cfg(bootstrap)]
160161
let image_base = unsafe { addr_of!(__ImageBase) }.addr();
162+
#[cfg(not(bootstrap))]
163+
let image_base = addr_of!(__ImageBase).addr();
161164
let offset: usize = addr - image_base;
162165
Self(offset as u32)
163166
}
@@ -250,7 +253,10 @@ extern "C" {
250253
// This is fine since the MSVC runtime uses string comparison on the type name
251254
// to match TypeDescriptors rather than pointer equality.
252255
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
256+
#[cfg(bootstrap)]
253257
pVFTable: unsafe { addr_of!(TYPE_INFO_VTABLE) } as *const _,
258+
#[cfg(not(bootstrap))]
259+
pVFTable: addr_of!(TYPE_INFO_VTABLE) as *const _,
254260
spare: core::ptr::null_mut(),
255261
name: TYPE_NAME,
256262
};

src/tools/miri/tests/fail/extern_static.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ extern "C" {
55
}
66

77
fn main() {
8-
let _val = unsafe { std::ptr::addr_of!(FOO) }; //~ ERROR: is not supported by Miri
8+
let _val = std::ptr::addr_of!(FOO); //~ ERROR: is not supported by Miri
99
}

src/tools/miri/tests/fail/extern_static.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: unsupported operation: extern static `FOO` is not supported by Miri
22
--> $DIR/extern_static.rs:LL:CC
33
|
4-
LL | let _val = unsafe { std::ptr::addr_of!(FOO) };
5-
| ^^^ extern static `FOO` is not supported by Miri
4+
LL | let _val = std::ptr::addr_of!(FOO);
5+
| ^^^ extern static `FOO` is not supported by Miri
66
|
77
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
88
= note: BACKTRACE:

src/tools/miri/tests/pass/static_mut.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::ptr::addr_of;
22

33
static mut FOO: i32 = 42;
44

5-
static BAR: Foo = Foo(unsafe { addr_of!(FOO) });
5+
static BAR: Foo = Foo(addr_of!(FOO));
66

77
#[allow(dead_code)]
88
struct Foo(*const i32);

tests/ui/consts/const_refs_to_static.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const C1: &i32 = &S;
99
const C1_READ: () = {
1010
assert!(*C1 == 0);
1111
};
12-
const C2: *const i32 = unsafe { std::ptr::addr_of!(S_MUT) };
12+
const C2: *const i32 = std::ptr::addr_of!(S_MUT);
1313

1414
fn main() {
1515
assert_eq!(*C1, 0);

tests/ui/consts/mut-ptr-to-static.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@ static mut STATIC: u32 = 42;
1616
static INTERIOR_MUTABLE_STATIC: SyncUnsafeCell<u32> = SyncUnsafeCell::new(42);
1717

1818
// A static that mutably points to STATIC.
19-
static PTR: SyncPtr = SyncPtr {
20-
foo: unsafe { ptr::addr_of_mut!(STATIC) },
21-
};
22-
static INTERIOR_MUTABLE_PTR: SyncPtr = SyncPtr {
23-
foo: ptr::addr_of!(INTERIOR_MUTABLE_STATIC) as *mut u32,
24-
};
19+
static PTR: SyncPtr = SyncPtr { foo: ptr::addr_of_mut!(STATIC) };
20+
static INTERIOR_MUTABLE_PTR: SyncPtr =
21+
SyncPtr { foo: ptr::addr_of!(INTERIOR_MUTABLE_STATIC) as *mut u32 };
2522

2623
fn main() {
2724
let ptr = PTR.foo;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ check-pass
2+
#![feature(const_mut_refs)]
3+
use std::ptr;
4+
5+
// This code should remain unsafe because of the two unsafe operations here,
6+
// even if in a hypothetical future we deem all &raw (const|mut) *ptr exprs safe.
7+
8+
static mut BYTE: u8 = 0;
9+
static mut BYTE_PTR: *mut u8 = ptr::addr_of_mut!(BYTE);
10+
// An unsafe static's ident is a place expression in its own right, so despite the above being safe
11+
// (it's fine to create raw refs to places!) the following derefs the ptr before creating its ref
12+
static mut DEREF_BYTE_PTR: *mut u8 = unsafe { ptr::addr_of_mut!(*BYTE_PTR) };
13+
14+
fn main() {
15+
let _ = unsafe { DEREF_BYTE_PTR };
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(const_mut_refs)]
2+
3+
use std::ptr;
4+
5+
// This code should remain unsafe because of the two unsafe operations here,
6+
// even if in a hypothetical future we deem all &raw (const|mut) *ptr exprs safe.
7+
8+
static mut BYTE: u8 = 0;
9+
static mut BYTE_PTR: *mut u8 = ptr::addr_of_mut!(BYTE);
10+
// An unsafe static's ident is a place expression in its own right, so despite the above being safe
11+
// (it's fine to create raw refs to places!) the following derefs the ptr before creating its ref!
12+
static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);
13+
//~^ ERROR: use of mutable static
14+
//~| ERROR: dereference of raw pointer
15+
16+
fn main() {
17+
let _ = unsafe { DEREF_BYTE_PTR };
18+
}

0 commit comments

Comments
 (0)