Skip to content

Commit 85545dd

Browse files
committed
Make uninit checks stricter but avoid issues for old hyper
1 parent 0f4bcad commit 85545dd

File tree

4 files changed

+212
-124
lines changed

4 files changed

+212
-124
lines changed

compiler/rustc_const_eval/src/might_permit_raw_init.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use crate::const_eval::CompileTimeInterpreter;
22
use crate::interpret::{InterpCx, MemoryKind, OpTy};
33
use rustc_middle::ty::layout::LayoutCx;
4-
use rustc_middle::ty::{layout::TyAndLayout, ParamEnv, TyCtxt};
4+
use rustc_middle::ty::{
5+
layout::{self, TyAndLayout},
6+
ParamEnv, TyCtxt,
7+
};
58
use rustc_session::Limit;
69
use rustc_target::abi::InitKind;
710

@@ -12,7 +15,7 @@ pub fn might_permit_raw_init<'tcx>(
1215
) -> bool {
1316
let strict = tcx.sess.opts.unstable_opts.strict_init_checks;
1417

15-
if strict {
18+
if strict || kind == InitKind::Zero {
1619
let machine = CompileTimeInterpreter::new(Limit::new(0), false);
1720

1821
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
@@ -35,6 +38,6 @@ pub fn might_permit_raw_init<'tcx>(
3538
cx.validate_operand(&ot).is_ok()
3639
} else {
3740
let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() };
38-
ty.might_permit_raw_init(&layout_cx, kind)
41+
layout::might_permit_raw_init(ty, &layout_cx)
3942
}
4043
}

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3492,3 +3492,86 @@ fn make_thin_self_ptr<'tcx>(
34923492
..tcx.layout_of(ty::ParamEnv::reveal_all().and(unit_ptr_ty)).unwrap()
34933493
}
34943494
}
3495+
3496+
/// Determines if this type permits "raw" initialization by just transmuting some
3497+
/// uninitialized memory into an instance of `T`.
3498+
///
3499+
/// This code is intentionally conservative, and will not detect
3500+
/// * making uninitialized types who have a full valid range (ints, floats, raw pointers)
3501+
/// * uninit invalid &[T] where T has align 1 (only inside arrays)
3502+
///
3503+
/// A strict form of these checks that uses const evaluation exists in
3504+
/// `rustc_const_eval::might_permit_raw_init`, and a tracking issue for making these checks
3505+
/// stricter is <https://github.com/rust-lang/rust/issues/66151>.
3506+
///
3507+
/// FIXME: Once all the conservatism is removed from here, and the checks are ran by default,
3508+
/// we can use the const evaluation checks always instead.
3509+
pub fn might_permit_raw_init<'tcx, C>(this: TyAndLayout<'tcx>, cx: &C) -> bool
3510+
where
3511+
C: HasDataLayout + ty::layout::HasParamEnv<'tcx> + ty::layout::HasTyCtxt<'tcx>,
3512+
{
3513+
return might_permit_raw_init_inner(this, cx, false);
3514+
3515+
fn might_permit_raw_init_inner<'tcx, C>(
3516+
this: TyAndLayout<'tcx>,
3517+
cx: &C,
3518+
inside_array: bool,
3519+
) -> bool
3520+
where
3521+
C: HasDataLayout + ty::layout::HasParamEnv<'tcx> + ty::layout::HasTyCtxt<'tcx>,
3522+
{
3523+
if inside_array {
3524+
if let ty::Ref(_, inner, _) = this.ty.kind() {
3525+
if let ty::Slice(inner) = inner.kind() {
3526+
let penv = ty::ParamEnv::reveal_all().and(*inner);
3527+
if let Ok(l) = cx.tcx().layout_of(penv) {
3528+
return l.layout.align().abi == Align::ONE;
3529+
}
3530+
}
3531+
3532+
if let ty::Str = inner.kind() {
3533+
return true;
3534+
}
3535+
}
3536+
}
3537+
3538+
// Check the ABI.
3539+
let valid = match this.abi {
3540+
Abi::Uninhabited => false, // definitely UB
3541+
Abi::Scalar(s) => s.is_always_valid(cx),
3542+
Abi::ScalarPair(s1, s2) => s1.is_always_valid(cx) && s2.is_always_valid(cx),
3543+
Abi::Vector { element: s, count } => count == 0 || s.is_always_valid(cx),
3544+
Abi::Aggregate { .. } => true, // Fields are checked below.
3545+
};
3546+
if !valid {
3547+
// This is definitely not okay.
3548+
return false;
3549+
}
3550+
3551+
// If we have not found an error yet, we need to recursively descend into fields.
3552+
match &this.fields {
3553+
FieldsShape::Primitive | FieldsShape::Union { .. } => {}
3554+
FieldsShape::Array { count, .. } => {
3555+
if *count > 0 && !might_permit_raw_init_inner(this.field(cx, 0), cx, true) {
3556+
// Found non empty array with a type that is unhappy about this kind of initialization
3557+
return false;
3558+
}
3559+
}
3560+
FieldsShape::Arbitrary { offsets, .. } => {
3561+
for idx in 0..offsets.len() {
3562+
if !might_permit_raw_init_inner(this.field(cx, idx), cx, inside_array) {
3563+
// We found a field that is unhappy with this kind of initialization.
3564+
return false;
3565+
}
3566+
}
3567+
}
3568+
}
3569+
3570+
if matches!(this.variants, Variants::Multiple { .. }) {
3571+
// All uninit enums are automatically invalid, even if their discriminant includes all values.
3572+
return false;
3573+
}
3574+
3575+
true
3576+
}
3577+
}

compiler/rustc_target/src/abi/mod.rs

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,72 +1485,4 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
14851485
Abi::Aggregate { sized } => sized && self.size.bytes() == 0,
14861486
}
14871487
}
1488-
1489-
/// Determines if this type permits "raw" initialization by just transmuting some
1490-
/// memory into an instance of `T`.
1491-
///
1492-
/// `init_kind` indicates if the memory is zero-initialized or left uninitialized.
1493-
///
1494-
/// This code is intentionally conservative, and will not detect
1495-
/// * zero init of an enum whose 0 variant does not allow zero initialization
1496-
/// * making uninitialized types who have a full valid range (ints, floats, raw pointers)
1497-
/// * Any form of invalid value being made inside an array (unless the value is uninhabited)
1498-
///
1499-
/// A strict form of these checks that uses const evaluation exists in
1500-
/// `rustc_const_eval::might_permit_raw_init`, and a tracking issue for making these checks
1501-
/// stricter is <https://github.com/rust-lang/rust/issues/66151>.
1502-
///
1503-
/// FIXME: Once all the conservatism is removed from here, and the checks are ran by default,
1504-
/// we can use the const evaluation checks always instead.
1505-
pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind) -> bool
1506-
where
1507-
Self: Copy,
1508-
Ty: TyAbiInterface<'a, C>,
1509-
C: HasDataLayout,
1510-
{
1511-
let scalar_allows_raw_init = move |s: Scalar| -> bool {
1512-
match init_kind {
1513-
InitKind::Zero => {
1514-
// The range must contain 0.
1515-
s.valid_range(cx).contains(0)
1516-
}
1517-
InitKind::Uninit => {
1518-
// The range must include all values.
1519-
s.is_always_valid(cx)
1520-
}
1521-
}
1522-
};
1523-
1524-
// Check the ABI.
1525-
let valid = match self.abi {
1526-
Abi::Uninhabited => false, // definitely UB
1527-
Abi::Scalar(s) => scalar_allows_raw_init(s),
1528-
Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2),
1529-
Abi::Vector { element: s, count } => count == 0 || scalar_allows_raw_init(s),
1530-
Abi::Aggregate { .. } => true, // Fields are checked below.
1531-
};
1532-
if !valid {
1533-
// This is definitely not okay.
1534-
return false;
1535-
}
1536-
1537-
// If we have not found an error yet, we need to recursively descend into fields.
1538-
match &self.fields {
1539-
FieldsShape::Primitive | FieldsShape::Union { .. } => {}
1540-
FieldsShape::Array { .. } => {
1541-
// FIXME(#66151): For now, we are conservative and do not check arrays by default.
1542-
}
1543-
FieldsShape::Arbitrary { offsets, .. } => {
1544-
for idx in 0..offsets.len() {
1545-
if !self.field(cx, idx).might_permit_raw_init(cx, init_kind) {
1546-
// We found a field that is unhappy with this kind of initialization.
1547-
return false;
1548-
}
1549-
}
1550-
}
1551-
}
1552-
1553-
// FIXME(#66151): For now, we are conservative and do not check `self.variants`.
1554-
true
1555-
}
15561488
}

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

Lines changed: 123 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,31 @@ enum ZeroIsValid {
6464
One(NonNull<()>) = 1,
6565
}
6666

67+
#[rustfmt::skip]
68+
#[allow(dead_code)]
69+
enum SoManyVariants {
70+
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17,
71+
A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32,
72+
A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47,
73+
A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, A60, A61, A62,
74+
A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77,
75+
A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, A90, A91, A92,
76+
A93, A94, A95, A96, A97, A98, A99, A100, A101, A102, A103, A104, A105, A106,
77+
A107, A108, A109, A110, A111, A112, A113, A114, A115, A116, A117, A118, A119,
78+
A120, A121, A122, A123, A124, A125, A126, A127, A128, A129, A130, A131, A132,
79+
A133, A134, A135, A136, A137, A138, A139, A140, A141, A142, A143, A144, A145,
80+
A146, A147, A148, A149, A150, A151, A152, A153, A154, A155, A156, A157, A158,
81+
A159, A160, A161, A162, A163, A164, A165, A166, A167, A168, A169, A170, A171,
82+
A172, A173, A174, A175, A176, A177, A178, A179, A180, A181, A182, A183, A184,
83+
A185, A186, A187, A188, A189, A190, A191, A192, A193, A194, A195, A196, A197,
84+
A198, A199, A200, A201, A202, A203, A204, A205, A206, A207, A208, A209, A210,
85+
A211, A212, A213, A214, A215, A216, A217, A218, A219, A220, A221, A222, A223,
86+
A224, A225, A226, A227, A228, A229, A230, A231, A232, A233, A234, A235, A236,
87+
A237, A238, A239, A240, A241, A242, A243, A244, A245, A246, A247, A248, A249,
88+
A250, A251, A252, A253, A254, A255, A256,
89+
}
90+
91+
#[track_caller]
6792
fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
6893
let err = panic::catch_unwind(op).err();
6994
assert_eq!(
@@ -72,6 +97,19 @@ fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
7297
);
7398
}
7499

100+
#[track_caller]
101+
// If strict mode is enabled, expect the msg. Otherwise, expect there to be no error.
102+
fn test_strict_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
103+
let err = panic::catch_unwind(op).err();
104+
105+
let expectation = if cfg!(strict) { Some(&msg) } else { None };
106+
107+
assert_eq!(
108+
err.as_ref().and_then(|a| a.downcast_ref::<&str>()),
109+
expectation
110+
);
111+
}
112+
75113
fn main() {
76114
unsafe {
77115
// Uninhabited types
@@ -221,6 +259,67 @@ fn main() {
221259
"attempted to leave type `core::mem::manually_drop::ManuallyDrop<LR>` uninitialized, which is invalid"
222260
);
223261

262+
test_panic_msg(
263+
|| mem::uninitialized::<&'static [u8]>(),
264+
"attempted to leave type `&[u8]` uninitialized, which is invalid"
265+
);
266+
267+
test_panic_msg(
268+
|| mem::uninitialized::<&'static [u16]>(),
269+
"attempted to leave type `&[u16]` uninitialized, which is invalid"
270+
);
271+
272+
test_panic_msg(
273+
|| mem::uninitialized::<SoManyVariants>(),
274+
"attempted to leave type `SoManyVariants` uninitialized, which is invalid"
275+
);
276+
277+
test_panic_msg(
278+
|| mem::zeroed::<[(&'static [u8], &'static str); 1]>(),
279+
"attempted to zero-initialize type `[(&[u8], &str); 1]`, which is invalid"
280+
);
281+
282+
test_panic_msg(
283+
|| mem::uninitialized::<[&'static [u16]; 1]>(),
284+
"attempted to leave type `[&[u16]; 1]` uninitialized, which is invalid"
285+
);
286+
287+
test_panic_msg(
288+
|| mem::zeroed::<[NonNull<()>; 1]>(),
289+
"attempted to zero-initialize type `[core::ptr::non_null::NonNull<()>; 1]`, which is invalid"
290+
);
291+
292+
test_panic_msg(
293+
|| mem::uninitialized::<[NonNull<()>; 1]>(),
294+
"attempted to leave type `[core::ptr::non_null::NonNull<()>; 1]` uninitialized, which is invalid"
295+
);
296+
297+
test_panic_msg(
298+
|| mem::zeroed::<LR_NonZero>(),
299+
"attempted to zero-initialize type `LR_NonZero`, which is invalid"
300+
);
301+
302+
test_panic_msg(
303+
|| mem::zeroed::<[LR_NonZero; 1]>(),
304+
"attempted to zero-initialize type `[LR_NonZero; 1]`, which is invalid"
305+
);
306+
307+
test_panic_msg(
308+
|| mem::zeroed::<[LR_NonZero; 1]>(),
309+
"attempted to zero-initialize type `[LR_NonZero; 1]`, which is invalid"
310+
);
311+
312+
test_panic_msg(
313+
|| mem::zeroed::<ManuallyDrop<LR_NonZero>>(),
314+
"attempted to zero-initialize type `core::mem::manually_drop::ManuallyDrop<LR_NonZero>`, \
315+
which is invalid"
316+
);
317+
318+
test_panic_msg(
319+
|| mem::uninitialized::<(&'static [u8], &'static str)>(),
320+
"attempted to leave type `(&[u8], &str)` uninitialized, which is invalid"
321+
);
322+
224323
// Some things that should work.
225324
let _val = mem::zeroed::<bool>();
226325
let _val = mem::zeroed::<LR>();
@@ -230,63 +329,34 @@ fn main() {
230329
let _val = mem::zeroed::<MaybeUninit<NonNull<u32>>>();
231330
let _val = mem::zeroed::<[!; 0]>();
232331
let _val = mem::zeroed::<ZeroIsValid>();
332+
let _val = mem::zeroed::<SoManyVariants>();
333+
let _val = mem::uninitialized::<[SoManyVariants; 0]>();
233334
let _val = mem::uninitialized::<MaybeUninit<bool>>();
234335
let _val = mem::uninitialized::<[!; 0]>();
235336
let _val = mem::uninitialized::<()>();
236337
let _val = mem::uninitialized::<ZeroSized>();
237338

238-
if cfg!(strict) {
239-
test_panic_msg(
240-
|| mem::uninitialized::<i32>(),
241-
"attempted to leave type `i32` uninitialized, which is invalid"
242-
);
243-
244-
test_panic_msg(
245-
|| mem::uninitialized::<*const ()>(),
246-
"attempted to leave type `*const ()` uninitialized, which is invalid"
247-
);
248-
249-
test_panic_msg(
250-
|| mem::uninitialized::<[i32; 1]>(),
251-
"attempted to leave type `[i32; 1]` uninitialized, which is invalid"
252-
);
253-
254-
test_panic_msg(
255-
|| mem::zeroed::<NonNull<()>>(),
256-
"attempted to zero-initialize type `core::ptr::non_null::NonNull<()>`, which is invalid"
257-
);
258-
259-
test_panic_msg(
260-
|| mem::zeroed::<[NonNull<()>; 1]>(),
261-
"attempted to zero-initialize type `[core::ptr::non_null::NonNull<()>; 1]`, which is invalid"
262-
);
263-
264-
// FIXME(#66151) we conservatively do not error here yet (by default).
265-
test_panic_msg(
266-
|| mem::zeroed::<LR_NonZero>(),
267-
"attempted to zero-initialize type `LR_NonZero`, which is invalid"
268-
);
269-
270-
test_panic_msg(
271-
|| mem::zeroed::<ManuallyDrop<LR_NonZero>>(),
272-
"attempted to zero-initialize type `core::mem::manually_drop::ManuallyDrop<LR_NonZero>`, \
273-
which is invalid"
274-
);
275-
} else {
276-
// These are UB because they have not been officially blessed, but we await the resolution
277-
// of <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing
278-
// anything about that.
279-
let _val = mem::uninitialized::<i32>();
280-
let _val = mem::uninitialized::<*const ()>();
281-
282-
// These are UB, but best to test them to ensure we don't become unintentionally
283-
// stricter.
284-
285-
// It's currently unchecked to create invalid enums and values inside arrays.
286-
let _val = mem::zeroed::<LR_NonZero>();
287-
let _val = mem::zeroed::<[LR_NonZero; 1]>();
288-
let _val = mem::zeroed::<[NonNull<()>; 1]>();
289-
let _val = mem::uninitialized::<[NonNull<()>; 1]>();
290-
}
339+
// These are UB because they have not been officially blessed, but we await the resolution
340+
// of <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing
341+
// anything about that.
342+
test_strict_panic_msg(
343+
|| mem::uninitialized::<i32>(),
344+
"attempted to leave type `i32` uninitialized, which is invalid"
345+
);
346+
347+
test_strict_panic_msg(
348+
|| mem::uninitialized::<*const ()>(),
349+
"attempted to leave type `*const ()` uninitialized, which is invalid"
350+
);
351+
352+
test_strict_panic_msg(
353+
|| mem::uninitialized::<[i32; 1]>(),
354+
"attempted to leave type `[i32; 1]` uninitialized, which is invalid"
355+
);
356+
357+
test_strict_panic_msg(
358+
|| mem::uninitialized::<[(&'static [u8], &'static str); 1]>(),
359+
"attempted to leave type `[(&[u8], &str); 1]` uninitialized, which is invalid"
360+
);
291361
}
292362
}

0 commit comments

Comments
 (0)