Skip to content

Commit c05b9a1

Browse files
committed
M
2 parents cfb6a1f + 8fe1cea commit c05b9a1

File tree

21 files changed

+490
-254
lines changed

21 files changed

+490
-254
lines changed

compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use std::borrow::Cow;
88
use std::fmt::Write;
99
use std::hash::Hash;
10+
use std::mem;
1011
use std::num::NonZero;
1112

1213
use either::{Left, Right};
@@ -288,6 +289,7 @@ struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> {
288289
/// If this is `Some`, then `reset_provenance_and_padding` must be true (but not vice versa:
289290
/// we might not track data vs padding bytes if the operand isn't stored in memory anyway).
290291
data_bytes: Option<RangeSet>,
292+
may_dangle: bool,
291293
}
292294

293295
impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
@@ -503,27 +505,29 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
503505
// alignment and size determined by the layout (size will be 0,
504506
// alignment should take attributes into account).
505507
.unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
506-
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
507-
try_validation!(
508-
self.ecx.check_ptr_access(
509-
place.ptr(),
510-
size,
511-
CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
512-
),
513-
self.path,
514-
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false },
515-
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
516-
ptr_kind,
517-
// FIXME this says "null pointer" when null but we need translate
518-
pointer: format!("{}", Pointer::<Option<AllocId>>::without_provenance(i))
519-
},
520-
Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
521-
ptr_kind
522-
},
523-
Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree {
524-
ptr_kind,
525-
},
526-
);
508+
if !self.may_dangle {
509+
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
510+
try_validation!(
511+
self.ecx.check_ptr_access(
512+
place.ptr(),
513+
size,
514+
CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
515+
),
516+
self.path,
517+
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false },
518+
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
519+
ptr_kind,
520+
// FIXME this says "null pointer" when null but we need translate
521+
pointer: format!("{}", Pointer::<Option<AllocId>>::without_provenance(i))
522+
},
523+
Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
524+
ptr_kind
525+
},
526+
Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree {
527+
ptr_kind,
528+
},
529+
);
530+
}
527531
try_validation!(
528532
self.ecx.check_ptr_align(
529533
place.ptr(),
@@ -536,6 +540,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
536540
found_bytes: has.bytes()
537541
},
538542
);
543+
539544
// Make sure this is non-null. We checked dereferenceability above, but if `size` is zero
540545
// that does not imply non-null.
541546
let scalar = Scalar::from_maybe_pointer(place.ptr(), self.ecx);
@@ -1269,6 +1274,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
12691274
ty::PatternKind::Or(_patterns) => {}
12701275
}
12711276
}
1277+
ty::Adt(adt, _) if adt.is_maybe_dangling() => {
1278+
let could_dangle = mem::replace(&mut self.may_dangle, true);
1279+
1280+
let inner = self.ecx.project_field(val, FieldIdx::ZERO)?;
1281+
self.visit_value(&inner)?;
1282+
1283+
self.may_dangle = could_dangle;
1284+
}
12721285
_ => {
12731286
// default handler
12741287
try_validation!(
@@ -1354,6 +1367,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
13541367
ecx,
13551368
reset_provenance_and_padding,
13561369
data_bytes: reset_padding.then_some(RangeSet(Vec::new())),
1370+
may_dangle: false,
13571371
};
13581372
v.visit_value(val)?;
13591373
v.reset_padding(val)?;

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ language_item_table! {
353353

354354
PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
355355

356-
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
356+
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::Exact(1);
357+
MaybeDangling, sym::maybe_dangling, maybe_dangling, Target::Struct, GenericRequirement::Exact(1);
357358
BikeshedGuaranteedNoDrop, sym::bikeshed_guaranteed_no_drop, bikeshed_guaranteed_no_drop, Target::Trait, GenericRequirement::Exact(0);
358359

359360
MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;

compiler/rustc_middle/src/ty/adt.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ bitflags::bitflags! {
5959
const IS_PIN = 1 << 11;
6060
/// Indicates whether the type is `#[pin_project]`.
6161
const IS_PIN_PROJECT = 1 << 12;
62+
/// Indicates whether the type is `MaybeDangling<_>`.
63+
const IS_MAYBE_DANGLING = 1 << 13;
6264
}
6365
}
6466
rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -315,6 +317,9 @@ impl AdtDefData {
315317
if tcx.is_lang_item(did, LangItem::ManuallyDrop) {
316318
flags |= AdtFlags::IS_MANUALLY_DROP;
317319
}
320+
if tcx.is_lang_item(did, LangItem::MaybeDangling) {
321+
flags |= AdtFlags::IS_MAYBE_DANGLING;
322+
}
318323
if tcx.is_lang_item(did, LangItem::UnsafeCell) {
319324
flags |= AdtFlags::IS_UNSAFE_CELL;
320325
}
@@ -439,6 +444,12 @@ impl<'tcx> AdtDef<'tcx> {
439444
self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
440445
}
441446

447+
/// Returns `true` if this is `MaybeDangling<T>`.
448+
#[inline]
449+
pub fn is_maybe_dangling(self) -> bool {
450+
self.flags().contains(AdtFlags::IS_MAYBE_DANGLING)
451+
}
452+
442453
/// Returns `true` if this is `Pin<T>`.
443454
#[inline]
444455
pub fn is_pin(self) -> bool {

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,12 @@ where
10531053
})
10541054
}
10551055

1056+
ty::Adt(adt_def, ..) if adt_def.is_maybe_dangling() => {
1057+
// FIXME: what is the exact effect of maybe dangling?
1058+
Self::ty_and_layout_pointee_info_at(this.field(cx, 0), cx, offset)
1059+
.map(|info| PointeeInfo { safe: None, ..info })
1060+
}
1061+
10561062
_ => {
10571063
let mut data_variant = match &this.variants {
10581064
// Within the discriminant field, only the niche itself is
@@ -1091,7 +1097,7 @@ where
10911097
}
10921098
}
10931099
Variants::Multiple { .. } => None,
1094-
_ => Some(this),
1100+
Variants::Empty | Variants::Single { .. } => Some(this),
10951101
};
10961102

10971103
if let Some(variant) = data_variant

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,7 @@ symbols! {
14031403
maxnumf128,
14041404
may_dangle,
14051405
may_unwind,
1406+
maybe_dangling,
14061407
maybe_uninit,
14071408
maybe_uninit_uninit,
14081409
maybe_uninit_zeroed,

library/core/src/mem/manually_drop.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::marker::Destruct;
1+
use crate::cmp::Ordering;
2+
use crate::hash::{Hash, Hasher};
3+
use crate::marker::{Destruct, StructuralPartialEq};
4+
use crate::mem::MaybeDangling;
25
use crate::ops::{Deref, DerefMut, DerefPure};
36
use crate::ptr;
47

@@ -152,11 +155,11 @@ use crate::ptr;
152155
/// [`MaybeUninit`]: crate::mem::MaybeUninit
153156
#[stable(feature = "manually_drop", since = "1.20.0")]
154157
#[lang = "manually_drop"]
155-
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
158+
#[derive(Copy, Clone, Debug, Default)]
156159
#[repr(transparent)]
157160
#[rustc_pub_transparent]
158161
pub struct ManuallyDrop<T: ?Sized> {
159-
value: T,
162+
value: MaybeDangling<T>,
160163
}
161164

162165
impl<T> ManuallyDrop<T> {
@@ -179,7 +182,7 @@ impl<T> ManuallyDrop<T> {
179182
#[rustc_const_stable(feature = "const_manually_drop", since = "1.32.0")]
180183
#[inline(always)]
181184
pub const fn new(value: T) -> ManuallyDrop<T> {
182-
ManuallyDrop { value }
185+
ManuallyDrop { value: MaybeDangling::new(value) }
183186
}
184187

185188
/// Extracts the value from the `ManuallyDrop` container.
@@ -197,7 +200,7 @@ impl<T> ManuallyDrop<T> {
197200
#[rustc_const_stable(feature = "const_manually_drop", since = "1.32.0")]
198201
#[inline(always)]
199202
pub const fn into_inner(slot: ManuallyDrop<T>) -> T {
200-
slot.value
203+
slot.value.into_inner()
201204
}
202205

203206
/// Takes the value from the `ManuallyDrop<T>` container out.
@@ -222,7 +225,7 @@ impl<T> ManuallyDrop<T> {
222225
pub const unsafe fn take(slot: &mut ManuallyDrop<T>) -> T {
223226
// SAFETY: we are reading from a reference, which is guaranteed
224227
// to be valid for reads.
225-
unsafe { ptr::read(&slot.value) }
228+
unsafe { ptr::read(slot.value.as_ref()) }
226229
}
227230
}
228231

@@ -259,7 +262,7 @@ impl<T: ?Sized> ManuallyDrop<T> {
259262
// SAFETY: we are dropping the value pointed to by a mutable reference
260263
// which is guaranteed to be valid for writes.
261264
// It is up to the caller to make sure that `slot` isn't dropped again.
262-
unsafe { ptr::drop_in_place(&mut slot.value) }
265+
unsafe { ptr::drop_in_place(slot.value.as_mut()) }
263266
}
264267
}
265268

@@ -269,7 +272,7 @@ impl<T: ?Sized> const Deref for ManuallyDrop<T> {
269272
type Target = T;
270273
#[inline(always)]
271274
fn deref(&self) -> &T {
272-
&self.value
275+
self.value.as_ref()
273276
}
274277
}
275278

@@ -278,9 +281,43 @@ impl<T: ?Sized> const Deref for ManuallyDrop<T> {
278281
impl<T: ?Sized> const DerefMut for ManuallyDrop<T> {
279282
#[inline(always)]
280283
fn deref_mut(&mut self) -> &mut T {
281-
&mut self.value
284+
self.value.as_mut()
282285
}
283286
}
284287

285288
#[unstable(feature = "deref_pure_trait", issue = "87121")]
286289
unsafe impl<T: ?Sized> DerefPure for ManuallyDrop<T> {}
290+
291+
#[stable(feature = "manually_drop", since = "1.20.0")]
292+
impl<T: ?Sized + Eq> Eq for ManuallyDrop<T> {}
293+
294+
#[stable(feature = "manually_drop", since = "1.20.0")]
295+
impl<T: ?Sized + PartialEq> PartialEq for ManuallyDrop<T> {
296+
fn eq(&self, other: &Self) -> bool {
297+
self.value.as_ref().eq(other.value.as_ref())
298+
}
299+
}
300+
301+
#[stable(feature = "manually_drop", since = "1.20.0")]
302+
impl<T: ?Sized> StructuralPartialEq for ManuallyDrop<T> {}
303+
304+
#[stable(feature = "manually_drop", since = "1.20.0")]
305+
impl<T: ?Sized + Ord> Ord for ManuallyDrop<T> {
306+
fn cmp(&self, other: &Self) -> Ordering {
307+
self.value.as_ref().cmp(other.value.as_ref())
308+
}
309+
}
310+
311+
#[stable(feature = "manually_drop", since = "1.20.0")]
312+
impl<T: ?Sized + PartialOrd> PartialOrd for ManuallyDrop<T> {
313+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
314+
self.value.as_ref().partial_cmp(other.value.as_ref())
315+
}
316+
}
317+
318+
#[stable(feature = "manually_drop", since = "1.20.0")]
319+
impl<T: ?Sized + Hash> Hash for ManuallyDrop<T> {
320+
fn hash<H: Hasher>(&self, state: &mut H) {
321+
self.value.as_ref().hash(state);
322+
}
323+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#![unstable(feature = "maybe_dangling", issue = "118166")]
2+
3+
use crate::{mem, ptr};
4+
5+
/// Allows wrapped [references] and [boxes] to dangle.
6+
///
7+
/// That is, if a reference (or a `Box`) is wrapped in `MaybeDangling` (including when in a
8+
/// (nested) field of a compound type wrapped in `MaybeDangling`), it does not have to follow
9+
/// pointer aliasing rules or be dereferenceable.
10+
///
11+
/// This can be useful when the value can become dangling while the function holding it is still
12+
/// executing (particularly in concurrent code). As a somewhat absurd example, consider this code:
13+
///
14+
/// ```rust,no_run
15+
/// #![feature(box_as_ptr)]
16+
/// # use std::alloc::{dealloc, Layout};
17+
/// # use std::mem;
18+
///
19+
/// let mut boxed = Box::new(0_u32);
20+
/// let ptr = Box::as_mut_ptr(&mut boxed);
21+
///
22+
/// // Safety: the pointer comes from a box and thus was allocated before; `box` is not used afterwards
23+
/// unsafe { dealloc(ptr.cast(), Layout::new::<u32>()) };
24+
///
25+
/// mem::forget(boxed); // <-- this is UB!
26+
/// ```
27+
///
28+
/// Even though the `Box`e's destructor is not run (and thus we don't have a double free bug), this
29+
/// code is still UB. This is because when moving `boxed` into `forget`, its validity invariants
30+
/// are asserted, causing UB since the `Box` is dangling.
31+
///
32+
/// To fix this we could use `MaybeDangling`:
33+
///
34+
/// ```rust
35+
/// #![feature(maybe_dangling, box_as_ptr)]
36+
/// # use std::alloc::{dealloc, Layout};
37+
/// # use std::mem::{self, MaybeDangling};
38+
///
39+
/// let mut boxed = MaybeDangling::new(Box::new(0_u32));
40+
/// let ptr = Box::as_mut_ptr(boxed.as_mut());
41+
///
42+
/// // Safety: the pointer comes from a box and thus was allocated before; `box` is not used afterwards
43+
/// unsafe { dealloc(ptr.cast(), Layout::new::<u32>()) };
44+
///
45+
/// mem::forget(boxed); // <-- this is OK!
46+
/// ```
47+
///
48+
/// Note that the bit pattern must still be valid for the wrapped type. That is, [references]
49+
/// (and [boxes]) still must be aligned and non-null.
50+
///
51+
/// Additionally note that safe code can still assume that the inner value in a `MaybeDangling` is
52+
/// **not** dangling -- functions like [`as_ref`] and [`into_inner`] are safe. It is not sound to
53+
/// return a dangling reference in a `MaybeDangling` to safe code. However, it *is* sound
54+
/// to hold such values internally inside your code -- and there's no way to do that without
55+
/// this type.
56+
///
57+
/// [references]: prim@reference
58+
/// [boxes]: ../../std/boxed/struct.Box.html
59+
/// [`into_inner`]: MaybeDangling::into_inner
60+
/// [`as_ref`]: MaybeDangling::as_ref
61+
#[repr(transparent)]
62+
#[rustc_pub_transparent]
63+
#[derive(Debug, Copy, Clone, Default)]
64+
#[lang = "maybe_dangling"]
65+
pub struct MaybeDangling<P: ?Sized>(P);
66+
67+
impl<P: ?Sized> MaybeDangling<P> {
68+
/// Wraps a value in a `MaybeDangling`, allowing it to dangle.
69+
pub const fn new(x: P) -> Self
70+
where
71+
P: Sized,
72+
{
73+
MaybeDangling(x)
74+
}
75+
76+
/// Returns a reference to the inner value.
77+
///
78+
/// Note that this is UB if the inner value is currently dangling.
79+
pub const fn as_ref(&self) -> &P {
80+
&self.0
81+
}
82+
83+
/// Returns a mutable reference to the inner value.
84+
///
85+
/// Note that this is UB if the inner value is currently dangling.
86+
pub const fn as_mut(&mut self) -> &mut P {
87+
&mut self.0
88+
}
89+
90+
/// Extracts the value from the `MaybeDangling` container.
91+
///
92+
/// Note that this is UB if the inner value is currently dangling.
93+
pub const fn into_inner(self) -> P
94+
where
95+
P: Sized,
96+
{
97+
// FIXME: replace this with `self.0` when const checker can figure out that `self` isn't actually dropped
98+
// SAFETY: this is equivalent to `self.0`
99+
let x = unsafe { ptr::read(&self.0) };
100+
mem::forget(self);
101+
x
102+
}
103+
}

library/core/src/mem/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ mod maybe_uninit;
1919
#[stable(feature = "maybe_uninit", since = "1.36.0")]
2020
pub use maybe_uninit::MaybeUninit;
2121

22+
mod maybe_dangling;
23+
#[unstable(feature = "maybe_dangling", issue = "118166")]
24+
pub use maybe_dangling::MaybeDangling;
25+
2226
mod transmutability;
2327
#[unstable(feature = "transmutability", issue = "99571")]
2428
pub use transmutability::{Assume, TransmuteFrom};

0 commit comments

Comments
 (0)