1use rustc_abi::{Align, AlignFromBytesError, Size};
2use rustc_ast::expand::allocator::SpecialAllocatorMethod;
3use rustc_middle::ty::Ty;
4use rustc_span::Symbol;
5use rustc_target::callconv::FnAbi;
6use rustc_target::spec::{Arch, Os};
7
8use crate::*;
9
10impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
11pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
12 fn malloc_align(&self, size: u64) -> Align {
14 let this = self.eval_context_ref();
15 let os = &this.tcx.sess.target.os;
23 let max_fundamental_align = match &this.tcx.sess.target.arch {
24 Arch::RiscV32 if matches!(os, Os::EspIdf | Os::Zkvm) => 4,
25 Arch::Xtensa if matches!(os, Os::EspIdf) => 4,
26 Arch::X86
27 | Arch::Arm
28 | Arch::M68k
29 | Arch::CSky
30 | Arch::LoongArch32
31 | Arch::Mips
32 | Arch::Mips32r6
33 | Arch::PowerPC
34 | Arch::PowerPC64
35 | Arch::Sparc
36 | Arch::Wasm32
37 | Arch::Hexagon
38 | Arch::RiscV32
39 | Arch::Xtensa => 8,
40 Arch::X86_64
41 | Arch::AArch64
42 | Arch::Arm64EC
43 | Arch::LoongArch64
44 | Arch::Mips64
45 | Arch::Mips64r6
46 | Arch::S390x
47 | Arch::Sparc64
48 | Arch::RiscV64
49 | Arch::Wasm64 => 16,
50 arch @ (Arch::AmdGpu
51 | Arch::Avr
52 | Arch::Bpf
53 | Arch::Msp430
54 | Arch::Nvptx64
55 | Arch::SpirV
56 | Arch::Other(_)) => bug!("unsupported target architecture for malloc: `{arch}`"),
57 };
58 if size >= max_fundamental_align {
67 return Align::from_bytes(max_fundamental_align).unwrap();
68 }
69 if size == 0 {
71 return Align::ONE;
72 }
73 fn prev_power_of_two(x: u64) -> u64 {
75 let next_pow2 = x.next_power_of_two();
76 if next_pow2 == x {
77 x
79 } else {
80 next_pow2 / 2
82 }
83 }
84 Align::from_bytes(prev_power_of_two(size)).unwrap()
85 }
86
87 fn check_rust_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> {
90 let this = self.eval_context_ref();
91 if size == 0 {
92 throw_ub_format!("creating allocation with size 0");
93 }
94 if size > this.max_size_of_val().bytes() {
95 throw_ub_format!("creating an allocation larger than half the address space");
96 }
97 if let Err(e) = Align::from_bytes(align) {
98 match e {
99 AlignFromBytesError::TooLarge(_) => {
100 throw_unsup_format!(
101 "creating allocation with alignment {align} exceeding rustc's maximum \
102 supported value"
103 );
104 }
105 AlignFromBytesError::NotPowerOfTwo(_) => {
106 throw_ub_format!("creating allocation with non-power-of-two alignment {align}");
107 }
108 }
109 }
110
111 interp_ok(())
112 }
113
114 fn rust_special_allocator_method(
115 &mut self,
116 method: SpecialAllocatorMethod,
117 link_name: Symbol,
118 abi: &FnAbi<'tcx, Ty<'tcx>>,
119 args: &[OpTy<'tcx>],
120 dest: &PlaceTy<'tcx>,
121 ) -> InterpResult<'tcx> {
122 let this = self.eval_context_mut();
123
124 match method {
125 SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => {
126 let [size, align] = this.check_shim_sig(
127 shim_sig!(extern "Rust" fn(usize, core::mem::Alignment) -> *_),
128 (link_name, abi, args),
129 )?;
130 let size = this.read_target_usize(size)?;
131 let align = this.read_target_usize(align)?;
132
133 this.check_rust_alloc_request(size, align)?;
134
135 let ptr = this.allocate_ptr(
136 Size::from_bytes(size),
137 Align::from_bytes(align).unwrap(),
138 MiriMemoryKind::Rust.into(),
139 if matches!(method, SpecialAllocatorMethod::AllocZeroed) {
140 AllocInit::Zero
141 } else {
142 AllocInit::Uninit
143 },
144 )?;
145
146 this.write_pointer(ptr, dest)
147 }
148 SpecialAllocatorMethod::Dealloc => {
149 let [ptr, old_size, align] = this.check_shim_sig(
150 shim_sig!(extern "Rust" fn(*_, usize, core::mem::Alignment) -> ()),
151 (link_name, abi, args),
152 )?;
153 let ptr = this.read_pointer(ptr)?;
154 let old_size = this.read_target_usize(old_size)?;
155 let align = this.read_target_usize(align)?;
156
157 this.deallocate_ptr(
159 ptr,
160 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
161 MiriMemoryKind::Rust.into(),
162 )
163 }
164 SpecialAllocatorMethod::Realloc => {
165 let [ptr, old_size, align, new_size] = this.check_shim_sig(
166 shim_sig!(extern "Rust" fn(*_, usize, core::mem::Alignment, usize) -> *_),
167 (link_name, abi, args),
168 )?;
169 let ptr = this.read_pointer(ptr)?;
170 let old_size = this.read_target_usize(old_size)?;
171 let align = this.read_target_usize(align)?;
172 let new_size = this.read_target_usize(new_size)?;
173 this.check_rust_alloc_request(new_size, align)?;
176
177 let align = Align::from_bytes(align).unwrap();
178 let new_ptr = this.reallocate_ptr(
179 ptr,
180 Some((Size::from_bytes(old_size), align)),
181 Size::from_bytes(new_size),
182 align,
183 MiriMemoryKind::Rust.into(),
184 AllocInit::Uninit,
185 )?;
186 this.write_pointer(new_ptr, dest)
187 }
188 }
189 }
190
191 fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> {
192 let this = self.eval_context_mut();
193 let align = this.malloc_align(size);
194 let ptr =
195 this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?;
196 interp_ok(ptr.into())
197 }
198
199 fn posix_memalign(
200 &mut self,
201 memptr: &OpTy<'tcx>,
202 align: &OpTy<'tcx>,
203 size: &OpTy<'tcx>,
204 ) -> InterpResult<'tcx, Scalar> {
205 let this = self.eval_context_mut();
206 let memptr = this.deref_pointer_as(memptr, this.machine.layouts.mut_raw_ptr)?;
207 let align = this.read_target_usize(align)?;
208 let size = this.read_target_usize(size)?;
209
210 if !align.is_power_of_two() || align < this.pointer_size().bytes() {
213 interp_ok(this.eval_libc("EINVAL"))
214 } else {
215 let ptr = this.allocate_ptr(
216 Size::from_bytes(size),
217 Align::from_bytes(align).unwrap(),
218 MiriMemoryKind::C.into(),
219 AllocInit::Uninit,
220 )?;
221 this.write_pointer(ptr, &memptr)?;
222 interp_ok(Scalar::from_i32(0))
223 }
224 }
225
226 fn free(&mut self, ptr: Pointer) -> InterpResult<'tcx> {
227 let this = self.eval_context_mut();
228 if !this.ptr_is_null(ptr)? {
229 this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?;
230 }
231 interp_ok(())
232 }
233
234 fn realloc(&mut self, old_ptr: Pointer, new_size: u64) -> InterpResult<'tcx, Pointer> {
235 let this = self.eval_context_mut();
236 let new_align = this.malloc_align(new_size);
237 if this.ptr_is_null(old_ptr)? {
238 self.malloc(new_size, AllocInit::Uninit)
240 } else {
241 if new_size == 0 {
242 throw_ub_format!("`realloc` with a size of zero");
245 } else {
246 let new_ptr = this.reallocate_ptr(
247 old_ptr,
248 None,
249 Size::from_bytes(new_size),
250 new_align,
251 MiriMemoryKind::C.into(),
252 AllocInit::Uninit,
253 )?;
254 interp_ok(new_ptr.into())
255 }
256 }
257 }
258
259 fn aligned_alloc(
260 &mut self,
261 align: &OpTy<'tcx>,
262 size: &OpTy<'tcx>,
263 ) -> InterpResult<'tcx, Pointer> {
264 let this = self.eval_context_mut();
265 let align = this.read_target_usize(align)?;
266 let size = this.read_target_usize(size)?;
267
268 match size.checked_rem(align) {
287 Some(0) if align.is_power_of_two() => {
288 let align = align.max(this.malloc_align(size).bytes());
289 let ptr = this.allocate_ptr(
290 Size::from_bytes(size),
291 Align::from_bytes(align).unwrap(),
292 MiriMemoryKind::C.into(),
293 AllocInit::Uninit,
294 )?;
295 interp_ok(ptr.into())
296 }
297 _ => interp_ok(Pointer::null()),
298 }
299 }
300}