Skip to main content

shadow_rs/host/syscall/handler/
prctl.rs

1use linux_api::errno::Errno;
2use linux_api::prctl::{ArchPrctlOp, PrctlOp};
3use linux_api::sched::SuidDump;
4use linux_api::signal::Signal;
5use shadow_shim_helper_rs::syscall_types::ForeignPtr;
6
7use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
8use crate::host::syscall::types::SyscallError;
9
10impl SyscallHandler {
11    log_syscall!(
12        prctl,
13        /* rv */ std::ffi::c_int,
14        /* option */ PrctlOp,
15        /* arg2 */ std::ffi::c_ulong,
16        /* arg3 */ std::ffi::c_ulong,
17        /* arg4 */ std::ffi::c_ulong,
18        /* arg5 */ std::ffi::c_ulong,
19    );
20    pub fn prctl(
21        ctx: &mut SyscallContext,
22        option: PrctlOp,
23        arg2: std::ffi::c_ulong,
24        _arg3: std::ffi::c_ulong,
25        _arg4: std::ffi::c_ulong,
26        _arg5: std::ffi::c_ulong,
27    ) -> Result<std::ffi::c_int, SyscallError> {
28        match option {
29            PrctlOp::PR_CAP_AMBIENT
30            | PrctlOp::PR_CAPBSET_READ
31            | PrctlOp::PR_CAPBSET_DROP
32            | PrctlOp::PR_SET_CHILD_SUBREAPER
33            | PrctlOp::PR_GET_CHILD_SUBREAPER
34            | PrctlOp::PR_SET_ENDIAN
35            | PrctlOp::PR_GET_ENDIAN
36            | PrctlOp::PR_SET_FP_MODE
37            | PrctlOp::PR_GET_FP_MODE
38            | PrctlOp::PR_SET_FPEMU
39            | PrctlOp::PR_GET_FPEMU
40            | PrctlOp::PR_SET_FPEXC
41            | PrctlOp::PR_GET_FPEXC
42            | PrctlOp::PR_SET_KEEPCAPS
43            | PrctlOp::PR_GET_KEEPCAPS
44            | PrctlOp::PR_MCE_KILL
45            | PrctlOp::PR_MCE_KILL_GET
46            | PrctlOp::PR_MPX_ENABLE_MANAGEMENT
47            | PrctlOp::PR_MPX_DISABLE_MANAGEMENT
48            | PrctlOp::PR_SET_NAME
49            | PrctlOp::PR_GET_NAME
50            | PrctlOp::PR_SET_NO_NEW_PRIVS
51            | PrctlOp::PR_GET_NO_NEW_PRIVS
52            | PrctlOp::PR_SET_MM
53            | PrctlOp::PR_SET_PTRACER
54            | PrctlOp::PR_SET_SECUREBITS
55            | PrctlOp::PR_GET_SECUREBITS
56            | PrctlOp::PR_GET_SPECULATION_CTRL
57            | PrctlOp::PR_SET_THP_DISABLE
58            | PrctlOp::PR_TASK_PERF_EVENTS_DISABLE
59            | PrctlOp::PR_TASK_PERF_EVENTS_ENABLE
60            | PrctlOp::PR_GET_THP_DISABLE
61            | PrctlOp::PR_GET_TIMERSLACK
62            | PrctlOp::PR_SET_TIMING
63            | PrctlOp::PR_GET_TIMING
64            | PrctlOp::PR_GET_TSC
65            | PrctlOp::PR_GET_UNALIGN => {
66                log::trace!("prctl {option} executing natively");
67                Err(SyscallError::Native)
68            }
69            PrctlOp::PR_SET_SECCOMP | PrctlOp::PR_GET_SECCOMP => {
70                log::warn!("Not allowing seccomp prctl {option}");
71                Err(Errno::EINVAL.into())
72            }
73            // Needs emulation to have the desired effect, but also N/A on x86_64.
74            PrctlOp::PR_SET_UNALIGN
75            // Executing natively could interfere with shadow's interception of rdtsc. Needs
76            // emulation.
77            | PrctlOp::PR_SET_TSC
78            // Executing natively wouldn't directly hurt anything, but wouldn't have the desired
79            // effect.
80            | PrctlOp::PR_SET_TIMERSLACK
81            // Wouldn't actually hurt correctness, but could significantly hurt performance.
82            | PrctlOp::PR_SET_SPECULATION_CTRL => {
83                log::warn!("Not allowing unimplemented prctl {option}");
84                Err(Errno::EINVAL.into())
85            }
86            PrctlOp::PR_SET_PDEATHSIG => {
87                let signal = if arg2 == 0 {
88                    None
89                } else {
90                    let signal = i32::try_from(arg2).or(Err(Errno::EINVAL))?;
91                    Some(Signal::try_from(signal).or(Err(Errno::EINVAL))?)
92                };
93                ctx.objs.process.set_parent_death_signal(signal);
94                Ok(0)
95            }
96            PrctlOp::PR_GET_PDEATHSIG => {
97                let out_ptr = ForeignPtr::from(arg2).cast::<std::ffi::c_int>();
98                let signal = Signal::as_raw(ctx.objs.process.parent_death_signal());
99                ctx.objs.process.memory_borrow_mut().write(out_ptr, &signal)?;
100                Ok(0)
101            }
102            PrctlOp::PR_GET_TID_ADDRESS => {
103                let out_ptr = ForeignPtr::from(arg2)
104                    .cast::<ForeignPtr<linux_api::posix_types::kernel_pid_t>>();
105                let tid_addr = ctx.objs.thread.get_tid_address();
106                ctx.objs.process.memory_borrow_mut().write(out_ptr, &tid_addr)?;
107                Ok(0)
108            }
109            PrctlOp::PR_SET_DUMPABLE => {
110                let dumpable = SuidDump::new(arg2.try_into().or(Err(Errno::EINVAL))?);
111                if [SuidDump::SUID_DUMP_DISABLE, SuidDump::SUID_DUMP_USER].contains(&dumpable) {
112                    ctx.objs.process.set_dumpable(dumpable);
113                    Ok(0)
114                } else {
115                    Err(Errno::EINVAL.into())
116                }
117            }
118            PrctlOp::PR_GET_DUMPABLE => {
119                Ok(ctx.objs.process.dumpable().val())
120            }
121            _ => {
122                log::warn!("Unknown prctl operation {option}");
123                Err(Errno::EINVAL.into())
124            }
125        }
126    }
127
128    log_syscall!(
129        arch_prctl,
130        /* rv */ std::ffi::c_ulong,
131        /* option */ ArchPrctlOp,
132        // Sometimes a pointer; sometimes a small integer, depending on the
133        // operation. Probably most conveniently formatted as a pointer.
134        /* value */
135        *const std::ffi::c_void,
136    );
137    pub fn arch_prctl(
138        _ctx: &mut SyscallContext,
139        option: ArchPrctlOp,
140        _arg: std::ffi::c_ulong,
141    ) -> Result<std::ffi::c_ulong, SyscallError> {
142        match option {
143            ArchPrctlOp::ARCH_GET_CPUID => {
144                // Always *say* that cpuid is allowed. The shim actually
145                // arranges to trap and emulate it, but the managed program
146                // doesn't need to know that.
147                Ok(1u64)
148            }
149            ArchPrctlOp::ARCH_SET_CPUID => {
150                // Don't allow the managed program to try trapping cpuid itself.
151                // arch_prctl(2): "ENODEV: ARCH_SET_CPUID was requested, but the underlying hardware does not support CPUID faulting."
152                //
153                // It could be reasonable to return Ok(0) here when the argument
154                // is 1 (i.e. to allow cpuid, which would be a no-op), but
155                // experimentally linux returns ENODEV here in that case too.
156                // (At least in shadow's github CI, though it's possible that
157                // docker or something is intercepting the arch_prctl and we're
158                // inadvertently modeling *that* behavior.)
159                Err(Errno::ENODEV.into())
160            }
161            ArchPrctlOp::ARCH_SET_FS
162            | ArchPrctlOp::ARCH_GET_FS
163            | ArchPrctlOp::ARCH_SET_GS
164            | ArchPrctlOp::ARCH_GET_GS => {
165                // Potentially used by threading libraries. Allow natively.
166                Err(SyscallError::Native)
167            }
168            x => {
169                // The kernel headers (and ArchPrctlOp) have more values defined
170                // that aren't in the libc man pages.  It'd take some work to
171                // hunt down corresponding documentation and figure out how
172                // they'd interact with shadow. In the meantime conservatively
173                // block everything else.
174                log::warn!("Unknown or unsupported arch_prctl operation {x:?}");
175                Err(Errno::EINVAL.into())
176            }
177        }
178    }
179}