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::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] =
127 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
128 let size = this.read_target_usize(size)?;
129 let align = this.read_target_usize(align)?;
130
131 this.check_rust_alloc_request(size, align)?;
132
133 let ptr = this.allocate_ptr(
134 Size::from_bytes(size),
135 Align::from_bytes(align).unwrap(),
136 MiriMemoryKind::Rust.into(),
137 if matches!(method, SpecialAllocatorMethod::AllocZeroed) {
138 AllocInit::Zero
139 } else {
140 AllocInit::Uninit
141 },
142 )?;
143
144 this.write_pointer(ptr, dest)
145 }
146 SpecialAllocatorMethod::Dealloc => {
147 let [ptr, old_size, align] =
148 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
149 let ptr = this.read_pointer(ptr)?;
150 let old_size = this.read_target_usize(old_size)?;
151 let align = this.read_target_usize(align)?;
152
153 this.deallocate_ptr(
155 ptr,
156 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
157 MiriMemoryKind::Rust.into(),
158 )
159 }
160 SpecialAllocatorMethod::Realloc => {
161 let [ptr, old_size, align, new_size] =
162 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
163 let ptr = this.read_pointer(ptr)?;
164 let old_size = this.read_target_usize(old_size)?;
165 let align = this.read_target_usize(align)?;
166 let new_size = this.read_target_usize(new_size)?;
167 this.check_rust_alloc_request(new_size, align)?;
170
171 let align = Align::from_bytes(align).unwrap();
172 let new_ptr = this.reallocate_ptr(
173 ptr,
174 Some((Size::from_bytes(old_size), align)),
175 Size::from_bytes(new_size),
176 align,
177 MiriMemoryKind::Rust.into(),
178 AllocInit::Uninit,
179 )?;
180 this.write_pointer(new_ptr, dest)
181 }
182 }
183 }
184
185 fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> {
186 let this = self.eval_context_mut();
187 let align = this.malloc_align(size);
188 let ptr =
189 this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?;
190 interp_ok(ptr.into())
191 }
192
193 fn posix_memalign(
194 &mut self,
195 memptr: &OpTy<'tcx>,
196 align: &OpTy<'tcx>,
197 size: &OpTy<'tcx>,
198 ) -> InterpResult<'tcx, Scalar> {
199 let this = self.eval_context_mut();
200 let memptr = this.deref_pointer_as(memptr, this.machine.layouts.mut_raw_ptr)?;
201 let align = this.read_target_usize(align)?;
202 let size = this.read_target_usize(size)?;
203
204 if !align.is_power_of_two() || align < this.pointer_size().bytes() {
207 interp_ok(this.eval_libc("EINVAL"))
208 } else {
209 let ptr = this.allocate_ptr(
210 Size::from_bytes(size),
211 Align::from_bytes(align).unwrap(),
212 MiriMemoryKind::C.into(),
213 AllocInit::Uninit,
214 )?;
215 this.write_pointer(ptr, &memptr)?;
216 interp_ok(Scalar::from_i32(0))
217 }
218 }
219
220 fn free(&mut self, ptr: Pointer) -> InterpResult<'tcx> {
221 let this = self.eval_context_mut();
222 if !this.ptr_is_null(ptr)? {
223 this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?;
224 }
225 interp_ok(())
226 }
227
228 fn realloc(&mut self, old_ptr: Pointer, new_size: u64) -> InterpResult<'tcx, Pointer> {
229 let this = self.eval_context_mut();
230 let new_align = this.malloc_align(new_size);
231 if this.ptr_is_null(old_ptr)? {
232 self.malloc(new_size, AllocInit::Uninit)
234 } else {
235 if new_size == 0 {
236 throw_ub_format!("`realloc` with a size of zero");
239 } else {
240 let new_ptr = this.reallocate_ptr(
241 old_ptr,
242 None,
243 Size::from_bytes(new_size),
244 new_align,
245 MiriMemoryKind::C.into(),
246 AllocInit::Uninit,
247 )?;
248 interp_ok(new_ptr.into())
249 }
250 }
251 }
252
253 fn aligned_alloc(
254 &mut self,
255 align: &OpTy<'tcx>,
256 size: &OpTy<'tcx>,
257 ) -> InterpResult<'tcx, Pointer> {
258 let this = self.eval_context_mut();
259 let align = this.read_target_usize(align)?;
260 let size = this.read_target_usize(size)?;
261
262 match size.checked_rem(align) {
281 Some(0) if align.is_power_of_two() => {
282 let align = align.max(this.malloc_align(size).bytes());
283 let ptr = this.allocate_ptr(
284 Size::from_bytes(size),
285 Align::from_bytes(align).unwrap(),
286 MiriMemoryKind::C.into(),
287 AllocInit::Uninit,
288 )?;
289 interp_ok(ptr.into())
290 }
291 _ => interp_ok(Pointer::null()),
292 }
293 }
294}