1use std::io;
5use std::io::ErrorKind;
6
7use rand::RngExt;
8use rustc_abi::{Align, Size};
9use rustc_target::spec::Os;
10
11use crate::shims::files::{BIG_ACCESS, DynFileDescriptionRef, FdNum, FileDescription};
12use crate::shims::sig::Varargs;
13use crate::shims::unix::socket::UnixSocketFileDescription;
14use crate::shims::unix::*;
15use crate::shims::{FdTable, FileDescriptionRef};
16use crate::*;
17
18#[derive(Debug, Clone, Copy, Eq, PartialEq)]
19pub enum FlockOp {
20 SharedLock { nonblocking: bool },
21 ExclusiveLock { nonblocking: bool },
22 Unlock,
23}
24
25pub trait UnixFileDescription: FileDescription {
27 fn pread<'tcx>(
31 &self,
32 _communicate_allowed: bool,
33 _ptr: Pointer,
34 _len: u64,
35 _offset: u64,
36 _ecx: &mut MiriInterpCx<'tcx>,
37 _finish: DynMachineCallback<'tcx, Result<u64, IoError>>,
38 ) -> InterpResult<'tcx> {
39 throw_unsup_format!("cannot pread from {}", self.name());
40 }
41
42 fn pwrite<'tcx>(
47 &self,
48 _communicate_allowed: bool,
49 _ptr: Pointer,
50 _len: u64,
51 _offset: u64,
52 _ecx: &mut MiriInterpCx<'tcx>,
53 _finish: DynMachineCallback<'tcx, Result<u64, IoError>>,
54 ) -> InterpResult<'tcx> {
55 throw_unsup_format!("cannot pwrite to {}", self.name());
56 }
57
58 fn flock<'tcx>(
59 &self,
60 _communicate_allowed: bool,
61 _op: FlockOp,
62 ) -> InterpResult<'tcx, io::Result<()>> {
63 throw_unsup_format!("cannot flock {}", self.name());
64 }
65
66 fn get_flags<'tcx>(&self, _ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> {
68 throw_unsup_format!("fcntl: {} is not supported for F_GETFL", self.name());
69 }
70
71 fn set_flags<'tcx>(
73 &self,
74 _flag: i32,
75 _ecx: &mut MiriInterpCx<'tcx>,
76 ) -> InterpResult<'tcx, Scalar> {
77 throw_unsup_format!("fcntl: {} is not supported for F_SETFL", self.name());
78 }
79
80 fn as_socket(
82 self: FileDescriptionRef<Self>,
83 ) -> Option<FileDescriptionRef<dyn UnixSocketFileDescription>> {
84 None
85 }
86}
87
88impl UnixFileDescription for shims::files::Stdin {}
89impl UnixFileDescription for shims::files::Stdout {}
90impl UnixFileDescription for shims::files::Stderr {}
91impl UnixFileDescription for shims::files::NullOutput {}
92
93impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
94pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
95 fn close(&mut self, fd_num: FdNum) -> InterpResult<'tcx, Scalar> {
96 let ecx = self.eval_context_mut();
97
98 let Some(fd) = ecx.machine.fds.remove(fd_num) else {
99 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
100 };
101 let fd = fd.as_unix(); if ecx.tcx.sess.target.os == Os::Illumos {
103 if let Some(watched) = fd.readiness_watched() {
115 watched.remove_file_num_interests(fd.id(), fd_num);
116 }
117 }
118 drop(fd);
119 interp_ok(Scalar::from_i32(0))
122 }
123
124 fn dup(&mut self, old_fd_num: FdNum) -> InterpResult<'tcx, Scalar> {
125 let ecx = self.eval_context_mut();
126
127 let Some(fd) = ecx.machine.fds.get(old_fd_num) else {
128 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
129 };
130 let Some(new_fd) = ecx.machine.fds.insert(fd) else {
131 return ecx.set_errno_and_return_neg1_i32(LibcError("EMFILE"));
132 };
133 interp_ok(Scalar::from_i32(new_fd))
134 }
135
136 fn dup2(&mut self, old_fd_num: FdNum, new_fd_num: FdNum) -> InterpResult<'tcx, Scalar> {
137 let ecx = self.eval_context_mut();
138
139 let Some(fd) = ecx.machine.fds.get(old_fd_num) else {
140 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
141 };
142 if !FdTable::fd_num_valid(new_fd_num) {
143 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
144 }
145 if new_fd_num != old_fd_num {
146 if ecx.machine.fds.get(new_fd_num).is_some() {
147 let ret = ecx.close(new_fd_num)?;
149 assert!(ret.to_i32().unwrap() == 0);
150 }
151 let Some(actual_fd_num) = ecx.machine.fds.insert_with_min_num(fd, new_fd_num) else {
153 panic!("we validated new_fd_num above")
154 };
155 assert_eq!(actual_fd_num, new_fd_num);
156 }
157 interp_ok(Scalar::from_i32(new_fd_num))
158 }
159
160 fn flock(&mut self, fd_num: FdNum, op: i32) -> InterpResult<'tcx, Scalar> {
161 let ecx = self.eval_context_mut();
162 let Some(fd) = ecx.machine.fds.get(fd_num) else {
163 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
164 };
165
166 let lock_sh = ecx.eval_libc_i32("LOCK_SH");
168 let lock_ex = ecx.eval_libc_i32("LOCK_EX");
169 let lock_nb = ecx.eval_libc_i32("LOCK_NB");
170 let lock_un = ecx.eval_libc_i32("LOCK_UN");
171
172 use FlockOp::*;
173 let parsed_op = if op == lock_sh {
174 SharedLock { nonblocking: false }
175 } else if op == lock_sh | lock_nb {
176 SharedLock { nonblocking: true }
177 } else if op == lock_ex {
178 ExclusiveLock { nonblocking: false }
179 } else if op == lock_ex | lock_nb {
180 ExclusiveLock { nonblocking: true }
181 } else if op == lock_un {
182 Unlock
183 } else {
184 throw_unsup_format!("unsupported flags {:#x}", op);
185 };
186
187 let result = fd.as_unix().flock(ecx.machine.communicate(), parsed_op)?;
188 let result = result.map(|()| 0i32);
190 interp_ok(Scalar::from_i32(ecx.try_unwrap_io_result(result)?))
191 }
192
193 fn ioctl(
194 &mut self,
195 fd: &OpTy<'tcx>,
196 op: &OpTy<'tcx>,
197 varargs: Varargs<'tcx, '_>,
198 ) -> InterpResult<'tcx, Scalar> {
199 let ecx = self.eval_context_mut();
200
201 let fd = ecx.read_scalar(fd)?.to_i32()?;
202 let op = ecx.read_scalar(op)?;
203
204 let Some(fd) = ecx.machine.fds.get(fd) else {
205 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
206 };
207
208 let fioclex = ecx.eval_libc("FIOCLEX");
210 let fionclex = ecx.eval_libc("FIONCLEX");
211 let fionbio = ecx.eval_libc("FIONBIO");
212
213 if op == fioclex || op == fionclex {
214 interp_ok(Scalar::from_i32(0))
216 } else if op == fionbio {
217 if !matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android | Os::MacOs | Os::FreeBsd)
221 {
222 throw_unsup_format!(
227 "ioctl: setting FIONBIO is unsupported on {} on target {}",
228 fd.name(),
229 ecx.tcx.sess.target.os
230 );
231 }
232
233 let ([value_ptr], _) = ecx.check_varargs(shim_varargs![*i32], varargs, "ioctl")?;
234 let value = ecx.deref_pointer(&value_ptr)?;
235 let non_block = ecx.read_scalar(&value)?.to_i32()? != 0;
236
237 let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK");
238
239 let unix_fd = fd.as_unix();
244 let mut flags = unix_fd.get_flags(ecx)?.to_i32()?;
245 if non_block {
246 flags |= o_nonblock
248 } else {
249 flags &= !o_nonblock
251 }
252 unix_fd.set_flags(flags & !ecx.setfl_ignored_flags(), ecx)?;
253 interp_ok(Scalar::from_i32(0))
254 } else {
255 throw_unsup_format!("ioctl: unsupported operation {op:#x} on {}", fd.name())
256 }
257 }
258
259 fn fcntl(
260 &mut self,
261 fd_num: &OpTy<'tcx>,
262 cmd: &OpTy<'tcx>,
263 varargs: Varargs<'tcx, '_>,
264 ) -> InterpResult<'tcx, Scalar> {
265 let ecx = self.eval_context_mut();
266
267 let fd_num = ecx.read_scalar(fd_num)?.to_i32()?;
268 let cmd = ecx.read_scalar(cmd)?.to_i32()?;
269
270 let f_getfd = ecx.eval_libc_i32("F_GETFD");
271 let f_dupfd = ecx.eval_libc_i32("F_DUPFD");
272 let f_dupfd_cloexec = ecx.eval_libc_i32("F_DUPFD_CLOEXEC");
273 let f_getfl = ecx.eval_libc_i32("F_GETFL");
274 let f_setfl = ecx.eval_libc_i32("F_SETFL");
275
276 match cmd {
278 cmd if cmd == f_getfd => {
279 if !ecx.machine.fds.is_fd_num(fd_num) {
284 ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"))
285 } else {
286 interp_ok(ecx.eval_libc("FD_CLOEXEC"))
287 }
288 }
289 cmd if cmd == f_dupfd || cmd == f_dupfd_cloexec => {
290 let cmd_name = if cmd == f_dupfd {
295 "fcntl(fd, F_DUPFD, ...)"
296 } else {
297 "fcntl(fd, F_DUPFD_CLOEXEC, ...)"
298 };
299
300 let ([start], _) = ecx.check_varargs(shim_varargs![i32], varargs, cmd_name)?;
301 let start = ecx.read_scalar(&start)?.to_i32()?;
302
303 let Some(fd) = ecx.machine.fds.get(fd_num) else {
304 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
305 };
306 if !FdTable::fd_num_valid(start) {
307 return ecx.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
308 }
309 let Some(new_fd_num) = ecx.machine.fds.insert_with_min_num(fd, start) else {
310 return ecx.set_errno_and_return_neg1_i32(LibcError("EMFILE"));
311 };
312 interp_ok(Scalar::from_i32(new_fd_num))
313 }
314 cmd if cmd == f_getfl => {
315 let Some(fd) = ecx.machine.fds.get(fd_num) else {
317 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
318 };
319
320 fd.as_unix().get_flags(ecx)
321 }
322 cmd if cmd == f_setfl => {
323 let Some(fd) = ecx.machine.fds.get(fd_num) else {
325 return ecx.set_errno_and_return_neg1_i32(LibcError("EBADF"));
326 };
327
328 let ([flag], _) =
329 ecx.check_varargs(shim_varargs![i32], varargs, "fcntl(fd, F_SETFL, ...)")?;
330 let flag = ecx.read_scalar(&flag)?.to_i32()?;
331
332 fd.as_unix().set_flags(flag & !ecx.setfl_ignored_flags(), ecx)
333 }
334 cmd if ecx.tcx.sess.target.os == Os::MacOs
335 && cmd == ecx.eval_libc_i32("F_FULLFSYNC") =>
336 {
337 if let IsolatedOp::Reject(reject_with) = ecx.machine.isolated_op {
339 ecx.reject_in_isolation("`fcntl`", reject_with)?;
340 return ecx.set_errno_and_return_neg1_i32(ErrorKind::PermissionDenied);
341 }
342
343 ecx.ffullsync_fd(fd_num)
344 }
345 cmd if ecx.tcx.sess.target.os == Os::MacOs
346 && ecx.frame_in_std()
347 && cmd == ecx.eval_libc_i32("F_GETPATH") =>
348 {
349 ecx.set_errno_and_return_neg1_i32(LibcError("EOPNOTSUPP"))
351 }
352 cmd if ecx.tcx.sess.target.os == Os::FreeBsd
353 && ecx.frame_in_std()
354 && cmd == ecx.eval_libc_i32("F_KINFO") =>
355 {
356 ecx.set_errno_and_return_neg1_i32(LibcError("EOPNOTSUPP"))
358 }
359 cmd => {
360 throw_unsup_format!("fcntl: unsupported command {cmd:#x}");
361 }
362 }
363 }
364
365 fn read(
371 &mut self,
372 fd_num: i32,
373 buf: Pointer,
374 count: u64,
375 offset: Option<i128>,
376 dest: &MPlaceTy<'tcx>,
377 ) -> InterpResult<'tcx> {
378 let ecx = self.eval_context_mut();
379
380 trace!("Reading from FD {}, size {}", fd_num, count);
383
384 ecx.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccess)?;
386
387 let count = count.clamp_to(..=u64::try_from(ecx.target_isize_max()).unwrap());
390
391 let Some(fd) = ecx.machine.fds.get(fd_num) else {
393 trace!("read: FD not found");
394 return ecx.set_errno_and_return_neg1(LibcError("EBADF"), dest);
395 };
396
397 let dest = dest.clone();
398 ecx.read_from_fd(
399 fd,
400 buf,
401 count,
402 offset,
403 callback!(
404 @capture<'tcx> {
405 count: u64,
406 dest: MPlaceTy<'tcx>,
407 }
408 |ecx, result: Result<u64, IoError>| {
409 match result {
410 Ok(read_size) => {
411 assert!(read_size <= count);
412 ecx.write_int(i64::try_from(read_size).unwrap(), &dest)
414 }
415 Err(e) => ecx.set_errno_and_return_neg1(e, &dest)
416 }}
417 ),
418 )
419 }
420
421 fn write(
422 &mut self,
423 fd_num: i32,
424 buf: Pointer,
425 count: u64,
426 offset: Option<i128>,
427 dest: &MPlaceTy<'tcx>,
428 ) -> InterpResult<'tcx> {
429 let ecx = self.eval_context_mut();
430
431 ecx.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccess)?;
435
436 let count = count.clamp_to(..=u64::try_from(ecx.target_isize_max()).unwrap());
439
440 let Some(fd) = ecx.machine.fds.get(fd_num) else {
442 return ecx.set_errno_and_return_neg1(LibcError("EBADF"), dest);
443 };
444
445 let dest = dest.clone();
446 ecx.write_to_fd(
447 fd,
448 buf,
449 count,
450 offset,
451 callback!(
452 @capture<'tcx> {
453 count: u64,
454 dest: MPlaceTy<'tcx>,
455 }
456 |ecx, result: Result<u64, IoError>| {
457 match result {
458 Ok(write_size) => {
459 assert!(write_size <= count);
460 ecx.write_int(i64::try_from(write_size).unwrap(), &dest)
462 }
463 Err(e) => ecx.set_errno_and_return_neg1(e, &dest)
464
465 }}
466 ),
467 )
468 }
469
470 fn readv(
475 &mut self,
476 fd: &OpTy<'tcx>,
477 iov: &OpTy<'tcx>,
478 iovcnt: &OpTy<'tcx>,
479 offset: Option<&OpTy<'tcx>>,
480 dest: &MPlaceTy<'tcx>,
481 ) -> InterpResult<'tcx> {
482 let ecx = self.eval_context_mut();
483
484 let fd = ecx.read_scalar(fd)?.to_i32()?;
485 let iov = ecx.deref_pointer(iov)?;
486 let iovcnt = ecx.read_scalar(iovcnt)?.to_i32()?;
487
488 let offset = if let Some(offset) = offset {
490 if matches!(ecx.tcx.sess.target.os, Os::Solaris) {
491 throw_unsup_format!(
492 "preadv: vectored reads with offsets aren't supported on Solaris"
493 )
494 }
495 Some(ecx.read_scalar(offset)?.to_int(offset.layout.size)?)
496 } else {
497 None
498 };
499
500 let Ok(iovcnt) = u64::try_from(iovcnt) else {
501 return ecx.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
502 };
503
504 let Some(fd) = ecx.machine.fds.get(fd) else {
506 return ecx.set_errno_and_return_neg1(LibcError("EBADF"), dest);
507 };
508
509 let Some((buffers, total_bytes)) = ecx.read_iovec_array(&iov, iovcnt)? else {
510 return ecx.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
511 };
512 let tmp_ptr: Pointer = ecx
516 .allocate_ptr(
517 Size::from_bytes(total_bytes),
518 Align::ONE,
519 MemoryKind::Stack,
520 AllocInit::Uninit,
521 )?
522 .into();
523
524 let dest = dest.clone();
525 ecx.read_from_fd(
526 fd,
527 tmp_ptr,
528 total_bytes,
529 offset,
530 callback!(
531 @capture<'tcx> {
532 tmp_ptr: Pointer,
533 buffers: Vec<(Pointer, u64)>,
534 dest: MPlaceTy<'tcx>
535 } |ecx, result: Result<u64, IoError>| {
536 let bytes_read = match result {
537 Ok(size) => size,
538 Err(e) => {
539 ecx.deallocate_ptr(tmp_ptr, None, MemoryKind::Stack)?;
540 return ecx.set_errno_and_return_neg1(e, &dest)
541 }
542 };
543 ecx.write_scalar(Scalar::from_target_isize(bytes_read.try_into().unwrap(), ecx), &dest)?;
544 let mut remaining_bytes = bytes_read;
545
546 for (buffer_ptr, buffer_len) in buffers {
550 let tmp_ptr_with_offset =
552 ecx.ptr_offset_inbounds(tmp_ptr, i64::try_from(bytes_read.strict_sub(remaining_bytes)).unwrap())?;
553
554 let copy_amount = buffer_len.min(remaining_bytes);
557 ecx.mem_copy(
558 tmp_ptr_with_offset,
559 buffer_ptr,
560 Size::from_bytes(copy_amount),
561 true,
565 )?;
566
567 remaining_bytes = remaining_bytes.strict_sub(copy_amount);
568 if remaining_bytes == 0 {
569 break;
571 }
572 }
573
574 ecx.deallocate_ptr(tmp_ptr, None, MemoryKind::Stack)
575 }),
576 )
577 }
578
579 fn writev(
583 &mut self,
584 fd: &OpTy<'tcx>,
585 iov: &OpTy<'tcx>,
586 iovcnt: &OpTy<'tcx>,
587 offset: Option<&OpTy<'tcx>>,
588 dest: &MPlaceTy<'tcx>,
589 ) -> InterpResult<'tcx> {
590 let ecx = self.eval_context_mut();
591
592 let fd = ecx.read_scalar(fd)?.to_i32()?;
593 let iov = ecx.deref_pointer(iov)?;
594 let iovcnt = ecx.read_scalar(iovcnt)?.to_i32()?;
595 let offset = if let Some(offset) = offset {
597 if matches!(ecx.tcx.sess.target.os, Os::Solaris) {
598 throw_unsup_format!(
599 "pwritev: vectored writes with offsets aren't supported on Solaris"
600 )
601 }
602 Some(ecx.read_scalar(offset)?.to_int(offset.layout.size)?)
603 } else {
604 None
605 };
606
607 let Ok(iovcnt) = u64::try_from(iovcnt) else {
608 return ecx.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
609 };
610
611 let Some(fd) = ecx.machine.fds.get(fd) else {
613 return ecx.set_errno_and_return_neg1(LibcError("EBADF"), dest);
614 };
615
616 let Some((buffers, total_bytes)) = ecx.read_iovec_array(&iov, iovcnt)? else {
617 return ecx.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
618 };
619 let tmp_ptr: Pointer = ecx
623 .allocate_ptr(
624 Size::from_bytes(total_bytes),
625 Align::ONE,
626 MemoryKind::Stack,
627 AllocInit::Uninit,
628 )?
629 .into();
630
631 let mut bytes_copied: u64 = 0;
634 for (buffer_ptr, buffer_len) in buffers {
635 let tmp_ptr_with_offset =
637 ecx.ptr_offset_inbounds(tmp_ptr, i64::try_from(bytes_copied).unwrap())?;
638
639 ecx.mem_copy(
640 buffer_ptr,
641 tmp_ptr_with_offset,
642 Size::from_bytes(buffer_len),
643 true,
647 )?;
648
649 bytes_copied = bytes_copied.strict_add(buffer_len);
650 }
651
652 let dest = dest.clone();
653 ecx.write_to_fd(
655 fd,
656 tmp_ptr,
657 total_bytes,
658 offset,
659 callback!(
660 @capture<'tcx> {
661 tmp_ptr: Pointer,
662 dest: MPlaceTy<'tcx>,
663 }
664 |ecx, result: Result<u64, IoError>| {
665 ecx.deallocate_ptr(tmp_ptr, None, MemoryKind::Stack)?;
666 match result {
667 Ok(size) => ecx.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), ecx), &dest),
668 Err(e) => ecx.set_errno_and_return_neg1(e, &dest)
669 }
670 }),
671 )
672 }
673}
674
675impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
676trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
677 fn read_from_fd(
684 &mut self,
685 fd: DynFileDescriptionRef,
686 ptr: Pointer,
687 len: u64,
688 offset: Option<i128>,
689 finish: DynMachineCallback<'tcx, Result<u64, IoError>>,
690 ) -> InterpResult<'tcx> {
691 let ecx = self.eval_context_mut();
692
693 if len == 0 {
698 return finish.call(ecx, Ok(0));
699 }
700
701 let len = if ecx.machine.short_fd_operations
704 && fd.short_fd_operations()
705 && len >= 2
706 && let rng = ecx.machine.rng.get_mut()
707 && (rng.random() || (len >= BIG_ACCESS && rng.random()))
709 {
710 len / 2 } else {
712 len
713 };
714
715 match offset {
716 None => fd.read(ecx.machine.communicate(), ptr, len, ecx, finish)?,
717 Some(offset) => {
718 let Ok(offset) = u64::try_from(offset) else {
719 return finish.call(ecx, Err(LibcError("EINVAL")));
720 };
721 fd.as_unix().pread(ecx.machine.communicate(), ptr, len, offset, ecx, finish)?
722 }
723 };
724 interp_ok(())
725 }
726
727 fn write_to_fd(
734 &mut self,
735 fd: DynFileDescriptionRef,
736 ptr: Pointer,
737 len: u64,
738 offset: Option<i128>,
739 finish: DynMachineCallback<'tcx, Result<u64, IoError>>,
740 ) -> InterpResult<'tcx> {
741 let ecx = self.eval_context_mut();
742
743 if len == 0 {
750 return finish.call(ecx, Ok(0));
752 }
753
754 let len = if ecx.machine.short_fd_operations
758 && fd.short_fd_operations()
759 && len >= 2
760 && let rng = ecx.machine.rng.get_mut()
761 && (rng.random() || (len >= BIG_ACCESS && rng.random()))
763 {
764 len / 2
765 } else {
766 len
767 };
768
769 match offset {
770 None => fd.write(ecx.machine.communicate(), ptr, len, ecx, finish)?,
771 Some(offset) => {
772 let Ok(offset) = u64::try_from(offset) else {
773 return finish.call(ecx, Err(LibcError("EINVAL")));
774 };
775 fd.as_unix().pwrite(ecx.machine.communicate(), ptr, len, offset, ecx, finish)?
776 }
777 };
778 interp_ok(())
779 }
780
781 fn setfl_ignored_flags(&self) -> i32 {
783 let ecx = self.eval_context_ref();
784
785 ecx.eval_libc_i32("O_RDONLY")
790 | ecx.eval_libc_i32("O_WRONLY")
791 | ecx.eval_libc_i32("O_RDWR")
792 | ecx.eval_libc_i32("O_CREAT")
793 | ecx.eval_libc_i32("O_EXCL")
794 | ecx.eval_libc_i32("O_NOCTTY")
795 | ecx.eval_libc_i32("O_TRUNC")
796 }
797
798 fn read_iovec_array(
804 &mut self,
805 iov: &MPlaceTy<'tcx>,
806 iovcnt: u64,
807 ) -> InterpResult<'tcx, Option<(Vec<(Pointer, u64)>, u64)>> {
808 let ecx = self.eval_context_mut();
809 let ssize_max = u64::try_from(ecx.target_isize_max()).unwrap();
810
811 let mut buffers = Vec::new();
812 let mut total_bytes: u64 = 0;
813 for idx in 0..iovcnt {
814 let iov_elem = ecx.project_c_array_field(iov, idx)?;
815 let iov_len_field = ecx.project_field_named(&iov_elem, "iov_len")?;
816 let iov_len = ecx.read_target_usize(&iov_len_field)?;
817 let iov_base_field = ecx.project_field_named(&iov_elem, "iov_base")?;
818 let iov_base_ptr = ecx.read_pointer(&iov_base_field)?;
819
820 match total_bytes.checked_add(iov_len) {
824 Some(sum) if sum <= ssize_max => total_bytes = sum,
825 _ => return interp_ok(None),
826 }
827 buffers.push((iov_base_ptr, iov_len));
828 }
829
830 for &(ptr, len) in &buffers {
834 ecx.check_ptr_access(ptr, Size::from_bytes(len), CheckInAllocMsg::MemoryAccess)?;
835 }
836
837 interp_ok(Some((buffers, total_bytes)))
838 }
839}