Skip to content

Commit dc9c303

Browse files
committed
add separate intrinsic for uninit memory, and do a stronger check there
1 parent a314631 commit dc9c303

File tree

6 files changed

+61
-29
lines changed

6 files changed

+61
-29
lines changed

src/libcore/intrinsics.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,12 @@ extern "rust-intrinsic" {
699699
/// A guard for unsafe functions that cannot ever be executed if `T` does not permit
700700
/// zero-initialization: This will statically either panic, or do nothing.
701701
#[cfg(not(bootstrap))]
702-
pub fn panic_if_non_zero<T>();
702+
pub fn panic_if_zero_invalid<T>();
703+
704+
/// A guard for unsafe functions that cannot ever be executed if `T` has invalid
705+
/// bit patterns: This will statically either panic, or do nothing.
706+
#[cfg(not(bootstrap))]
707+
pub fn panic_if_any_invalid<T>();
703708

704709
/// Gets a reference to a static `Location` indicating where it was called.
705710
#[cfg(not(bootstrap))]

src/libcore/mem/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ pub const fn needs_drop<T>() -> bool {
459459
#[allow(deprecated)]
460460
pub unsafe fn zeroed<T>() -> T {
461461
#[cfg(not(bootstrap))]
462-
intrinsics::panic_if_non_zero::<T>();
462+
intrinsics::panic_if_zero_invalid::<T>();
463463
#[cfg(bootstrap)]
464464
intrinsics::panic_if_uninhabited::<T>();
465465
intrinsics::init()
@@ -490,7 +490,7 @@ pub unsafe fn zeroed<T>() -> T {
490490
#[allow(deprecated)]
491491
pub unsafe fn uninitialized<T>() -> T {
492492
#[cfg(not(bootstrap))]
493-
intrinsics::panic_if_non_zero::<T>();
493+
intrinsics::panic_if_any_invalid::<T>();
494494
#[cfg(bootstrap)]
495495
intrinsics::panic_if_uninhabited::<T>();
496496
intrinsics::uninit()

src/librustc_codegen_ssa/mir/block.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -532,10 +532,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
532532
// These are intrinsics that compile to panics so that we can get a message
533533
// which mentions the offending type, even from a const context.
534534
#[derive(Debug, PartialEq)]
535-
enum PanicIntrinsic { IfUninhabited, IfNonZero };
535+
enum PanicIntrinsic { IfUninhabited, IfZeroInvalid, IfAnyInvalid };
536536
let panic_intrinsic = intrinsic.and_then(|i| match i {
537537
"panic_if_uninhabited" => Some(PanicIntrinsic::IfUninhabited),
538-
"panic_if_non_zero" => Some(PanicIntrinsic::IfNonZero),
538+
"panic_if_zero_invalid" => Some(PanicIntrinsic::IfZeroInvalid),
539+
"panic_if_any_invalid" => Some(PanicIntrinsic::IfAnyInvalid),
539540
_ => None
540541
});
541542
if let Some(intrinsic) = panic_intrinsic {
@@ -544,14 +545,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
544545
let layout = bx.layout_of(ty);
545546
let do_panic = match intrinsic {
546547
IfUninhabited => layout.abi.is_uninhabited(),
547-
IfNonZero => !layout.might_permit_zero_init(&bx).unwrap(), // error type is `!`
548+
IfZeroInvalid => // We unwrap as the error type is `!`.
549+
!layout.might_permit_raw_init(&bx, /*zero:*/ true).unwrap(),
550+
IfAnyInvalid => // We unwrap as the error type is `!`.
551+
!layout.might_permit_raw_init(&bx, /*zero:*/ false).unwrap(),
548552
};
549553
if do_panic {
550554
let msg_str = if layout.abi.is_uninhabited() {
551-
// Use this error even for IfNonZero as it is more precise.
555+
// Use this error even for the other intrinsics as it is more precise.
552556
format!("attempted to instantiate uninhabited type `{}`", ty)
557+
} else if intrinsic == IfZeroInvalid {
558+
format!("attempted to zero-initialize type `{}`, which is invalid", ty)
553559
} else {
554-
format!("attempted to zero-initialize non-zero type `{}`", ty)
560+
format!("attempted to leave type `{}` uninitialized, which is invalid", ty)
555561
};
556562
let msg = bx.const_str(Symbol::intern(&msg_str));
557563
let location = self.get_caller_location(&mut bx, span).immediate();

src/librustc_target/abi/mod.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,34 +1120,45 @@ impl<'a, Ty> TyLayout<'a, Ty> {
11201120
}
11211121
}
11221122

1123-
/// Determines if zero-initializing this type might be okay.
1123+
/// Determines if this type permits "raw" initialization by just transmuting some
1124+
/// memory into an instance of `T`.
1125+
/// `zero` indicates if the memory is zero-initialized, or alternatively
1126+
/// left entirely uninitialized.
11241127
/// This is conservative: in doubt, it will answer `true`.
1125-
pub fn might_permit_zero_init<C, E>(
1128+
pub fn might_permit_raw_init<C, E>(
11261129
&self,
11271130
cx: &C,
1131+
zero: bool,
11281132
) -> Result<bool, E>
11291133
where
11301134
Self: Copy,
11311135
Ty: TyLayoutMethods<'a, C>,
11321136
C: LayoutOf<Ty = Ty, TyLayout: MaybeResult<Self, Error = E>>
11331137
{
1134-
fn scalar_allows_zero(s: &Scalar) -> bool {
1135-
s.valid_range.contains(&0) ||
1136-
(*s.valid_range.start() > *s.valid_range.end()) // wrap-around allows 0
1137-
}
1138+
let scalar_allows_raw_init = move |s: &Scalar| -> bool {
1139+
let range = &s.valid_range;
1140+
if zero {
1141+
// The range must contain 0.
1142+
range.contains(&0) ||
1143+
(*range.start() > *range.end()) // wrap-around allows 0
1144+
} else {
1145+
// The range must include all values.
1146+
*range.start() == range.end().wrapping_add(1)
1147+
}
1148+
};
11381149

11391150
// Abi is the most informative here.
11401151
let res = match &self.abi {
11411152
Abi::Uninhabited => false, // definitely UB
1142-
Abi::Scalar(s) => scalar_allows_zero(s),
1153+
Abi::Scalar(s) => scalar_allows_raw_init(s),
11431154
Abi::ScalarPair(s1, s2) =>
1144-
scalar_allows_zero(s1) && scalar_allows_zero(s2),
1155+
scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2),
11451156
Abi::Vector { element: s, count } =>
1146-
*count == 0 || scalar_allows_zero(s),
1157+
*count == 0 || scalar_allows_raw_init(s),
11471158
Abi::Aggregate { .. } => {
11481159
// For aggregates, recurse.
11491160
let inner = match self.variants {
1150-
Variants::Multiple { .. } => // FIXME: get variant with "0" discriminant.
1161+
Variants::Multiple { .. } => // FIXME: could we be more precise here?
11511162
return Ok(true),
11521163
Variants::Single { index } => self.for_variant(&cx, index),
11531164
};
@@ -1157,12 +1168,13 @@ impl<'a, Ty> TyLayout<'a, Ty> {
11571168
FieldPlacement::Array { .. } =>
11581169
// FIXME: The widely use smallvec 0.6 creates uninit arrays
11591170
// with any element type, so let us not (yet) complain about that.
1160-
// count == 0 || inner.field(cx, 0).to_result()?.might_permit_zero_init(cx)?
1171+
// count == 0 ||
1172+
// inner.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)?
11611173
true,
11621174
FieldPlacement::Arbitrary { ref offsets, .. } => {
11631175
// Check that all fields accept zero-init.
11641176
for idx in 0..offsets.len() {
1165-
if !inner.field(cx, idx).to_result()?.might_permit_zero_init(cx)? {
1177+
if !inner.field(cx, idx).to_result()?.might_permit_raw_init(cx, zero)? {
11661178
return Ok(false);
11671179
}
11681180
}
@@ -1172,7 +1184,7 @@ impl<'a, Ty> TyLayout<'a, Ty> {
11721184
}
11731185
}
11741186
};
1175-
trace!("might_permit_zero_init({:?}) = {}", self.details, res);
1187+
trace!("might_permit_raw_init({:?}, zero={}) = {}", self.details, zero, res);
11761188
Ok(res)
11771189
}
11781190
}

src/librustc_typeck/check/intrinsic.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) {
153153
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
154154
),
155155
),
156-
"panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()),
157-
"panic_if_non_zero" => (1, Vec::new(), tcx.mk_unit()),
156+
"panic_if_uninhabited" |
157+
"panic_if_zero_invalid" |
158+
"panic_if_any_invalid" =>
159+
(1, Vec::new(), tcx.mk_unit()),
158160
"init" => (1, Vec::new(), param(0)),
159161
"uninit" => (1, Vec::new(), param(0)),
160162
"forget" => (1, vec![param(0)], tcx.mk_unit()),

src/test/ui/intrinsics/panic-uninitialized-zeroed.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,29 +73,36 @@ fn main() {
7373
// Types that do not like zero-initialziation
7474
test_panic_msg(
7575
|| mem::uninitialized::<fn()>(),
76-
"attempted to zero-initialize non-zero type `fn()`"
76+
"attempted to leave type `fn()` uninitialized, which is invalid"
7777
);
7878
test_panic_msg(
7979
|| mem::zeroed::<fn()>(),
80-
"attempted to zero-initialize non-zero type `fn()`"
80+
"attempted to zero-initialize type `fn()`, which is invalid"
8181
);
8282

8383
test_panic_msg(
8484
|| mem::uninitialized::<*const dyn Send>(),
85-
"attempted to zero-initialize non-zero type `*const dyn std::marker::Send`"
85+
"attempted to leave type `*const dyn std::marker::Send` uninitialized, which is invalid"
8686
);
8787
test_panic_msg(
8888
|| mem::zeroed::<*const dyn Send>(),
89-
"attempted to zero-initialize non-zero type `*const dyn std::marker::Send`"
89+
"attempted to zero-initialize type `*const dyn std::marker::Send`, which is invalid"
9090
);
9191

9292
test_panic_msg(
9393
|| mem::uninitialized::<(NonNull<u32>, u32, u32)>(),
94-
"attempted to zero-initialize non-zero type `(std::ptr::NonNull<u32>, u32, u32)`"
94+
"attempted to leave type `(std::ptr::NonNull<u32>, u32, u32)` uninitialized, \
95+
which is invalid"
9596
);
9697
test_panic_msg(
9798
|| mem::zeroed::<(NonNull<u32>, u32, u32)>(),
98-
"attempted to zero-initialize non-zero type `(std::ptr::NonNull<u32>, u32, u32)`"
99+
"attempted to zero-initialize type `(std::ptr::NonNull<u32>, u32, u32)`, \
100+
which is invalid"
101+
);
102+
103+
test_panic_msg(
104+
|| mem::uninitialized::<bool>(),
105+
"attempted to leave type `bool` uninitialized, which is invalid"
99106
);
100107

101108
// Some things that should work.

0 commit comments

Comments
 (0)