shadow_rs/host/syscall/handler/
prctl.rs

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