1use rustc_abi::Size;
18use rustc_target::spec::Os;
19
20use crate::*;
21
22impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
23pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
24 fn mmap(
25 &mut self,
26 addr: &OpTy<'tcx>,
27 length: &OpTy<'tcx>,
28 prot: &OpTy<'tcx>,
29 flags: &OpTy<'tcx>,
30 fd: &OpTy<'tcx>,
31 offset: i128,
32 ) -> InterpResult<'tcx, Scalar> {
33 let this = self.eval_context_mut();
34
35 let addr = this.read_target_usize(addr)?;
37 let length = this.read_target_usize(length)?;
38 let prot = this.read_scalar(prot)?.to_i32()?;
39 let flags = this.read_scalar(flags)?.to_i32()?;
40 let fd = this.read_scalar(fd)?.to_i32()?;
41
42 let map_private = this.eval_libc_i32("MAP_PRIVATE");
43 let map_anonymous = this.eval_libc_i32("MAP_ANONYMOUS");
44 let map_shared = this.eval_libc_i32("MAP_SHARED");
45 let map_fixed = this.eval_libc_i32("MAP_FIXED");
46
47 if this.frame_in_std()
50 && matches!(&this.tcx.sess.target.os, Os::MacOs | Os::Solaris | Os::Illumos)
51 && (flags & map_fixed) != 0
52 {
53 return interp_ok(Scalar::from_maybe_pointer(Pointer::without_provenance(addr), this));
54 }
55
56 if (flags & (map_private | map_shared)).count_ones() != 1 {
58 this.set_last_error(LibcError("EINVAL"))?;
59 return interp_ok(this.eval_libc("MAP_FAILED"));
60 }
61 if length == 0 {
62 this.set_last_error(LibcError("EINVAL"))?;
63 return interp_ok(this.eval_libc("MAP_FAILED"));
64 }
65
66 if fd != -1 {
70 throw_unsup_format!("Miri does not support file-backed memory mappings");
71 }
72
73 if flags & map_fixed != 0 {
75 throw_unsup_format!(
76 "Miri does not support calls to mmap with MAP_FIXED as part of the flags argument",
77 );
78 }
79
80 verify_prot(this, prot)?;
81
82 if flags != map_private | map_anonymous {
85 throw_unsup_format!(
86 "Miri only supports calls to mmap which set the flags argument to \
87 MAP_PRIVATE|MAP_ANONYMOUS",
88 );
89 }
90
91 if offset != 0 {
93 throw_unsup_format!("Miri does not support non-zero offsets to mmap");
94 }
95
96 let align = this.machine.page_align();
97 let Some(map_length) = round_up_to_page_size(this, length) else {
98 this.set_last_error(LibcError("EINVAL"))?;
99 return interp_ok(this.eval_libc("MAP_FAILED"));
100 };
101
102 let ptr = this.allocate_ptr(
103 Size::from_bytes(map_length),
104 align,
105 MiriMemoryKind::Mmap.into(),
106 AllocInit::Zero,
108 )?;
109
110 interp_ok(Scalar::from_pointer(ptr, this))
111 }
112
113 fn munmap(&mut self, addr: &OpTy<'tcx>, length: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
114 let this = self.eval_context_mut();
115
116 let addr = this.read_pointer(addr)?;
117 let length = this.read_target_usize(length)?;
118
119 if !addr.addr().bytes().is_multiple_of(this.machine.page_size) {
122 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
123 }
124
125 let Some(length) = round_up_to_page_size(this, length) else {
126 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
127 };
128
129 let length = Size::from_bytes(length);
130 this.deallocate_ptr(
131 addr,
132 Some((length, this.machine.page_align())),
133 MemoryKind::Machine(MiriMemoryKind::Mmap),
134 )?;
135
136 interp_ok(Scalar::from_i32(0))
137 }
138
139 fn mprotect(
140 &mut self,
141 addr: &OpTy<'tcx>,
142 length: &OpTy<'tcx>,
143 prot: &OpTy<'tcx>,
144 ) -> InterpResult<'tcx, Scalar> {
145 let this = self.eval_context_mut();
146
147 let addr = this.read_pointer(addr)?;
148 let length = this.read_target_usize(length)?;
149 let prot = this.read_scalar(prot)?.to_i32()?;
150
151 if !addr.addr().bytes().is_multiple_of(this.machine.page_size) {
153 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
154 }
155
156 verify_prot(this, prot)?;
157
158 let Some(length) = round_up_to_page_size(this, length) else {
160 return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM"));
161 };
162 this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess)
164 .map_err_kind(|_| err_ub_format!("`mprotect` called on out-of-bounds memory"))?;
165
166 interp_ok(Scalar::from_i32(0))
174 }
175
176 fn madvise(
177 &mut self,
178 addr: &OpTy<'tcx>,
179 length: &OpTy<'tcx>,
180 advice: &OpTy<'tcx>,
181 ) -> InterpResult<'tcx, Scalar> {
182 let this = self.eval_context_mut();
183
184 let addr = this.read_pointer(addr)?;
185 let length = this.read_target_usize(length)?;
186 let advise = this.read_scalar(advice)?.to_i32()?;
187
188 if !addr.addr().bytes().is_multiple_of(this.machine.page_size) {
190 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
191 }
192
193 let madv_normal = this.eval_libc_i32("MADV_NORMAL");
195 let madv_random = this.eval_libc_i32("MADV_RANDOM");
196 let madv_sequential = this.eval_libc_i32("MADV_SEQUENTIAL");
197 let madv_willneed = this.eval_libc_i32("MADV_WILLNEED");
198 if advise != madv_normal
199 && advise != madv_random
200 && advise != madv_sequential
201 && advise != madv_willneed
202 {
203 throw_unsup_format!(
204 "Miri does not support calls to madvise with advice other than MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, or MADV_WILLNEED",
205 );
206 }
207
208 let Some(length) = round_up_to_page_size(this, length) else {
210 return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM"));
211 };
212 this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess)
214 .map_err_kind(|_| err_ub_format!("`madvise` called on out-of-bounds memory"))?;
215
216 interp_ok(Scalar::from_i32(0))
218 }
219}
220
221fn round_up_to_page_size(this: &MiriInterpCx<'_>, length: u64) -> Option<u64> {
222 length
223 .checked_next_multiple_of(this.machine.page_size)
224 .filter(|length| *length <= this.target_isize_max().try_into().unwrap())
225}
226
227fn verify_prot<'tcx>(this: &mut MiriInterpCx<'tcx>, prot: i32) -> InterpResult<'tcx> {
228 let prot_read = this.eval_libc_i32("PROT_READ");
229 let prot_write = this.eval_libc_i32("PROT_WRITE");
230
231 if prot != prot_read | prot_write {
233 throw_unsup_format!(
234 "Miri does not support calls to mmap/mprotect with protections other than \
235 PROT_READ|PROT_WRITE",
236 );
237 }
238
239 interp_ok(())
240}