]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_const_eval/src/util/might_permit_raw_init.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / compiler / rustc_const_eval / src / util / might_permit_raw_init.rs
CommitLineData
2b03887a
FG
1use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
2use rustc_middle::ty::{ParamEnv, TyCtxt};
3use rustc_session::Limit;
4use rustc_target::abi::{Abi, FieldsShape, InitKind, Scalar, Variants};
5
9c376795 6use crate::const_eval::{CheckAlignment, CompileTimeInterpreter};
2b03887a
FG
7use crate::interpret::{InterpCx, MemoryKind, OpTy};
8
9/// Determines if this type permits "raw" initialization by just transmuting some memory into an
10/// instance of `T`.
11///
12/// `init_kind` indicates if the memory is zero-initialized or left uninitialized. We assume
13/// uninitialized memory is mitigated by filling it with 0x01, which reduces the chance of causing
14/// LLVM UB.
15///
16/// By default we check whether that operation would cause *LLVM UB*, i.e., whether the LLVM IR we
17/// generate has UB or not. This is a mitigation strategy, which is why we are okay with accepting
18/// Rust UB as long as there is no risk of miscompilations. The `strict_init_checks` can be set to
19/// do a full check against Rust UB instead (in which case we will also ignore the 0x01-filling and
20/// to the full uninit check).
21pub fn might_permit_raw_init<'tcx>(
22 tcx: TyCtxt<'tcx>,
23 ty: TyAndLayout<'tcx>,
24 kind: InitKind,
25) -> bool {
26 if tcx.sess.opts.unstable_opts.strict_init_checks {
27 might_permit_raw_init_strict(ty, tcx, kind)
28 } else {
29 let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() };
30 might_permit_raw_init_lax(ty, &layout_cx, kind)
31 }
32}
33
34/// Implements the 'strict' version of the `might_permit_raw_init` checks; see that function for
35/// details.
36fn might_permit_raw_init_strict<'tcx>(
37 ty: TyAndLayout<'tcx>,
38 tcx: TyCtxt<'tcx>,
39 kind: InitKind,
40) -> bool {
41 let machine = CompileTimeInterpreter::new(
42 Limit::new(0),
43 /*can_access_statics:*/ false,
9c376795 44 CheckAlignment::Error,
2b03887a
FG
45 );
46
47 let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
48
49 let allocated = cx
50 .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
51 .expect("OOM: failed to allocate for uninit check");
52
53 if kind == InitKind::Zero {
54 cx.write_bytes_ptr(
55 allocated.ptr,
56 std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()),
57 )
58 .expect("failed to write bytes for zero valid check");
59 }
60
61 let ot: OpTy<'_, _> = allocated.into();
62
63 // Assume that if it failed, it's a validation failure.
64 // This does *not* actually check that references are dereferenceable, but since all types that
65 // require dereferenceability also require non-null, we don't actually get any false negatives
66 // due to this.
67 cx.validate_operand(&ot).is_ok()
68}
69
70/// Implements the 'lax' (default) version of the `might_permit_raw_init` checks; see that function for
71/// details.
72fn might_permit_raw_init_lax<'tcx>(
73 this: TyAndLayout<'tcx>,
74 cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
75 init_kind: InitKind,
76) -> bool {
77 let scalar_allows_raw_init = move |s: Scalar| -> bool {
78 match init_kind {
79 InitKind::Zero => {
80 // The range must contain 0.
81 s.valid_range(cx).contains(0)
82 }
83 InitKind::UninitMitigated0x01Fill => {
84 // The range must include an 0x01-filled buffer.
85 let mut val: u128 = 0x01;
86 for _ in 1..s.size(cx).bytes() {
87 // For sizes >1, repeat the 0x01.
88 val = (val << 8) | 0x01;
89 }
90 s.valid_range(cx).contains(val)
91 }
92 }
93 };
94
95 // Check the ABI.
96 let valid = match this.abi {
97 Abi::Uninhabited => false, // definitely UB
98 Abi::Scalar(s) => scalar_allows_raw_init(s),
99 Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2),
100 Abi::Vector { element: s, count } => count == 0 || scalar_allows_raw_init(s),
101 Abi::Aggregate { .. } => true, // Fields are checked below.
102 };
103 if !valid {
104 // This is definitely not okay.
105 return false;
106 }
107
108 // Special magic check for references and boxes (i.e., special pointer types).
109 if let Some(pointee) = this.ty.builtin_deref(false) {
110 let pointee = cx.layout_of(pointee.ty).expect("need to be able to compute layouts");
111 // We need to ensure that the LLVM attributes `aligned` and `dereferenceable(size)` are satisfied.
112 if pointee.align.abi.bytes() > 1 {
113 // 0x01-filling is not aligned.
114 return false;
115 }
116 if pointee.size.bytes() > 0 {
117 // A 'fake' integer pointer is not sufficiently dereferenceable.
118 return false;
119 }
120 }
121
122 // If we have not found an error yet, we need to recursively descend into fields.
123 match &this.fields {
124 FieldsShape::Primitive | FieldsShape::Union { .. } => {}
125 FieldsShape::Array { .. } => {
126 // Arrays never have scalar layout in LLVM, so if the array is not actually
127 // accessed, there is no LLVM UB -- therefore we can skip this.
128 }
129 FieldsShape::Arbitrary { offsets, .. } => {
130 for idx in 0..offsets.len() {
131 if !might_permit_raw_init_lax(this.field(cx, idx), cx, init_kind) {
132 // We found a field that is unhappy with this kind of initialization.
133 return false;
134 }
135 }
136 }
137 }
138
139 match &this.variants {
140 Variants::Single { .. } => {
141 // All fields of this single variant have already been checked above, there is nothing
142 // else to do.
143 }
144 Variants::Multiple { .. } => {
145 // We cannot tell LLVM anything about the details of this multi-variant layout, so
146 // invalid values "hidden" inside the variant cannot cause LLVM trouble.
147 }
148 }
149
150 true
151}