1use std::io;
5use std::io::ErrorKind;
6
7use rustc_abi::Size;
8
9use crate::helpers::check_min_vararg_count;
10use crate::shims::files::FileDescription;
11use crate::shims::unix::linux_like::epoll::EpollReadyEvents;
12use crate::shims::unix::*;
13use crate::*;
14
15#[derive(Debug, Clone, Copy, Eq, PartialEq)]
16pub(crate) enum FlockOp {
17 SharedLock { nonblocking: bool },
18 ExclusiveLock { nonblocking: bool },
19 Unlock,
20}
21
22pub trait UnixFileDescription: FileDescription {
24 fn pread<'tcx>(
28 &self,
29 _communicate_allowed: bool,
30 _offset: u64,
31 _ptr: Pointer,
32 _len: usize,
33 _ecx: &mut MiriInterpCx<'tcx>,
34 _finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
35 ) -> InterpResult<'tcx> {
36 throw_unsup_format!("cannot pread from {}", self.name());
37 }
38
39 fn pwrite<'tcx>(
44 &self,
45 _communicate_allowed: bool,
46 _ptr: Pointer,
47 _len: usize,
48 _offset: u64,
49 _ecx: &mut MiriInterpCx<'tcx>,
50 _finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
51 ) -> InterpResult<'tcx> {
52 throw_unsup_format!("cannot pwrite to {}", self.name());
53 }
54
55 fn flock<'tcx>(
56 &self,
57 _communicate_allowed: bool,
58 _op: FlockOp,
59 ) -> InterpResult<'tcx, io::Result<()>> {
60 throw_unsup_format!("cannot flock {}", self.name());
61 }
62
63 fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
65 throw_unsup_format!("{}: epoll does not support this file description", self.name());
66 }
67}
68
69impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
70pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
71 fn dup(&mut self, old_fd_num: i32) -> InterpResult<'tcx, Scalar> {
72 let this = self.eval_context_mut();
73
74 let Some(fd) = this.machine.fds.get(old_fd_num) else {
75 return this.set_last_error_and_return_i32(LibcError("EBADF"));
76 };
77 interp_ok(Scalar::from_i32(this.machine.fds.insert(fd)))
78 }
79
80 fn dup2(&mut self, old_fd_num: i32, new_fd_num: i32) -> InterpResult<'tcx, Scalar> {
81 let this = self.eval_context_mut();
82
83 let Some(fd) = this.machine.fds.get(old_fd_num) else {
84 return this.set_last_error_and_return_i32(LibcError("EBADF"));
85 };
86 if new_fd_num != old_fd_num {
87 if let Some(old_new_fd) = this.machine.fds.fds.insert(new_fd_num, fd) {
90 old_new_fd.close_ref(this.machine.communicate(), this)?.ok();
92 }
93 }
94 interp_ok(Scalar::from_i32(new_fd_num))
95 }
96
97 fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> {
98 let this = self.eval_context_mut();
99 let Some(fd) = this.machine.fds.get(fd_num) else {
100 return this.set_last_error_and_return_i32(LibcError("EBADF"));
101 };
102
103 let lock_sh = this.eval_libc_i32("LOCK_SH");
105 let lock_ex = this.eval_libc_i32("LOCK_EX");
106 let lock_nb = this.eval_libc_i32("LOCK_NB");
107 let lock_un = this.eval_libc_i32("LOCK_UN");
108
109 use FlockOp::*;
110 let parsed_op = if op == lock_sh {
111 SharedLock { nonblocking: false }
112 } else if op == lock_sh | lock_nb {
113 SharedLock { nonblocking: true }
114 } else if op == lock_ex {
115 ExclusiveLock { nonblocking: false }
116 } else if op == lock_ex | lock_nb {
117 ExclusiveLock { nonblocking: true }
118 } else if op == lock_un {
119 Unlock
120 } else {
121 throw_unsup_format!("unsupported flags {:#x}", op);
122 };
123
124 let result = fd.as_unix(this).flock(this.machine.communicate(), parsed_op)?;
125 let result = result.map(|()| 0i32);
127 interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
128 }
129
130 fn fcntl(
131 &mut self,
132 fd_num: &OpTy<'tcx>,
133 cmd: &OpTy<'tcx>,
134 varargs: &[OpTy<'tcx>],
135 ) -> InterpResult<'tcx, Scalar> {
136 let this = self.eval_context_mut();
137
138 let fd_num = this.read_scalar(fd_num)?.to_i32()?;
139 let cmd = this.read_scalar(cmd)?.to_i32()?;
140
141 let f_getfd = this.eval_libc_i32("F_GETFD");
142 let f_dupfd = this.eval_libc_i32("F_DUPFD");
143 let f_dupfd_cloexec = this.eval_libc_i32("F_DUPFD_CLOEXEC");
144 let f_getfl = this.eval_libc_i32("F_GETFL");
145 let f_setfl = this.eval_libc_i32("F_SETFL");
146
147 match cmd {
149 cmd if cmd == f_getfd => {
150 if !this.machine.fds.is_fd_num(fd_num) {
155 this.set_last_error_and_return_i32(LibcError("EBADF"))
156 } else {
157 interp_ok(this.eval_libc("FD_CLOEXEC"))
158 }
159 }
160 cmd if cmd == f_dupfd || cmd == f_dupfd_cloexec => {
161 let cmd_name = if cmd == f_dupfd {
166 "fcntl(fd, F_DUPFD, ...)"
167 } else {
168 "fcntl(fd, F_DUPFD_CLOEXEC, ...)"
169 };
170
171 let [start] = check_min_vararg_count(cmd_name, varargs)?;
172 let start = this.read_scalar(start)?.to_i32()?;
173
174 if let Some(fd) = this.machine.fds.get(fd_num) {
175 interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start)))
176 } else {
177 this.set_last_error_and_return_i32(LibcError("EBADF"))
178 }
179 }
180 cmd if cmd == f_getfl => {
181 let Some(fd) = this.machine.fds.get(fd_num) else {
183 return this.set_last_error_and_return_i32(LibcError("EBADF"));
184 };
185
186 fd.get_flags(this)
187 }
188 cmd if cmd == f_setfl => {
189 let Some(fd) = this.machine.fds.get(fd_num) else {
191 return this.set_last_error_and_return_i32(LibcError("EBADF"));
192 };
193
194 let [flag] = check_min_vararg_count("fcntl(fd, F_SETFL, ...)", varargs)?;
195 let flag = this.read_scalar(flag)?.to_i32()?;
196
197 fd.set_flags(flag, this)
198 }
199 cmd if this.tcx.sess.target.os == "macos"
200 && cmd == this.eval_libc_i32("F_FULLFSYNC") =>
201 {
202 if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
204 this.reject_in_isolation("`fcntl`", reject_with)?;
205 return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
206 }
207
208 this.ffullsync_fd(fd_num)
209 }
210 cmd => {
211 throw_unsup_format!("fcntl: unsupported command {cmd:#x}");
212 }
213 }
214 }
215
216 fn close(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
217 let this = self.eval_context_mut();
218
219 let fd_num = this.read_scalar(fd_op)?.to_i32()?;
220
221 let Some(fd) = this.machine.fds.remove(fd_num) else {
222 return this.set_last_error_and_return_i32(LibcError("EBADF"));
223 };
224 let result = fd.close_ref(this.machine.communicate(), this)?;
225 let result = result.map(|()| 0i32);
227 interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
228 }
229
230 fn read(
236 &mut self,
237 fd_num: i32,
238 buf: Pointer,
239 count: u64,
240 offset: Option<i128>,
241 dest: &MPlaceTy<'tcx>,
242 ) -> InterpResult<'tcx> {
243 let this = self.eval_context_mut();
244
245 trace!("Reading from FD {}, size {}", fd_num, count);
248
249 this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccess)?;
251
252 let count = count
255 .min(u64::try_from(this.target_isize_max()).unwrap())
256 .min(u64::try_from(isize::MAX).unwrap());
257 let count = usize::try_from(count).unwrap(); let communicate = this.machine.communicate();
259
260 let Some(fd) = this.machine.fds.get(fd_num) else {
262 trace!("read: FD not found");
263 return this.set_last_error_and_return(LibcError("EBADF"), dest);
264 };
265
266 trace!("read: FD mapped to {fd:?}");
267 let finish = {
272 let dest = dest.clone();
273 callback!(
274 @capture<'tcx> {
275 count: usize,
276 dest: MPlaceTy<'tcx>,
277 }
278 |this, result: Result<usize, IoError>| {
279 match result {
280 Ok(read_size) => {
281 assert!(read_size <= count);
282 this.write_int(u64::try_from(read_size).unwrap(), &dest)
284 }
285 Err(e) => {
286 this.set_last_error_and_return(e, &dest)
287 }
288 }}
289 )
290 };
291 match offset {
292 None => fd.read(communicate, buf, count, this, finish)?,
293 Some(offset) => {
294 let Ok(offset) = u64::try_from(offset) else {
295 return this.set_last_error_and_return(LibcError("EINVAL"), dest);
296 };
297 fd.as_unix(this).pread(communicate, offset, buf, count, this, finish)?
298 }
299 };
300 interp_ok(())
301 }
302
303 fn write(
304 &mut self,
305 fd_num: i32,
306 buf: Pointer,
307 count: u64,
308 offset: Option<i128>,
309 dest: &MPlaceTy<'tcx>,
310 ) -> InterpResult<'tcx> {
311 let this = self.eval_context_mut();
312
313 this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccess)?;
317
318 let count = count
321 .min(u64::try_from(this.target_isize_max()).unwrap())
322 .min(u64::try_from(isize::MAX).unwrap());
323 let count = usize::try_from(count).unwrap(); let communicate = this.machine.communicate();
325
326 let Some(fd) = this.machine.fds.get(fd_num) else {
328 return this.set_last_error_and_return(LibcError("EBADF"), dest);
329 };
330
331 let finish = {
332 let dest = dest.clone();
333 callback!(
334 @capture<'tcx> {
335 count: usize,
336 dest: MPlaceTy<'tcx>,
337 }
338 |this, result: Result<usize, IoError>| {
339 match result {
340 Ok(write_size) => {
341 assert!(write_size <= count);
342 this.write_int(u64::try_from(write_size).unwrap(), &dest)
344 }
345 Err(e) => {
346 this.set_last_error_and_return(e, &dest)
347 }
348 }}
349 )
350 };
351 match offset {
352 None => fd.write(communicate, buf, count, this, finish)?,
353 Some(offset) => {
354 let Ok(offset) = u64::try_from(offset) else {
355 return this.set_last_error_and_return(LibcError("EINVAL"), dest);
356 };
357 fd.as_unix(this).pwrite(communicate, buf, count, offset, this, finish)?
358 }
359 };
360 interp_ok(())
361 }
362}