Skip to content

Commit ac7d9a1

Browse files
Supported fioclex for ioctl on macos
1 parent a2c10e4 commit ac7d9a1

File tree

4 files changed

+70
-0
lines changed

4 files changed

+70
-0
lines changed

src/tools/miri/src/helpers.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
262262
})
263263
}
264264

265+
/// Helper function to get a `libc` constant as an `u64`.
266+
fn eval_libc_u64(&self, name: &str) -> u64 {
267+
// TODO: Cache the result.
268+
self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
269+
panic!("required libc item has unexpected type (not `u64`): {name}")
270+
})
271+
}
272+
265273
/// Helper function to get a `windows` constant as a `Scalar`.
266274
fn eval_windows(&self, module: &str, name: &str) -> Scalar {
267275
self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])

src/tools/miri/src/shims/unix/macos/foreign_items.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use rustc_span::Symbol;
33
use rustc_target::callconv::{Conv, FnAbi};
44

55
use super::sync::EvalContextExt as _;
6+
use crate::helpers::check_min_arg_count;
67
use crate::shims::unix::*;
78
use crate::*;
89

@@ -67,6 +68,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
6768
let result = this.realpath(path, resolved_path)?;
6869
this.write_scalar(result, dest)?;
6970
}
71+
"ioctl" => {
72+
// `ioctl` is variadic. The argument count is checked based on the first argument
73+
// in `this.ioctl()`, so we do not use `check_shim` here.
74+
this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?;
75+
let result = this.ioctl(args)?;
76+
this.write_scalar(result, dest)?;
77+
}
7078

7179
// Environment related shims
7280
"_NSGetEnviron" => {
@@ -234,4 +242,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
234242

235243
interp_ok(EmulateItemResult::NeedsReturn)
236244
}
245+
246+
fn ioctl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
247+
let this = self.eval_context_mut();
248+
249+
let fioclex = this.eval_libc_u64("FIOCLEX");
250+
251+
let [fd_num, cmd] = check_min_arg_count("ioctl", args)?;
252+
let fd_num = this.read_scalar(fd_num)?.to_i32()?;
253+
let cmd = this.read_scalar(cmd)?.to_u64()?;
254+
255+
if cmd == fioclex {
256+
// Since we don't support `exec`, this is a NOP. However, we want to
257+
// return EBADF if the FD is invalid.
258+
if this.machine.fds.is_fd_num(fd_num) {
259+
interp_ok(Scalar::from_i32(0))
260+
} else {
261+
this.set_last_error_and_return_i32(LibcError("EBADF"))
262+
}
263+
} else {
264+
throw_unsup_format!("ioctl: unsupported command {cmd:#x}");
265+
}
266+
}
237267
}

src/tools/miri/tests/pass-dep/libc/libc-fs.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ fn main() {
3838
test_isatty();
3939
test_read_and_uninit();
4040
test_nofollow_not_symlink();
41+
#[cfg(target_os = "macos")]
42+
test_ioctl();
4143
}
4244

4345
fn test_file_open_unix_allow_two_args() {
@@ -431,3 +433,21 @@ fn test_nofollow_not_symlink() {
431433
let ret = unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) };
432434
assert!(ret >= 0);
433435
}
436+
437+
#[cfg(target_os = "macos")]
438+
fn test_ioctl() {
439+
let path = utils::prepare_with_content("miri_test_libc_ioctl.txt", &[]);
440+
441+
let mut name = path.into_os_string();
442+
name.push("\0");
443+
let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>();
444+
unsafe {
445+
// 100 surely is an invalid FD.
446+
assert_eq!(libc::ioctl(100, libc::FIOCLEX), -1);
447+
let errno = std::io::Error::last_os_error().raw_os_error().unwrap();
448+
assert_eq!(errno, libc::EBADF);
449+
450+
let fd = libc::open(name_ptr, libc::O_RDONLY);
451+
assert_eq!(libc::ioctl(fd, libc::FIOCLEX), 0);
452+
}
453+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@ignore-target: windows
2+
3+
#![feature(anonymous_pipe)]
4+
use std::io::{Read, Write};
5+
6+
fn main() {
7+
let (mut ping_rx, mut ping_tx) = std::pipe::pipe().unwrap();
8+
ping_tx.write(b"hello").unwrap();
9+
let mut buf: [u8; 5] = [0; 5];
10+
ping_rx.read(&mut buf).unwrap();
11+
assert_eq!(&buf, "hello".as_bytes());
12+
}

0 commit comments

Comments
 (0)