Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 3c6f4c1

Browse files
committed
Bless tagged pointers (comply to strict provenance)
1 parent f028636 commit 3c6f4c1

File tree

5 files changed

+85
-67
lines changed

5 files changed

+85
-67
lines changed

compiler/rustc_data_structures/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#![feature(get_mut_unchecked)]
3030
#![feature(lint_reasons)]
3131
#![feature(unwrap_infallible)]
32+
#![feature(strict_provenance)]
3233
#![allow(rustc::default_hash_types)]
3334
#![allow(rustc::potential_query_instability)]
3435
#![deny(rustc::untranslatable_diagnostic)]

compiler/rustc_data_structures/src/tagged_ptr.rs

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
1616
use std::mem::{self, ManuallyDrop};
1717
use std::ops::Deref;
18+
use std::ptr::NonNull;
1819
use std::rc::Rc;
1920
use std::sync::Arc;
2021

@@ -29,21 +30,24 @@ pub use drop::TaggedPtr;
2930
///
3031
/// # Safety
3132
///
32-
/// The usize returned from `into_usize` must be a valid, dereferenceable,
33-
/// pointer to [`<Self as Deref>::Target`]. Note that pointers to
34-
/// [`Self::Target`] must be thin, even though [`Self::Target`] may not be
35-
/// `Sized`.
33+
/// The pointer returned from [`into_ptr`] must be a [valid], pointer to
34+
/// [`<Self as Deref>::Target`]. Note that pointers to [`Self::Target`] must be
35+
/// thin, even though [`Self::Target`] may not be `Sized`.
3636
///
37-
/// Note that the returned pointer from `into_usize` should be castable to `&mut
38-
/// <Self as Deref>::Target` if `Self: DerefMut`.
37+
/// Note that if `Self` implements [`DerefMut`] the pointer returned from
38+
/// [`into_ptr`] must be valid for writes (and thus calling [`NonNull::as_mut`]
39+
/// on it must be safe).
3940
///
40-
/// The BITS constant must be correct. At least `BITS` bits, least-significant,
41-
/// must be zero on all returned pointers from `into_usize`.
41+
/// The `BITS` constant must be correct. At least `BITS` bits, least-significant,
42+
/// must be zero on all pointers returned from [`into_ptr`].
4243
///
4344
/// For example, if the alignment of [`Self::Target`] is 2, then `BITS` should be 1.
4445
///
46+
/// [`into_ptr`]: Pointer::into_ptr
47+
/// [valid]: std::ptr#safety
4548
/// [`<Self as Deref>::Target`]: Deref::Target
4649
/// [`Self::Target`]: Deref::Target
50+
/// [`DerefMut`]: std::ops::DerefMut
4751
pub unsafe trait Pointer: Deref {
4852
/// Number of unused (always zero) **least significant bits** in this
4953
/// pointer, usually related to the pointees alignment.
@@ -63,15 +67,15 @@ pub unsafe trait Pointer: Deref {
6367
/// [`Self::Target`]: Deref::Target
6468
const BITS: usize;
6569

66-
fn into_usize(self) -> usize;
70+
fn into_ptr(self) -> NonNull<Self::Target>;
6771

6872
/// # Safety
6973
///
7074
/// The passed `ptr` must be returned from `into_usize`.
7175
///
7276
/// This acts as `ptr::read` semantically, it should not be called more than
7377
/// once on non-`Copy` `Pointer`s.
74-
unsafe fn from_usize(ptr: usize) -> Self;
78+
unsafe fn from_ptr(ptr: NonNull<Self::Target>) -> Self;
7579

7680
/// This provides a reference to the `Pointer` itself, rather than the
7781
/// `Deref::Target`. It is used for cases where we want to call methods that
@@ -81,7 +85,7 @@ pub unsafe trait Pointer: Deref {
8185
/// # Safety
8286
///
8387
/// The passed `ptr` must be returned from `into_usize`.
84-
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R;
88+
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: NonNull<Self::Target>, f: F) -> R;
8589
}
8690

8791
/// This describes tags that the `TaggedPtr` struct can hold.
@@ -106,17 +110,18 @@ unsafe impl<T> Pointer for Box<T> {
106110
const BITS: usize = bits_for::<Self::Target>();
107111

108112
#[inline]
109-
fn into_usize(self) -> usize {
110-
Box::into_raw(self) as usize
113+
fn into_ptr(self) -> NonNull<T> {
114+
// Safety: pointers from `Box::into_raw` are valid & non-null
115+
unsafe { NonNull::new_unchecked(Box::into_raw(self)) }
111116
}
112117

113118
#[inline]
114-
unsafe fn from_usize(ptr: usize) -> Self {
115-
Box::from_raw(ptr as *mut T)
119+
unsafe fn from_ptr(ptr: NonNull<T>) -> Self {
120+
Box::from_raw(ptr.as_ptr())
116121
}
117122

118-
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R {
119-
let raw = ManuallyDrop::new(Self::from_usize(ptr));
123+
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: NonNull<T>, f: F) -> R {
124+
let raw = ManuallyDrop::new(Self::from_ptr(ptr));
120125
f(&raw)
121126
}
122127
}
@@ -125,17 +130,17 @@ unsafe impl<T> Pointer for Rc<T> {
125130
const BITS: usize = bits_for::<Self::Target>();
126131

127132
#[inline]
128-
fn into_usize(self) -> usize {
129-
Rc::into_raw(self) as usize
133+
fn into_ptr(self) -> NonNull<T> {
134+
unsafe { NonNull::new_unchecked(Rc::into_raw(self).cast_mut()) }
130135
}
131136

132137
#[inline]
133-
unsafe fn from_usize(ptr: usize) -> Self {
134-
Rc::from_raw(ptr as *const T)
138+
unsafe fn from_ptr(ptr: NonNull<T>) -> Self {
139+
Rc::from_raw(ptr.as_ptr())
135140
}
136141

137-
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R {
138-
let raw = ManuallyDrop::new(Self::from_usize(ptr));
142+
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: NonNull<T>, f: F) -> R {
143+
let raw = ManuallyDrop::new(Self::from_ptr(ptr));
139144
f(&raw)
140145
}
141146
}
@@ -144,17 +149,17 @@ unsafe impl<T> Pointer for Arc<T> {
144149
const BITS: usize = bits_for::<Self::Target>();
145150

146151
#[inline]
147-
fn into_usize(self) -> usize {
148-
Arc::into_raw(self) as usize
152+
fn into_ptr(self) -> NonNull<T> {
153+
unsafe { NonNull::new_unchecked(Arc::into_raw(self).cast_mut()) }
149154
}
150155

151156
#[inline]
152-
unsafe fn from_usize(ptr: usize) -> Self {
153-
Arc::from_raw(ptr as *const T)
157+
unsafe fn from_ptr(ptr: NonNull<T>) -> Self {
158+
Arc::from_raw(ptr.as_ptr())
154159
}
155160

156-
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R {
157-
let raw = ManuallyDrop::new(Self::from_usize(ptr));
161+
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: NonNull<T>, f: F) -> R {
162+
let raw = ManuallyDrop::new(Self::from_ptr(ptr));
158163
f(&raw)
159164
}
160165
}
@@ -163,32 +168,35 @@ unsafe impl<'a, T: 'a> Pointer for &'a T {
163168
const BITS: usize = bits_for::<Self::Target>();
164169

165170
#[inline]
166-
fn into_usize(self) -> usize {
167-
self as *const T as usize
171+
fn into_ptr(self) -> NonNull<T> {
172+
NonNull::from(self)
168173
}
169174

170175
#[inline]
171-
unsafe fn from_usize(ptr: usize) -> Self {
172-
&*(ptr as *const T)
176+
unsafe fn from_ptr(ptr: NonNull<T>) -> Self {
177+
ptr.as_ref()
173178
}
174179

175-
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R {
176-
f(&*(&ptr as *const usize as *const Self))
180+
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: NonNull<T>, f: F) -> R {
181+
f(&ptr.as_ref())
177182
}
178183
}
179184

180185
unsafe impl<'a, T: 'a> Pointer for &'a mut T {
181186
const BITS: usize = bits_for::<Self::Target>();
187+
182188
#[inline]
183-
fn into_usize(self) -> usize {
184-
self as *mut T as usize
189+
fn into_ptr(self) -> NonNull<T> {
190+
NonNull::from(self)
185191
}
192+
186193
#[inline]
187-
unsafe fn from_usize(ptr: usize) -> Self {
188-
&mut *(ptr as *mut T)
194+
unsafe fn from_ptr(mut ptr: NonNull<T>) -> Self {
195+
ptr.as_mut()
189196
}
190-
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R {
191-
f(&*(&ptr as *const usize as *const Self))
197+
198+
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(mut ptr: NonNull<T>, f: F) -> R {
199+
f(&ptr.as_mut())
192200
}
193201
}
194202

compiler/rustc_data_structures/src/tagged_ptr/copy.rs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::fmt;
44
use std::marker::PhantomData;
55
use std::num::NonZeroUsize;
66
use std::ops::{Deref, DerefMut};
7+
use std::ptr::NonNull;
78

89
/// A `Copy` TaggedPtr.
910
///
@@ -18,7 +19,7 @@ where
1819
P: Pointer,
1920
T: Tag,
2021
{
21-
packed: NonZeroUsize,
22+
packed: NonNull<P::Target>,
2223
data: PhantomData<(P, T)>,
2324
}
2425

@@ -53,26 +54,36 @@ where
5354
const ASSERTION: () = {
5455
assert!(T::BITS <= P::BITS);
5556
// Used for the transmute_copy's below
57+
// TODO(waffle): do we need this assert anymore?
5658
assert!(std::mem::size_of::<&P::Target>() == std::mem::size_of::<usize>());
5759
};
5860

5961
pub fn new(pointer: P, tag: T) -> Self {
6062
// Trigger assert!
6163
let () = Self::ASSERTION;
64+
6265
let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT;
6366

6467
Self {
65-
// SAFETY: We know that the pointer is non-null, as it must be
66-
// dereferenceable per `Pointer` safety contract.
67-
packed: unsafe {
68-
NonZeroUsize::new_unchecked((P::into_usize(pointer) >> T::BITS) | packed_tag)
69-
},
68+
packed: P::into_ptr(pointer).map_addr(|addr| {
69+
// SAFETY:
70+
// - The pointer is `NonNull` => it's address is `NonZeroUsize`
71+
// - `P::BITS` least significant bits are always zero (`Pointer` contract)
72+
// - `T::BITS <= P::BITS` (from `Self::ASSERTION`)
73+
//
74+
// Thus `addr >> T::BITS` is guaranteed to be non-zero.
75+
//
76+
// `{non_zero} | packed_tag` can't make the value zero.
77+
78+
let packed = (addr.get() >> T::BITS) | packed_tag;
79+
unsafe { NonZeroUsize::new_unchecked(packed) }
80+
}),
7081
data: PhantomData,
7182
}
7283
}
7384

74-
pub(super) fn pointer_raw(&self) -> usize {
75-
self.packed.get() << T::BITS
85+
pub(super) fn pointer_raw(&self) -> NonNull<P::Target> {
86+
self.packed.map_addr(|addr| unsafe { NonZeroUsize::new_unchecked(addr.get() << T::BITS) })
7687
}
7788

7889
pub fn pointer(self) -> P
@@ -83,35 +94,35 @@ where
8394
//
8495
// Note that this isn't going to double-drop or anything because we have
8596
// P: Copy
86-
unsafe { P::from_usize(self.pointer_raw()) }
97+
unsafe { P::from_ptr(self.pointer_raw()) }
8798
}
8899

89100
pub fn pointer_ref(&self) -> &P::Target {
90101
// SAFETY: pointer_raw returns the original pointer
91-
unsafe { std::mem::transmute_copy(&self.pointer_raw()) }
102+
unsafe { self.pointer_raw().as_ref() }
92103
}
93104

94105
pub fn pointer_mut(&mut self) -> &mut P::Target
95106
where
96107
P: DerefMut,
97108
{
98109
// SAFETY: pointer_raw returns the original pointer
99-
unsafe { std::mem::transmute_copy(&self.pointer_raw()) }
110+
unsafe { self.pointer_raw().as_mut() }
100111
}
101112

102113
#[inline]
103114
pub fn tag(&self) -> T {
104-
unsafe { T::from_usize(self.packed.get() >> Self::TAG_BIT_SHIFT) }
115+
unsafe { T::from_usize(self.packed.addr().get() >> Self::TAG_BIT_SHIFT) }
105116
}
106117

107118
#[inline]
108119
pub fn set_tag(&mut self, tag: T) {
109-
let mut packed = self.packed.get();
120+
// TODO: refactor packing into a function and reuse it here
110121
let new_tag = T::into_usize(tag) << Self::TAG_BIT_SHIFT;
111122
let tag_mask = (1 << T::BITS) - 1;
112-
packed &= !(tag_mask << Self::TAG_BIT_SHIFT);
113-
packed |= new_tag;
114-
self.packed = unsafe { NonZeroUsize::new_unchecked(packed) };
123+
self.packed = self.packed.map_addr(|addr| unsafe {
124+
NonZeroUsize::new_unchecked(addr.get() & !(tag_mask << Self::TAG_BIT_SHIFT) | new_tag)
125+
});
115126
}
116127
}
117128

compiler/rustc_data_structures/src/tagged_ptr/drop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ where
7676
fn drop(&mut self) {
7777
// No need to drop the tag, as it's Copy
7878
unsafe {
79-
drop(P::from_usize(self.raw.pointer_raw()));
79+
drop(P::from_ptr(self.raw.pointer_raw()));
8080
}
8181
}
8282
}

compiler/rustc_middle/src/ty/list.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::hash::{Hash, Hasher};
88
use std::iter;
99
use std::mem;
1010
use std::ops::Deref;
11-
use std::ptr;
11+
use std::ptr::{self, NonNull};
1212
use std::slice;
1313

1414
/// `List<T>` is a bit like `&[T]`, but with some critical differences.
@@ -203,18 +203,16 @@ unsafe impl<'a, T: 'a> rustc_data_structures::tagged_ptr::Pointer for &'a List<T
203203
const BITS: usize = bits_for::<usize>();
204204

205205
#[inline]
206-
fn into_usize(self) -> usize {
207-
self as *const List<T> as usize
206+
fn into_ptr(self) -> NonNull<List<T>> {
207+
NonNull::from(self)
208208
}
209209

210210
#[inline]
211-
unsafe fn from_usize(ptr: usize) -> &'a List<T> {
212-
&*(ptr as *const List<T>)
211+
unsafe fn from_ptr(ptr: NonNull<List<T>>) -> &'a List<T> {
212+
ptr.as_ref()
213213
}
214214

215-
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R {
216-
// `Self` is `&'a List<T>` which impls `Copy`, so this is fine.
217-
let ptr = Self::from_usize(ptr);
218-
f(&ptr)
215+
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: NonNull<List<T>>, f: F) -> R {
216+
f(&ptr.as_ref())
219217
}
220218
}

0 commit comments

Comments
 (0)