]>
Commit | Line | Data |
---|---|---|
2b03887a FG |
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::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 | 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. | |
36 | fn 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, | |
44 | /*check_alignment:*/ true, | |
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. | |
72 | fn 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 | } |