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 let prot_read = this.eval_libc_i32("PROT_READ");
57 let prot_write = this.eval_libc_i32("PROT_WRITE");
58
59 if (flags & (map_private | map_shared)).count_ones() != 1 {
61 this.set_last_error(LibcError("EINVAL"))?;
62 return interp_ok(this.eval_libc("MAP_FAILED"));
63 }
64 if length == 0 {
65 this.set_last_error(LibcError("EINVAL"))?;
66 return interp_ok(this.eval_libc("MAP_FAILED"));
67 }
68
69 if fd != -1 {
73 throw_unsup_format!("Miri does not support file-backed memory mappings");
74 }
75
76 if flags & map_fixed != 0 {
78 throw_unsup_format!(
79 "Miri does not support calls to mmap with MAP_FIXED as part of the flags argument",
80 );
81 }
82
83 if prot != prot_read | prot_write {
85 throw_unsup_format!(
86 "Miri does not support calls to mmap with protections other than \
87 PROT_READ|PROT_WRITE",
88 );
89 }
90
91 if flags != map_private | map_anonymous {
94 throw_unsup_format!(
95 "Miri only supports calls to mmap which set the flags argument to \
96 MAP_PRIVATE|MAP_ANONYMOUS",
97 );
98 }
99
100 if offset != 0 {
102 throw_unsup_format!("Miri does not support non-zero offsets to mmap");
103 }
104
105 let align = this.machine.page_align();
106 let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else {
107 this.set_last_error(LibcError("EINVAL"))?;
108 return interp_ok(this.eval_libc("MAP_FAILED"));
109 };
110 if map_length > this.target_usize_max() {
111 this.set_last_error(LibcError("EINVAL"))?;
112 return interp_ok(this.eval_libc("MAP_FAILED"));
113 }
114
115 let ptr = this.allocate_ptr(
116 Size::from_bytes(map_length),
117 align,
118 MiriMemoryKind::Mmap.into(),
119 AllocInit::Zero,
121 )?;
122
123 interp_ok(Scalar::from_pointer(ptr, this))
124 }
125
126 fn munmap(&mut self, addr: &OpTy<'tcx>, length: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
127 let this = self.eval_context_mut();
128
129 let addr = this.read_pointer(addr)?;
130 let length = this.read_target_usize(length)?;
131
132 if !addr.addr().bytes().is_multiple_of(this.machine.page_size) {
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}