1use rustc_abi::{Align, AlignFromBytesError, CanonAbi, 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::PowerPC64LE
56 | Arch::SpirV
57 | Arch::Other(_)) => bug!("unsupported target architecture for malloc: `{arch}`"),
58 };
59 if size >= max_fundamental_align {
68 return Align::from_bytes(max_fundamental_align).unwrap();
69 }
70 if size == 0 {
72 return Align::ONE;
73 }
74 fn prev_power_of_two(x: u64) -> u64 {
76 let next_pow2 = x.next_power_of_two();
77 if next_pow2 == x {
78 x
80 } else {
81 next_pow2 / 2
83 }
84 }
85 Align::from_bytes(prev_power_of_two(size)).unwrap()
86 }
87
88 fn check_rust_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> {
91 let this = self.eval_context_ref();
92 if size == 0 {
93 throw_ub_format!("creating allocation with size 0");
94 }
95 if size > this.max_size_of_val().bytes() {
96 throw_ub_format!("creating an allocation larger than half the address space");
97 }
98 if let Err(e) = Align::from_bytes(align) {
99 match e {
100 AlignFromBytesError::TooLarge(_) => {
101 throw_unsup_format!(
102 "creating allocation with alignment {align} exceeding rustc's maximum \
103 supported value"
104 );
105 }
106 AlignFromBytesError::NotPowerOfTwo(_) => {
107 throw_ub_format!("creating allocation with non-power-of-two alignment {align}");
108 }
109 }
110 }
111
112 interp_ok(())
113 }
114
115 fn rust_special_allocator_method(
116 &mut self,
117 method: SpecialAllocatorMethod,
118 link_name: Symbol,
119 abi: &FnAbi<'tcx, Ty<'tcx>>,
120 args: &[OpTy<'tcx>],
121 dest: &PlaceTy<'tcx>,
122 ) -> InterpResult<'tcx> {
123 let this = self.eval_context_mut();
124
125 match method {
126 SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => {
127 let [size, align] =
128 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
129 let size = this.read_target_usize(size)?;
130 let align = this.read_target_usize(align)?;
131
132 this.check_rust_alloc_request(size, align)?;
133
134 let ptr = this.allocate_ptr(
135 Size::from_bytes(size),
136 Align::from_bytes(align).unwrap(),
137 MiriMemoryKind::Rust.into(),
138 if matches!(method, SpecialAllocatorMethod::AllocZeroed) {
139 AllocInit::Zero
140 } else {
141 AllocInit::Uninit
142 },
143 )?;
144
145 this.write_pointer(ptr, dest)
146 }
147 SpecialAllocatorMethod::Dealloc => {
148 let [ptr, old_size, align] =
149 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
150 let ptr = this.read_pointer(ptr)?;
151 let old_size = this.read_target_usize(old_size)?;
152 let align = this.read_target_usize(align)?;
153
154 this.deallocate_ptr(
156 ptr,
157 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
158 MiriMemoryKind::Rust.into(),
159 )
160 }
161 SpecialAllocatorMethod::Realloc => {
162 let [ptr, old_size, align, new_size] =
163 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
164 let ptr = this.read_pointer(ptr)?;
165 let old_size = this.read_target_usize(old_size)?;
166 let align = this.read_target_usize(align)?;
167 let new_size = this.read_target_usize(new_size)?;
168 this.check_rust_alloc_request(new_size, align)?;
171
172 let align = Align::from_bytes(align).unwrap();
173 let new_ptr = this.reallocate_ptr(
174 ptr,
175 Some((Size::from_bytes(old_size), align)),
176 Size::from_bytes(new_size),
177 align,
178 MiriMemoryKind::Rust.into(),
179 AllocInit::Uninit,
180 )?;
181 this.write_pointer(new_ptr, dest)
182 }
183 }
184 }
185
186 fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> {
187 let this = self.eval_context_mut();
188 let align = this.malloc_align(size);
189 let ptr =
190 this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?;
191 interp_ok(ptr.into())
192 }
193
194 fn posix_memalign(
195 &mut self,
196 memptr: &OpTy<'tcx>,
197 align: &OpTy<'tcx>,
198 size: &OpTy<'tcx>,
199 ) -> InterpResult<'tcx, Scalar> {
200 let this = self.eval_context_mut();
201 let memptr = this.deref_pointer_as(memptr, this.machine.layouts.mut_raw_ptr)?;
202 let align = this.read_target_usize(align)?;
203 let size = this.read_target_usize(size)?;
204
205 if !align.is_power_of_two() || align < this.pointer_size().bytes() {
208 interp_ok(this.eval_libc("EINVAL"))
209 } else {
210 let ptr = this.allocate_ptr(
211 Size::from_bytes(size),
212 Align::from_bytes(align).unwrap(),
213 MiriMemoryKind::C.into(),
214 AllocInit::Uninit,
215 )?;
216 this.write_pointer(ptr, &memptr)?;
217 interp_ok(Scalar::from_i32(0))
218 }
219 }
220
221 fn free(&mut self, ptr: Pointer) -> InterpResult<'tcx> {
222 let this = self.eval_context_mut();
223 if !this.ptr_is_null(ptr)? {
224 this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?;
225 }
226 interp_ok(())
227 }
228
229 fn realloc(&mut self, old_ptr: Pointer, new_size: u64) -> InterpResult<'tcx, Pointer> {
230 let this = self.eval_context_mut();
231 let new_align = this.malloc_align(new_size);
232 if this.ptr_is_null(old_ptr)? {
233 self.malloc(new_size, AllocInit::Uninit)
235 } else {
236 if new_size == 0 {
237 throw_ub_format!("`realloc` with a size of zero");
240 } else {
241 let new_ptr = this.reallocate_ptr(
242 old_ptr,
243 None,
244 Size::from_bytes(new_size),
245 new_align,
246 MiriMemoryKind::C.into(),
247 AllocInit::Uninit,
248 )?;
249 interp_ok(new_ptr.into())
250 }
251 }
252 }
253
254 fn aligned_alloc(
255 &mut self,
256 align: &OpTy<'tcx>,
257 size: &OpTy<'tcx>,
258 ) -> InterpResult<'tcx, Pointer> {
259 let this = self.eval_context_mut();
260 let align = this.read_target_usize(align)?;
261 let size = this.read_target_usize(size)?;
262
263 match size.checked_rem(align) {
282 Some(0) if align.is_power_of_two() => {
283 let align = align.max(this.malloc_align(size).bytes());
284 let ptr = this.allocate_ptr(
285 Size::from_bytes(size),
286 Align::from_bytes(align).unwrap(),
287 MiriMemoryKind::C.into(),
288 AllocInit::Uninit,
289 )?;
290 interp_ok(ptr.into())
291 }
292 _ => interp_ok(Pointer::null()),
293 }
294 }
295}