1use rustc_abi::Size;
18
19use crate::*;
20
21impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
22pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
23 fn mmap(
24 &mut self,
25 addr: &OpTy<'tcx>,
26 length: &OpTy<'tcx>,
27 prot: &OpTy<'tcx>,
28 flags: &OpTy<'tcx>,
29 fd: &OpTy<'tcx>,
30 offset: i128,
31 ) -> InterpResult<'tcx, Scalar> {
32 let this = self.eval_context_mut();
33
34 let addr = this.read_target_usize(addr)?;
36 let length = this.read_target_usize(length)?;
37 let prot = this.read_scalar(prot)?.to_i32()?;
38 let flags = this.read_scalar(flags)?.to_i32()?;
39 let fd = this.read_scalar(fd)?.to_i32()?;
40
41 let map_private = this.eval_libc_i32("MAP_PRIVATE");
42 let map_anonymous = this.eval_libc_i32("MAP_ANONYMOUS");
43 let map_shared = this.eval_libc_i32("MAP_SHARED");
44 let map_fixed = this.eval_libc_i32("MAP_FIXED");
45
46 if this.frame_in_std()
49 && matches!(&*this.tcx.sess.target.os, "macos" | "solaris" | "illumos")
50 && (flags & map_fixed) != 0
51 {
52 return interp_ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this));
53 }
54
55 let prot_read = this.eval_libc_i32("PROT_READ");
56 let prot_write = this.eval_libc_i32("PROT_WRITE");
57
58 if (flags & (map_private | map_shared)).count_ones() != 1 {
60 this.set_last_error(LibcError("EINVAL"))?;
61 return interp_ok(this.eval_libc("MAP_FAILED"));
62 }
63 if length == 0 {
64 this.set_last_error(LibcError("EINVAL"))?;
65 return interp_ok(this.eval_libc("MAP_FAILED"));
66 }
67
68 if fd != -1 {
72 throw_unsup_format!("Miri does not support file-backed memory mappings");
73 }
74
75 if flags & map_fixed != 0 {
77 throw_unsup_format!(
78 "Miri does not support calls to mmap with MAP_FIXED as part of the flags argument",
79 );
80 }
81
82 if prot != prot_read | prot_write {
84 throw_unsup_format!(
85 "Miri does not support calls to mmap with protections other than \
86 PROT_READ|PROT_WRITE",
87 );
88 }
89
90 if flags != map_private | map_anonymous {
93 throw_unsup_format!(
94 "Miri only supports calls to mmap which set the flags argument to \
95 MAP_PRIVATE|MAP_ANONYMOUS",
96 );
97 }
98
99 if offset != 0 {
101 throw_unsup_format!("Miri does not support non-zero offsets to mmap");
102 }
103
104 let align = this.machine.page_align();
105 let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else {
106 this.set_last_error(LibcError("EINVAL"))?;
107 return interp_ok(this.eval_libc("MAP_FAILED"));
108 };
109 if map_length > this.target_usize_max() {
110 this.set_last_error(LibcError("EINVAL"))?;
111 return interp_ok(this.eval_libc("MAP_FAILED"));
112 }
113
114 let ptr = this.allocate_ptr(
115 Size::from_bytes(map_length),
116 align,
117 MiriMemoryKind::Mmap.into(),
118 AllocInit::Zero,
120 )?;
121
122 interp_ok(Scalar::from_pointer(ptr, this))
123 }
124
125 fn munmap(&mut self, addr: &OpTy<'tcx>, length: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
126 let this = self.eval_context_mut();
127
128 let addr = this.read_pointer(addr)?;
129 let length = this.read_target_usize(length)?;
130
131 #[expect(clippy::arithmetic_side_effects)] if addr.addr().bytes() % this.machine.page_size != 0 {
135 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
136 }
137
138 let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else {
139 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
140 };
141 if length > this.target_usize_max() {
142 this.set_last_error(LibcError("EINVAL"))?;
143 return interp_ok(this.eval_libc("MAP_FAILED"));
144 }
145
146 let length = Size::from_bytes(length);
147 this.deallocate_ptr(
148 addr,
149 Some((length, this.machine.page_align())),
150 MemoryKind::Machine(MiriMemoryKind::Mmap),
151 )?;
152
153 interp_ok(Scalar::from_i32(0))
154 }
155}