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;
6
7use crate::*;
8
9impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
10pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11 fn malloc_align(&self, size: u64) -> Align {
13 let this = self.eval_context_ref();
14 let os = this.tcx.sess.target.os.as_ref();
22 let max_fundamental_align = match this.tcx.sess.target.arch.as_ref() {
23 "riscv32" if matches!(os, "espidf" | "zkvm") => 4,
24 "xtensa" if matches!(os, "espidf") => 4,
25 "x86" | "arm" | "m68k" | "csky" | "loongarch32" | "mips" | "mips32r6" | "powerpc"
26 | "powerpc64" | "sparc" | "wasm32" | "hexagon" | "riscv32" | "xtensa" => 8,
27 "x86_64" | "aarch64" | "arm64ec" | "loongarch64" | "mips64" | "mips64r6" | "s390x"
28 | "sparc64" | "riscv64" | "wasm64" => 16,
29 arch => bug!("unsupported target architecture for malloc: `{}`", arch),
30 };
31 if size >= max_fundamental_align {
40 return Align::from_bytes(max_fundamental_align).unwrap();
41 }
42 if size == 0 {
44 return Align::ONE;
45 }
46 fn prev_power_of_two(x: u64) -> u64 {
48 let next_pow2 = x.next_power_of_two();
49 if next_pow2 == x {
50 x
52 } else {
53 next_pow2 / 2
55 }
56 }
57 Align::from_bytes(prev_power_of_two(size)).unwrap()
58 }
59
60 fn check_rust_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> {
63 let this = self.eval_context_ref();
64 if size == 0 {
65 throw_ub_format!("creating allocation with size 0");
66 }
67 if size > this.max_size_of_val().bytes() {
68 throw_ub_format!("creating an allocation larger than half the address space");
69 }
70 if let Err(e) = Align::from_bytes(align) {
71 match e {
72 AlignFromBytesError::TooLarge(_) => {
73 throw_unsup_format!(
74 "creating allocation with alignment {align} exceeding rustc's maximum \
75 supported value"
76 );
77 }
78 AlignFromBytesError::NotPowerOfTwo(_) => {
79 throw_ub_format!("creating allocation with non-power-of-two alignment {align}");
80 }
81 }
82 }
83
84 interp_ok(())
85 }
86
87 fn rust_special_allocator_method(
88 &mut self,
89 method: SpecialAllocatorMethod,
90 link_name: Symbol,
91 abi: &FnAbi<'tcx, Ty<'tcx>>,
92 args: &[OpTy<'tcx>],
93 dest: &PlaceTy<'tcx>,
94 ) -> InterpResult<'tcx> {
95 let this = self.eval_context_mut();
96
97 match method {
98 SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => {
99 let [size, align] =
100 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
101 let size = this.read_target_usize(size)?;
102 let align = this.read_target_usize(align)?;
103
104 this.check_rust_alloc_request(size, align)?;
105
106 let ptr = this.allocate_ptr(
107 Size::from_bytes(size),
108 Align::from_bytes(align).unwrap(),
109 MiriMemoryKind::Rust.into(),
110 if matches!(method, SpecialAllocatorMethod::AllocZeroed) {
111 AllocInit::Zero
112 } else {
113 AllocInit::Uninit
114 },
115 )?;
116
117 this.write_pointer(ptr, dest)
118 }
119 SpecialAllocatorMethod::Dealloc => {
120 let [ptr, old_size, align] =
121 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
122 let ptr = this.read_pointer(ptr)?;
123 let old_size = this.read_target_usize(old_size)?;
124 let align = this.read_target_usize(align)?;
125
126 this.deallocate_ptr(
128 ptr,
129 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
130 MiriMemoryKind::Rust.into(),
131 )
132 }
133 SpecialAllocatorMethod::Realloc => {
134 let [ptr, old_size, align, new_size] =
135 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
136 let ptr = this.read_pointer(ptr)?;
137 let old_size = this.read_target_usize(old_size)?;
138 let align = this.read_target_usize(align)?;
139 let new_size = this.read_target_usize(new_size)?;
140 this.check_rust_alloc_request(new_size, align)?;
143
144 let align = Align::from_bytes(align).unwrap();
145 let new_ptr = this.reallocate_ptr(
146 ptr,
147 Some((Size::from_bytes(old_size), align)),
148 Size::from_bytes(new_size),
149 align,
150 MiriMemoryKind::Rust.into(),
151 AllocInit::Uninit,
152 )?;
153 this.write_pointer(new_ptr, dest)
154 }
155 }
156 }
157
158 fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> {
159 let this = self.eval_context_mut();
160 let align = this.malloc_align(size);
161 let ptr =
162 this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?;
163 interp_ok(ptr.into())
164 }
165
166 fn posix_memalign(
167 &mut self,
168 memptr: &OpTy<'tcx>,
169 align: &OpTy<'tcx>,
170 size: &OpTy<'tcx>,
171 ) -> InterpResult<'tcx, Scalar> {
172 let this = self.eval_context_mut();
173 let memptr = this.deref_pointer_as(memptr, this.machine.layouts.mut_raw_ptr)?;
174 let align = this.read_target_usize(align)?;
175 let size = this.read_target_usize(size)?;
176
177 if !align.is_power_of_two() || align < this.pointer_size().bytes() {
180 interp_ok(this.eval_libc("EINVAL"))
181 } else {
182 let ptr = this.allocate_ptr(
183 Size::from_bytes(size),
184 Align::from_bytes(align).unwrap(),
185 MiriMemoryKind::C.into(),
186 AllocInit::Uninit,
187 )?;
188 this.write_pointer(ptr, &memptr)?;
189 interp_ok(Scalar::from_i32(0))
190 }
191 }
192
193 fn free(&mut self, ptr: Pointer) -> InterpResult<'tcx> {
194 let this = self.eval_context_mut();
195 if !this.ptr_is_null(ptr)? {
196 this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?;
197 }
198 interp_ok(())
199 }
200
201 fn realloc(&mut self, old_ptr: Pointer, new_size: u64) -> InterpResult<'tcx, Pointer> {
202 let this = self.eval_context_mut();
203 let new_align = this.malloc_align(new_size);
204 if this.ptr_is_null(old_ptr)? {
205 self.malloc(new_size, AllocInit::Uninit)
207 } else {
208 if new_size == 0 {
209 throw_ub_format!("`realloc` with a size of zero");
212 } else {
213 let new_ptr = this.reallocate_ptr(
214 old_ptr,
215 None,
216 Size::from_bytes(new_size),
217 new_align,
218 MiriMemoryKind::C.into(),
219 AllocInit::Uninit,
220 )?;
221 interp_ok(new_ptr.into())
222 }
223 }
224 }
225
226 fn aligned_alloc(
227 &mut self,
228 align: &OpTy<'tcx>,
229 size: &OpTy<'tcx>,
230 ) -> InterpResult<'tcx, Pointer> {
231 let this = self.eval_context_mut();
232 let align = this.read_target_usize(align)?;
233 let size = this.read_target_usize(size)?;
234
235 match size.checked_rem(align) {
254 Some(0) if align.is_power_of_two() => {
255 let align = align.max(this.malloc_align(size).bytes());
256 let ptr = this.allocate_ptr(
257 Size::from_bytes(size),
258 Align::from_bytes(align).unwrap(),
259 MiriMemoryKind::C.into(),
260 AllocInit::Uninit,
261 )?;
262 interp_ok(ptr.into())
263 }
264 _ => interp_ok(Pointer::null()),
265 }
266 }
267}