shadow_rs/host/syscall/handler/
ioctl.rs

1use linux_api::errno::Errno;
2use linux_api::fcntl::DescriptorFlags;
3use linux_api::ioctls::IoctlRequest;
4use log::debug;
5use shadow_shim_helper_rs::syscall_types::ForeignPtr;
6
7use crate::cshadow as c;
8use crate::host::descriptor::{CompatFile, FileStatus};
9use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
10use crate::host::syscall::type_formatting::SyscallNonDeterministicArg;
11use crate::host::syscall::types::SyscallResult;
12
13impl SyscallHandler {
14    log_syscall!(
15        ioctl,
16        /* rv */ std::ffi::c_int,
17        /* fd */ std::ffi::c_uint,
18        /* cmd */ std::ffi::c_uint,
19        /* arg */ SyscallNonDeterministicArg<std::ffi::c_ulong>,
20    );
21    pub fn ioctl(
22        ctx: &mut SyscallContext,
23        fd: std::ffi::c_uint,
24        cmd: std::ffi::c_uint,
25        arg_ptr: ForeignPtr<()>,
26    ) -> SyscallResult {
27        log::trace!("Called ioctl() on fd {fd} with cmd {cmd}");
28
29        let Ok(cmd) = IoctlRequest::try_from(cmd) else {
30            debug!("Unrecognized ioctl cmd {cmd}");
31            return Err(Errno::EINVAL.into());
32        };
33
34        // get the descriptor, or return early if it doesn't exist
35        let file = {
36            let mut desc_table = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
37            let desc = Self::get_descriptor_mut(&mut desc_table, fd)?;
38
39            // add the CLOEXEC flag
40            if cmd == IoctlRequest::FIOCLEX {
41                let mut flags = desc.flags();
42                flags.insert(DescriptorFlags::FD_CLOEXEC);
43                desc.set_flags(flags);
44
45                return Ok(0.into());
46            }
47
48            // remove the CLOEXEC flag
49            if cmd == IoctlRequest::FIONCLEX {
50                let mut flags = desc.flags();
51                flags.remove(DescriptorFlags::FD_CLOEXEC);
52                desc.set_flags(flags);
53
54                return Ok(0.into());
55            }
56
57            // NOTE: anything past this point should only modify the file, not the descriptor
58
59            let file = match desc.file() {
60                CompatFile::New(file) => file,
61                // if it's a legacy file, use the C syscall handler instead
62                CompatFile::Legacy(_) => {
63                    drop(desc_table);
64                    return Self::legacy_syscall(c::syscallhandler_ioctl, ctx);
65                }
66            };
67
68            file.inner_file().clone()
69        };
70
71        let mut file = file.borrow_mut();
72
73        // all file types that shadow implements should support non-blocking operation
74        if cmd == IoctlRequest::FIONBIO {
75            let arg_ptr = arg_ptr.cast::<std::ffi::c_int>();
76            let arg = ctx.objs.process.memory_borrow_mut().read(arg_ptr)?;
77
78            let mut status = file.status();
79            status.set(FileStatus::NONBLOCK, arg != 0);
80            file.set_status(status);
81
82            return Ok(0.into());
83        }
84
85        // handle file-specific ioctls
86        file.ioctl(cmd, arg_ptr, &mut ctx.objs.process.memory_borrow_mut())
87    }
88}