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