Skip to main content

starnix_core/task/
syscalls.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::execution::execute_task;
6use crate::mm::{DumpPolicy, MemoryAccessor, MemoryAccessorExt, PAGE_SIZE};
7use crate::ptrace::{
8    PR_SET_PTRACER_ANY, PR_SET_PTRACER_ANY_ARCH32, PtraceAllowedPtracers, PtraceAttachType,
9    PtraceOptions, ptrace_attach, ptrace_dispatch, ptrace_traceme,
10};
11use crate::security;
12use crate::signals::syscalls::RUsagePtr;
13use crate::task::{
14    CurrentTask, ExitStatus, NormalPriority, SchedulingPolicy, SeccompAction, SeccompStateValue,
15    SyslogAccess, Task, ThreadGroup, max_priority_for_sched_policy, min_priority_for_sched_policy,
16};
17use crate::vfs::{
18    FdNumber, FileHandle, MountNamespaceFile, PidFdFileObject, UserBuffersOutputBuffer,
19    VecOutputBuffer,
20};
21use starnix_logging::{log_error, log_info, log_trace, track_stub};
22use starnix_syscalls::SyscallResult;
23use starnix_task_command::TaskCommand;
24use starnix_types::time::timeval_from_duration;
25use starnix_uapi::auth::{
26    CAP_SETGID, CAP_SETPCAP, CAP_SETUID, CAP_SYS_ADMIN, CAP_SYS_NICE, CAP_SYS_RESOURCE,
27    CAP_SYS_TTY_CONFIG, Capabilities, Credentials, PTRACE_MODE_READ_REALCREDS, SecureBits,
28};
29use starnix_uapi::errors::{ENAMETOOLONG, Errno};
30use starnix_uapi::kcmp::KcmpResource;
31use starnix_uapi::open_flags::OpenFlags;
32use starnix_uapi::resource_limits::Resource;
33use starnix_uapi::signals::{Signal, UncheckedSignal};
34use starnix_uapi::syslog::SyslogAction;
35use starnix_uapi::user_address::{
36    ArchSpecific, MappingMultiArchUserRef, MultiArchUserRef, UserAddress, UserCString,
37    UserCStringPtr, UserRef,
38};
39use starnix_uapi::{
40    __user_cap_data_struct, __user_cap_header_struct, _LINUX_CAPABILITY_VERSION_1,
41    _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_VERSION_3, AT_EMPTY_PATH, AT_SYMLINK_NOFOLLOW,
42    BPF_MAXINSNS, CLONE_ARGS_SIZE_VER0, CLONE_ARGS_SIZE_VER1, CLONE_ARGS_SIZE_VER2, CLONE_FILES,
43    CLONE_FS, CLONE_NEWNS, CLONE_NEWUTS, CLONE_SETTLS, CLONE_VFORK, NGROUPS_MAX, PR_CAP_AMBIENT,
44    PR_CAP_AMBIENT_CLEAR_ALL, PR_CAP_AMBIENT_IS_SET, PR_CAP_AMBIENT_LOWER, PR_CAP_AMBIENT_RAISE,
45    PR_CAPBSET_DROP, PR_CAPBSET_READ, PR_GET_CHILD_SUBREAPER, PR_GET_DUMPABLE, PR_GET_KEEPCAPS,
46    PR_GET_NAME, PR_GET_NO_NEW_PRIVS, PR_GET_PDEATHSIG, PR_GET_SECCOMP, PR_GET_SECUREBITS,
47    PR_GET_TIMERSLACK, PR_SET_CHILD_SUBREAPER, PR_SET_DUMPABLE, PR_SET_KEEPCAPS, PR_SET_NAME,
48    PR_SET_NO_NEW_PRIVS, PR_SET_PDEATHSIG, PR_SET_PTRACER, PR_SET_SECCOMP, PR_SET_SECUREBITS,
49    PR_SET_TIMERSLACK, PR_SET_VMA, PR_SET_VMA_ANON_NAME, PRIO_PROCESS, PTRACE_ATTACH, PTRACE_SEIZE,
50    PTRACE_TRACEME, RUSAGE_CHILDREN, SCHED_RESET_ON_FORK, SECCOMP_FILTER_FLAG_LOG,
51    SECCOMP_FILTER_FLAG_NEW_LISTENER, SECCOMP_FILTER_FLAG_SPEC_ALLOW, SECCOMP_FILTER_FLAG_TSYNC,
52    SECCOMP_FILTER_FLAG_TSYNC_ESRCH, SECCOMP_GET_ACTION_AVAIL, SECCOMP_GET_NOTIF_SIZES,
53    SECCOMP_MODE_FILTER, SECCOMP_MODE_STRICT, SECCOMP_SET_MODE_FILTER, SECCOMP_SET_MODE_STRICT,
54    c_char, c_int, clone_args, errno, error, gid_t, pid_t, rlimit, rusage, sched_param,
55    sock_filter, uapi, uid_t,
56};
57use static_assertions::const_assert;
58use std::cmp;
59use std::ffi::CString;
60use std::sync::{Arc, LazyLock};
61use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
62
63#[cfg(target_arch = "aarch64")]
64use starnix_uapi::{PR_GET_TAGGED_ADDR_CTRL, PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE};
65
66pub type SockFProgPtr =
67    MappingMultiArchUserRef<SockFProg, uapi::sock_fprog, uapi::arch32::sock_fprog>;
68pub type SockFilterPtr = MultiArchUserRef<uapi::sock_filter, uapi::arch32::sock_filter>;
69
70pub struct SockFProg {
71    pub len: u32,
72    pub filter: SockFilterPtr,
73}
74
75uapi::arch_map_data! {
76    BidiTryFrom<SockFProg, sock_fprog> {
77        len = len;
78        filter = filter;
79    }
80}
81
82uapi::check_arch_independent_layout! {
83    sched_param {
84        sched_priority,
85    }
86}
87
88pub fn do_clone(current_task: &mut CurrentTask, args: &clone_args) -> Result<pid_t, Errno> {
89    security::check_task_create_access(current_task)?;
90
91    let child_exit_signal = if args.exit_signal == 0 {
92        None
93    } else {
94        Some(Signal::try_from(UncheckedSignal::new(args.exit_signal))?)
95    };
96
97    let mut new_task = current_task.clone_task(
98        args.flags,
99        child_exit_signal,
100        UserRef::<pid_t>::new(UserAddress::from(args.parent_tid)),
101        UserRef::<pid_t>::new(UserAddress::from(args.child_tid)),
102        UserRef::<FdNumber>::new(UserAddress::from(args.pidfd)),
103    )?;
104
105    // Set the result register to 0 for the return value from clone in the
106    // cloned process.
107    new_task.thread_state.registers.set_return_register(0);
108    let (trace_kind, ptrace_state) = current_task.get_ptrace_core_state_for_clone(args);
109
110    if args.stack != 0 {
111        // In clone() the `stack` argument points to the top of the stack, while in clone3()
112        // `stack` points to the bottom of the stack. Therefore, in clone3() we need to add
113        // `stack_size` to calculate the stack pointer. Note that in clone() `stack_size` is 0.
114        new_task
115            .thread_state
116            .registers
117            .set_stack_pointer_register(args.stack.wrapping_add(args.stack_size));
118    }
119
120    if args.flags & (CLONE_SETTLS as u64) != 0 {
121        new_task.thread_state.registers.set_thread_pointer_register(args.tls);
122    }
123
124    let tid = new_task.task.tid.id;
125    let task_ref = Arc::downgrade(&new_task.task);
126    execute_task(new_task, |_| Ok(()), |_| {}, ptrace_state)?;
127
128    current_task.ptrace_event(trace_kind, tid as u64);
129
130    if args.flags & (CLONE_VFORK as u64) != 0 {
131        current_task.wait_for_execve(task_ref)?;
132        current_task.ptrace_event(PtraceOptions::TRACEVFORKDONE, tid as u64);
133    }
134
135    Ok(tid)
136}
137
138pub fn sys_clone3(
139    current_task: &mut CurrentTask,
140    user_clone_args: UserRef<clone_args>,
141    user_clone_args_size: usize,
142) -> Result<pid_t, Errno> {
143    // Only these specific sized versions are supported.
144    if !(user_clone_args_size == CLONE_ARGS_SIZE_VER0 as usize
145        || user_clone_args_size == CLONE_ARGS_SIZE_VER1 as usize
146        || user_clone_args_size == CLONE_ARGS_SIZE_VER2 as usize)
147    {
148        return error!(EINVAL);
149    }
150
151    // The most recent version of the struct size should match our definition.
152    const_assert!(std::mem::size_of::<clone_args>() == CLONE_ARGS_SIZE_VER2 as usize);
153
154    let clone_args = current_task.read_object_partial(user_clone_args, user_clone_args_size)?;
155    do_clone(current_task, &clone_args)
156}
157
158fn read_c_string_vector(
159    mm: &CurrentTask,
160    user_vector: UserCStringPtr,
161    elem_limit: usize,
162    vec_limit: usize,
163) -> Result<(Vec<CString>, usize), Errno> {
164    let mut user_current = user_vector;
165    let mut vector: Vec<CString> = vec![];
166    let mut vec_size: usize = 0;
167    loop {
168        let user_string = mm.read_multi_arch_ptr(user_current)?;
169        if user_string.is_null() {
170            break;
171        }
172        let string = mm
173            .read_c_string_to_vec(user_string, elem_limit)
174            .map_err(|e| if e.code == ENAMETOOLONG { errno!(E2BIG) } else { e })?;
175        let cstring = CString::new(string).map_err(|_| errno!(EINVAL))?;
176        vec_size =
177            vec_size.checked_add(cstring.as_bytes_with_nul().len()).ok_or_else(|| errno!(E2BIG))?;
178        if vec_size > vec_limit {
179            return error!(E2BIG);
180        }
181        vector.push(cstring);
182        user_current = user_current.next()?;
183    }
184    Ok((vector, vec_size))
185}
186
187pub fn sys_execve(
188    current_task: &mut CurrentTask,
189    user_path: UserCString,
190    user_argv: UserCStringPtr,
191    user_environ: UserCStringPtr,
192) -> Result<(), Errno> {
193    sys_execveat(current_task, FdNumber::AT_FDCWD, user_path, user_argv, user_environ, 0)
194}
195
196pub fn sys_execveat(
197    current_task: &mut CurrentTask,
198    dir_fd: FdNumber,
199    user_path: UserCString,
200    user_argv: UserCStringPtr,
201    user_environ: UserCStringPtr,
202    flags: u32,
203) -> Result<(), Errno> {
204    if flags & !(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW) != 0 {
205        return error!(EINVAL);
206    }
207
208    // Calculate the limit for argv and environ size as 1/4 of the stack size, floored at 32 pages.
209    // See the Limits sections in https://man7.org/linux/man-pages/man2/execve.2.html
210    const PAGE_LIMIT: usize = 32;
211    let page_limit_size: usize = PAGE_LIMIT * *PAGE_SIZE as usize;
212    let rlimit = current_task.thread_group().get_rlimit(Resource::STACK);
213    let stack_limit = rlimit / 4;
214    let argv_env_limit = cmp::max(page_limit_size, stack_limit as usize);
215
216    // The limit per argument or environment variable is 32 pages.
217    // See the Limits sections in https://man7.org/linux/man-pages/man2/execve.2.html
218    let (argv, argv_size) = if user_argv.is_null() {
219        (Vec::new(), 0)
220    } else {
221        read_c_string_vector(current_task, user_argv, page_limit_size, argv_env_limit)?
222    };
223
224    let (environ, _) = if user_environ.is_null() {
225        (Vec::new(), 0)
226    } else {
227        read_c_string_vector(
228            current_task,
229            user_environ,
230            page_limit_size,
231            argv_env_limit - argv_size,
232        )?
233    };
234
235    let path = &current_task.read_path(user_path)?;
236
237    log_trace!(argv:?, environ:?, flags:?; "execveat({dir_fd}, {path})");
238
239    let mut open_flags = OpenFlags::empty();
240
241    if flags & AT_SYMLINK_NOFOLLOW != 0 {
242        open_flags |= OpenFlags::NOFOLLOW;
243    }
244
245    if path.is_empty() && flags & AT_EMPTY_PATH == 0 {
246        // If AT_EMPTY_PATH is not set, an empty path is an error.
247        return error!(ENOENT);
248    }
249
250    let executable = current_task.open_file_for_exec(dir_fd, path.as_ref(), open_flags)?;
251
252    // This path can affect script resolution (the path is appended to the script args)
253    // and the auxiliary value `AT_EXECFN` from the syscall `getauxval()`
254    let path = if dir_fd == FdNumber::AT_FDCWD {
255        // The file descriptor is CWD, so the path is exactly
256        // what the user specified.
257        path.to_vec()
258    } else {
259        // The path is `/dev/fd/N/P` where N is the file descriptor
260        // number and P is the user-provided path (if relative and non-empty).
261        //
262        // See https://man7.org/linux/man-pages/man2/execveat.2.html#NOTES
263        match path.first() {
264            Some(b'/') => {
265                // The user-provided path is absolute, so dir_fd is ignored.
266                path.to_vec()
267            }
268            Some(_) => {
269                // User-provided path is relative, append it.
270                let mut new_path = format!("/dev/fd/{}/", dir_fd.raw()).into_bytes();
271                new_path.append(&mut path.to_vec());
272                new_path
273            }
274            // User-provided path is empty
275            None => format!("/dev/fd/{}", dir_fd.raw()).into_bytes(),
276        }
277    };
278
279    let path = CString::new(path).map_err(|_| errno!(EINVAL))?;
280
281    current_task.exec(executable, path, argv, environ)?;
282    Ok(())
283}
284
285pub fn sys_getcpu(
286    current_task: &CurrentTask,
287    cpu_out: UserRef<u32>,
288    node_out: UserRef<u32>,
289) -> Result<(), Errno> {
290    // "When either cpu or node is NULL nothing is written to the respective pointer."
291    // from https://man7.org/linux/man-pages/man2/getcpu.2.html
292    if !cpu_out.is_null() {
293        let thread_stats = current_task
294            .running_state()
295            .thread
296            .get()
297            .expect("current thread is never None when executing")
298            .thread
299            .stats()
300            .map_err(|e| errno!(EINVAL, format!("getting thread stats failed {e:?}")))?;
301        current_task.write_object(cpu_out, &thread_stats.last_scheduled_cpu)?;
302    }
303    if !node_out.is_null() {
304        // Zircon does not yet have a concept of NUMA task scheduling, always tell userspace that
305        // it's on the "first" node which should be true for non-NUMA systems.
306        track_stub!(TODO("https://fxbug.dev/325643815"), "getcpu() numa node");
307        current_task.write_object(node_out, &0)?;
308    }
309    Ok(())
310}
311
312pub fn sys_getpid(current_task: &CurrentTask) -> Result<pid_t, Errno> {
313    Ok(current_task.get_pid())
314}
315
316pub fn sys_gettid(current_task: &CurrentTask) -> Result<pid_t, Errno> {
317    Ok(current_task.get_tid())
318}
319
320pub fn sys_getppid(current_task: &CurrentTask) -> Result<pid_t, Errno> {
321    Ok(current_task.thread_group().read().get_ppid())
322}
323
324fn get_task_or_current(current_task: &CurrentTask, pid: pid_t) -> Result<Arc<Task>, Errno> {
325    if pid == 0 { Ok(current_task.task.clone()) } else { current_task.get_task(pid) }
326}
327
328pub fn sys_getsid(current_task: &CurrentTask, pid: pid_t) -> Result<pid_t, Errno> {
329    let target_task = get_task_or_current(current_task, pid)?;
330    if pid != 0 {
331        security::check_task_getsid(current_task, &target_task)?;
332    }
333    let sid = target_task.thread_group().read().session.id;
334    Ok(sid)
335}
336
337pub fn sys_getpgid(current_task: &CurrentTask, pid: pid_t) -> Result<pid_t, Errno> {
338    let task = get_task_or_current(current_task, pid)?;
339
340    security::check_getpgid_access(current_task, &task)?;
341    let pgid = task.thread_group().read().process_group.id;
342    Ok(pgid)
343}
344
345pub fn sys_setpgid(current_task: &CurrentTask, pid: pid_t, pgid: pid_t) -> Result<(), Errno> {
346    if pgid < 0 {
347        return error!(EINVAL);
348    }
349    let task = get_task_or_current(current_task, pid)?;
350    let pgid = if pgid == 0 {
351        task.pid.clone()
352    } else {
353        current_task.kernel().pids.get(pgid).map_err(|_| errno!(EPERM))?
354    };
355
356    current_task.thread_group().setpgid(current_task, &task, &pgid)
357}
358
359impl CurrentTask {
360    /// Returns true if the `current_task`'s effective user ID (EUID) is the same as the
361    /// EUID or UID of the `target_task`. We describe this as the current task being
362    /// "EUID-friendly" to the target and it enables actions to be performed that would
363    /// otherwise require additional privileges.
364    ///
365    /// See "The caller needs an effective user ID equal to the real user ID or effective
366    /// user ID of the [target]" at sched_setaffinity(2), comparable language at
367    /// setpriority(2), more ambiguous language at sched_setscheduler(2), and no
368    /// particular specification at sched_setparam(2).
369    fn is_euid_friendly_with(&self, target_task: &Task) -> bool {
370        let self_creds = self.current_creds();
371        let target_creds = target_task.real_creds();
372        self_creds.euid == target_creds.uid || self_creds.euid == target_creds.euid
373    }
374}
375
376// A non-root process is allowed to set any of its three uids to the value of any other. The
377// CAP_SETUID capability bypasses these checks and allows setting any uid to any integer. Likewise
378// for gids.
379fn new_uid_allowed(current_task: &CurrentTask, uid: uid_t) -> bool {
380    let current_creds = current_task.current_creds();
381    uid == current_creds.uid
382        || uid == current_creds.euid
383        || uid == current_creds.saved_uid
384        || security::is_task_capable_noaudit(current_task, CAP_SETUID)
385}
386
387fn new_gid_allowed(current_task: &CurrentTask, gid: gid_t) -> bool {
388    let current_creds = current_task.current_creds();
389    gid == current_creds.gid
390        || gid == current_creds.egid
391        || gid == current_creds.saved_gid
392        || security::is_task_capable_noaudit(current_task, CAP_SETGID)
393}
394
395pub fn sys_getuid(current_task: &CurrentTask) -> Result<uid_t, Errno> {
396    Ok(current_task.current_creds().uid)
397}
398
399pub fn sys_getgid(current_task: &CurrentTask) -> Result<gid_t, Errno> {
400    Ok(current_task.current_creds().gid)
401}
402
403pub fn sys_setuid(current_task: &CurrentTask, uid: uid_t) -> Result<(), Errno> {
404    if uid == uid_t::MAX {
405        return error!(EINVAL);
406    }
407    if !new_uid_allowed(&current_task, uid) {
408        return error!(EPERM);
409    }
410
411    let prev = current_task.current_creds();
412    let mut creds = Credentials::clone(&prev);
413    creds.euid = uid;
414    creds.fsuid = uid;
415    if security::is_task_capable_noaudit(current_task, CAP_SETUID) {
416        creds.uid = uid;
417        creds.saved_uid = uid;
418    }
419
420    creds.update_capabilities(&prev);
421    std::mem::drop(prev);
422    current_task.set_creds(creds);
423    Ok(())
424}
425
426pub fn sys_setgid(current_task: &CurrentTask, gid: gid_t) -> Result<(), Errno> {
427    if gid == gid_t::MAX {
428        return error!(EINVAL);
429    }
430    if !new_gid_allowed(&current_task, gid) {
431        return error!(EPERM);
432    }
433
434    let mut creds = Credentials::clone(&current_task.current_creds());
435    creds.egid = gid;
436    creds.fsgid = gid;
437    if security::is_task_capable_noaudit(current_task, CAP_SETGID) {
438        creds.gid = gid;
439        creds.saved_gid = gid;
440    }
441    current_task.set_creds(creds);
442    Ok(())
443}
444
445pub fn sys_geteuid(current_task: &CurrentTask) -> Result<uid_t, Errno> {
446    Ok(current_task.current_creds().euid)
447}
448
449pub fn sys_getegid(current_task: &CurrentTask) -> Result<gid_t, Errno> {
450    Ok(current_task.current_creds().egid)
451}
452
453pub fn sys_setfsuid(current_task: &CurrentTask, fsuid: uid_t) -> Result<uid_t, Errno> {
454    let prev = current_task.current_creds();
455    let prev_fsuid = prev.fsuid;
456    if fsuid != u32::MAX && new_uid_allowed(&current_task, fsuid) {
457        let mut creds = Credentials::clone(&prev);
458        creds.fsuid = fsuid;
459        creds.update_capabilities(&prev);
460        std::mem::drop(prev);
461        current_task.set_creds(creds);
462    }
463
464    Ok(prev_fsuid)
465}
466
467pub fn sys_setfsgid(current_task: &CurrentTask, fsgid: gid_t) -> Result<gid_t, Errno> {
468    let prev = current_task.current_creds();
469    let prev_fsgid = prev.fsgid;
470
471    if fsgid != u32::MAX && new_gid_allowed(&current_task, fsgid) {
472        let mut creds = Credentials::clone(&prev);
473        creds.fsgid = fsgid;
474        creds.update_capabilities(&prev);
475        std::mem::drop(prev);
476        current_task.set_creds(creds);
477    }
478
479    Ok(prev_fsgid)
480}
481
482pub fn sys_getresuid(
483    current_task: &CurrentTask,
484    ruid_addr: UserRef<uid_t>,
485    euid_addr: UserRef<uid_t>,
486    suid_addr: UserRef<uid_t>,
487) -> Result<(), Errno> {
488    let creds = current_task.current_creds();
489    current_task.write_object(ruid_addr, &creds.uid)?;
490    current_task.write_object(euid_addr, &creds.euid)?;
491    current_task.write_object(suid_addr, &creds.saved_uid)?;
492    Ok(())
493}
494
495pub fn sys_getresgid(
496    current_task: &CurrentTask,
497    rgid_addr: UserRef<gid_t>,
498    egid_addr: UserRef<gid_t>,
499    sgid_addr: UserRef<gid_t>,
500) -> Result<(), Errno> {
501    let creds = current_task.current_creds();
502    current_task.write_object(rgid_addr, &creds.gid)?;
503    current_task.write_object(egid_addr, &creds.egid)?;
504    current_task.write_object(sgid_addr, &creds.saved_gid)?;
505    Ok(())
506}
507
508pub fn sys_setreuid(current_task: &CurrentTask, ruid: uid_t, euid: uid_t) -> Result<(), Errno> {
509    // Linux __sys_setreuid() uses asymmetric checks: ruid cannot be set
510    // to saved_uid, while euid can. This prevents regaining root via
511    // setreuid after a privilege drop when setresuid would be required.
512    let validate_ruid = |uid: uid_t| {
513        let creds = current_task.current_creds();
514        uid == u32::MAX
515            || uid == creds.uid
516            || uid == creds.euid
517            || security::is_task_capable_noaudit(current_task, CAP_SETUID)
518    };
519    let validate_euid = |uid: uid_t| {
520        let creds = current_task.current_creds();
521        uid == u32::MAX
522            || uid == creds.uid
523            || uid == creds.euid
524            || uid == creds.saved_uid
525            || security::is_task_capable_noaudit(current_task, CAP_SETUID)
526    };
527    if !validate_ruid(ruid) || !validate_euid(euid) {
528        return error!(EPERM);
529    }
530
531    let prev = current_task.current_creds();
532    let mut creds = Credentials::clone(&prev);
533    let is_ruid_set = ruid != u32::MAX;
534    if is_ruid_set {
535        creds.uid = ruid;
536    }
537    let is_euid_set = euid != u32::MAX;
538    if is_euid_set {
539        creds.euid = euid;
540        creds.fsuid = euid;
541    }
542
543    // If the real user ID is set (i.e., ruid is not -1) or the effective
544    // user ID is set to a value not equal to the previous real user ID,
545    // the saved set-user-ID will be set to the new effective user ID.
546    if is_ruid_set || (is_euid_set && euid != prev.uid) {
547        creds.saved_uid = creds.euid;
548    }
549
550    creds.update_capabilities(&prev);
551    std::mem::drop(prev);
552    current_task.set_creds(creds);
553    Ok(())
554}
555
556pub fn sys_setregid(current_task: &CurrentTask, rgid: gid_t, egid: gid_t) -> Result<(), Errno> {
557    // Same asymmetric permission model as setreuid - see above.
558    let validate_rgid = |gid: gid_t| {
559        let creds = current_task.current_creds();
560        gid == u32::MAX
561            || gid == creds.gid
562            || gid == creds.egid
563            || security::is_task_capable_noaudit(current_task, CAP_SETGID)
564    };
565    let validate_egid = |gid: gid_t| {
566        let creds = current_task.current_creds();
567        gid == u32::MAX
568            || gid == creds.gid
569            || gid == creds.egid
570            || gid == creds.saved_gid
571            || security::is_task_capable_noaudit(current_task, CAP_SETGID)
572    };
573    if !validate_rgid(rgid) || !validate_egid(egid) {
574        return error!(EPERM);
575    }
576
577    let mut creds = Credentials::clone(&current_task.current_creds());
578    let previous_rgid = creds.gid;
579    let is_rgid_set = rgid != u32::MAX;
580    if is_rgid_set {
581        creds.gid = rgid;
582    }
583    let is_egid_set = egid != u32::MAX;
584    if is_egid_set {
585        creds.egid = egid;
586        creds.fsgid = egid;
587    }
588
589    // If the real group ID is set (i.e., rgid is not -1) or the effective
590    // group ID is set to a value not equal to the previous real group ID,
591    // the saved set-group-ID will be set to the new effective group ID.
592    if is_rgid_set || (is_egid_set && egid != previous_rgid) {
593        creds.saved_gid = creds.egid;
594    }
595
596    current_task.set_creds(creds);
597    Ok(())
598}
599
600pub fn sys_setresuid(
601    current_task: &CurrentTask,
602    ruid: uid_t,
603    euid: uid_t,
604    suid: uid_t,
605) -> Result<(), Errno> {
606    let allowed = |uid| uid == u32::MAX || new_uid_allowed(&current_task, uid);
607    if !allowed(ruid) || !allowed(euid) || !allowed(suid) {
608        return error!(EPERM);
609    }
610
611    let prev = current_task.current_creds();
612    let mut creds = Credentials::clone(&prev);
613    if ruid != u32::MAX {
614        creds.uid = ruid;
615    }
616    if euid != u32::MAX {
617        creds.euid = euid;
618        creds.fsuid = euid;
619    }
620    if suid != u32::MAX {
621        creds.saved_uid = suid;
622    }
623    creds.update_capabilities(&prev);
624    std::mem::drop(prev);
625    current_task.set_creds(creds);
626    Ok(())
627}
628
629pub fn sys_setresgid(
630    current_task: &CurrentTask,
631    rgid: gid_t,
632    egid: gid_t,
633    sgid: gid_t,
634) -> Result<(), Errno> {
635    let allowed = |gid| gid == u32::MAX || new_gid_allowed(&current_task, gid);
636    if !allowed(rgid) || !allowed(egid) || !allowed(sgid) {
637        return error!(EPERM);
638    }
639
640    let mut creds = Credentials::clone(&current_task.current_creds());
641    if rgid != u32::MAX {
642        creds.gid = rgid;
643    }
644    if egid != u32::MAX {
645        creds.egid = egid;
646        creds.fsgid = egid;
647    }
648    if sgid != u32::MAX {
649        creds.saved_gid = sgid;
650    }
651    current_task.set_creds(creds);
652    Ok(())
653}
654
655pub fn sys_exit(current_task: &CurrentTask, code: i32) -> Result<(), Errno> {
656    // Only change the current exit status if this has not been already set by exit_group, as
657    // otherwise it has priority.
658    current_task.write().set_exit_status_if_not_already(ExitStatus::Exit(code as u8));
659    Ok(())
660}
661
662pub fn sys_exit_group(current_task: &mut CurrentTask, code: i32) -> Result<(), Errno> {
663    current_task.kill_thread_group(ExitStatus::Exit(code as u8));
664    Ok(())
665}
666
667pub fn sys_sched_getscheduler(current_task: &CurrentTask, pid: pid_t) -> Result<u32, Errno> {
668    if pid < 0 {
669        return error!(EINVAL);
670    }
671
672    let target_task = get_task_or_current(current_task, pid)?;
673    security::check_task_getscheduler_access(current_task, target_task.as_ref())?;
674    Ok(target_task.read().scheduler_state.policy_for_sched_getscheduler())
675}
676
677pub fn sys_sched_setscheduler(
678    current_task: &CurrentTask,
679    pid: pid_t,
680    policy: u32,
681    param: UserRef<sched_param>,
682) -> Result<(), Errno> {
683    // Parse & validate the arguments.
684    if pid < 0 || param.is_null() {
685        return error!(EINVAL);
686    }
687
688    let target_task = get_task_or_current(current_task, pid)?;
689
690    let reset_on_fork = policy & SCHED_RESET_ON_FORK != 0;
691
692    let policy = SchedulingPolicy::try_from(policy & !SCHED_RESET_ON_FORK)?;
693    let realtime_priority =
694        policy.realtime_priority_from(current_task.read_object(param)?.sched_priority)?;
695
696    // TODO: https://fxbug.dev/425143440 - we probably want to improve the locking here.
697    let current_state = target_task.read().scheduler_state.clone();
698
699    // Check capabilities and permissions, if required, for the operation.
700    let euid_friendly = current_task.is_euid_friendly_with(&target_task);
701    let strengthening = current_state.realtime_priority < realtime_priority;
702    let rlimited = strengthening
703        && realtime_priority.exceeds(target_task.thread_group().get_rlimit(Resource::RTPRIO));
704    let clearing_reset_on_fork = current_state.reset_on_fork && !reset_on_fork;
705    let caught_in_idle_trap = current_state.policy == SchedulingPolicy::Idle
706        && policy != SchedulingPolicy::Idle
707        && current_state
708            .normal_priority
709            .exceeds(target_task.thread_group().get_rlimit(Resource::NICE));
710    if !euid_friendly || rlimited || clearing_reset_on_fork || caught_in_idle_trap {
711        security::check_task_capable(current_task, CAP_SYS_NICE)?;
712    }
713
714    security::check_task_setscheduler_access(current_task, &target_task)?;
715
716    // Apply the new scheduler configuration to the task.
717    target_task.set_scheduler_policy_priority_and_reset_on_fork(
718        policy,
719        realtime_priority,
720        reset_on_fork,
721    )?;
722
723    Ok(())
724}
725
726const CPU_SET_SIZE: usize = 128;
727
728#[repr(C)]
729#[derive(Debug, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
730pub struct CpuSet {
731    bits: [u8; CPU_SET_SIZE],
732}
733
734impl Default for CpuSet {
735    fn default() -> Self {
736        Self { bits: [0; CPU_SET_SIZE] }
737    }
738}
739
740fn check_cpu_set_alignment(current_task: &CurrentTask, cpusetsize: u32) -> Result<(), Errno> {
741    let alignment = if current_task.is_arch32() { 4 } else { 8 };
742    if cpusetsize < alignment || cpusetsize % alignment != 0 {
743        return error!(EINVAL);
744    }
745    Ok(())
746}
747
748fn get_default_cpu_set() -> CpuSet {
749    let mut result = CpuSet::default();
750    let mut cpus_count = zx::system_get_num_cpus();
751    let cpus_count_max = (CPU_SET_SIZE * 8) as u32;
752    if cpus_count > cpus_count_max {
753        log_error!("cpus_count={cpus_count}, greater than the {cpus_count_max} max supported.");
754        cpus_count = cpus_count_max;
755    }
756    let mut index = 0;
757    while cpus_count > 0 {
758        let count = std::cmp::min(cpus_count, 8);
759        let (shl, overflow) = 1_u8.overflowing_shl(count);
760        let mask = if overflow { u8::MAX } else { shl - 1 };
761        result.bits[index] = mask;
762        index += 1;
763        cpus_count -= count;
764    }
765    result
766}
767
768pub fn sys_sched_getaffinity(
769    current_task: &CurrentTask,
770    pid: pid_t,
771    cpusetsize: u32,
772    user_mask: UserAddress,
773) -> Result<usize, Errno> {
774    if pid < 0 {
775        return error!(EINVAL);
776    }
777
778    check_cpu_set_alignment(current_task, cpusetsize)?;
779
780    let target_task = get_task_or_current(current_task, pid)?;
781    security::check_task_getscheduler_access(current_task, &target_task)?;
782
783    // sched_setaffinity() is not implemented. Fake affinity mask based on the number of CPUs.
784    let mask = get_default_cpu_set();
785    let mask_size = std::cmp::min(cpusetsize as usize, CPU_SET_SIZE);
786    current_task.write_memory(user_mask, &mask.bits[..mask_size])?;
787    track_stub!(TODO("https://fxbug.dev/322874659"), "sched_getaffinity");
788    Ok(mask_size)
789}
790
791pub fn sys_sched_setaffinity(
792    current_task: &CurrentTask,
793    pid: pid_t,
794    cpusetsize: u32,
795    user_mask: UserAddress,
796) -> Result<(), Errno> {
797    if pid < 0 {
798        return error!(EINVAL);
799    }
800    let target_task = get_task_or_current(current_task, pid)?;
801
802    check_cpu_set_alignment(current_task, cpusetsize)?;
803
804    let mask_size = std::cmp::min(cpusetsize as usize, CPU_SET_SIZE);
805    let mut mask = CpuSet::default();
806    current_task.read_memory_to_slice(user_mask, &mut mask.bits[..mask_size])?;
807
808    // Specified mask must include at least one valid CPU.
809    let max_mask = get_default_cpu_set();
810    let mut has_valid_cpu_in_mask = false;
811    for (l1, l2) in std::iter::zip(max_mask.bits, mask.bits) {
812        has_valid_cpu_in_mask = has_valid_cpu_in_mask || (l1 & l2 > 0);
813    }
814    if !has_valid_cpu_in_mask {
815        return error!(EINVAL);
816    }
817
818    if !current_task.is_euid_friendly_with(&target_task) {
819        security::check_task_capable(current_task, CAP_SYS_NICE)?;
820    }
821
822    security::check_task_setscheduler_access(current_task, &target_task)?;
823
824    // Currently, we ignore the mask and act as if the system reset the mask
825    // immediately to allowing all CPUs.
826    track_stub!(TODO("https://fxbug.dev/322874889"), "sched_setaffinity");
827    Ok(())
828}
829
830pub fn sys_sched_getparam(
831    current_task: &CurrentTask,
832    pid: pid_t,
833    param: UserRef<sched_param>,
834) -> Result<(), Errno> {
835    if pid < 0 || param.is_null() {
836        return error!(EINVAL);
837    }
838
839    let target_task = get_task_or_current(current_task, pid)?;
840    let param_value = target_task.read().scheduler_state.get_sched_param();
841    current_task.write_object(param, &param_value)?;
842    Ok(())
843}
844
845pub fn sys_sched_setparam(
846    current_task: &CurrentTask,
847    pid: pid_t,
848    param: UserRef<sched_param>,
849) -> Result<(), Errno> {
850    // Parse & validate the arguments.
851    if pid < 0 || param.is_null() {
852        return error!(EINVAL);
853    }
854    let target_task = get_task_or_current(current_task, pid)?;
855
856    // TODO: https://fxbug.dev/425143440 - we probably want to improve the locking here.
857    let current_state = target_task.read().scheduler_state.clone();
858
859    let realtime_priority = current_state
860        .policy
861        .realtime_priority_from(current_task.read_object(param)?.sched_priority)?;
862
863    // Check capabilities and permissions, if required, for the operation.
864    let euid_friendly = current_task.is_euid_friendly_with(&target_task);
865    let strengthening = current_state.realtime_priority < realtime_priority;
866    let rlimited = strengthening
867        && realtime_priority.exceeds(target_task.thread_group().get_rlimit(Resource::RTPRIO));
868    if !euid_friendly || rlimited {
869        security::check_task_capable(current_task, CAP_SYS_NICE)?;
870    }
871
872    security::check_task_setscheduler_access(current_task, &target_task)?;
873
874    // Apply the new scheduler configuration to the task.
875    target_task.set_scheduler_priority(realtime_priority)?;
876
877    Ok(())
878}
879
880pub fn sys_sched_get_priority_min(_ctx: &CurrentTask, policy: u32) -> Result<u8, Errno> {
881    min_priority_for_sched_policy(policy)
882}
883
884pub fn sys_sched_get_priority_max(_ctx: &CurrentTask, policy: u32) -> Result<u8, Errno> {
885    max_priority_for_sched_policy(policy)
886}
887
888pub fn sys_ioprio_set(
889    _current_task: &mut CurrentTask,
890    _which: i32,
891    _who: i32,
892    _ioprio: i32,
893) -> Result<(), Errno> {
894    track_stub!(TODO("https://fxbug.dev/297591758"), "ioprio_set()");
895    error!(ENOSYS)
896}
897
898pub fn sys_prctl(
899    current_task: &mut CurrentTask,
900    option: u32,
901    arg2: u64,
902    arg3: u64,
903    arg4: u64,
904    arg5: u64,
905) -> Result<SyscallResult, Errno> {
906    match option {
907        PR_SET_VMA => {
908            if arg2 != PR_SET_VMA_ANON_NAME as u64 {
909                track_stub!(TODO("https://fxbug.dev/322874826"), "prctl PR_SET_VMA", arg2);
910                return error!(ENOSYS);
911            }
912            let addr = UserAddress::from(arg3);
913            let length = arg4 as usize;
914            let name_addr = UserAddress::from(arg5);
915            let name = if name_addr.is_null() {
916                None
917            } else {
918                let name = UserCString::new(current_task, UserAddress::from(arg5));
919                let name = current_task.read_c_string_to_vec(name, 256).map_err(|e| {
920                    // An overly long name produces EINVAL and not ENAMETOOLONG in Linux 5.15.
921                    if e.code == ENAMETOOLONG { errno!(EINVAL) } else { e }
922                })?;
923                // Some characters are forbidden in VMA names.
924                if name.iter().any(|b| {
925                    matches!(b,
926                        0..=0x1f |
927                        0x7f..=0xff |
928                        b'\\' | b'`' | b'$' | b'[' | b']'
929                    )
930                }) {
931                    return error!(EINVAL);
932                }
933                Some(name)
934            };
935            current_task.mm()?.set_mapping_name(addr, length, name)?;
936            Ok(().into())
937        }
938        PR_SET_DUMPABLE => {
939            let mm = current_task.mm()?;
940            mm.dumpable.store(match arg2 {
941                0 => DumpPolicy::Disable,
942                1 => DumpPolicy::User,
943                _ => return error!(EINVAL),
944            });
945            Ok(().into())
946        }
947        PR_GET_DUMPABLE => {
948            let mm = current_task.mm()?;
949            Ok(match mm.dumpable.load() {
950                DumpPolicy::Disable => 0.into(),
951                DumpPolicy::User => 1.into(),
952            })
953        }
954        PR_SET_PDEATHSIG => {
955            // From <https://man7.org/linux/man-pages/man2/PR_SET_PDEATHSIG.2const.html>:
956            //
957            //   Set the parent-death signal of the calling process to sig (either a
958            //   signal value in the range [1, NSIG - 1], or 0 to clear).  This is the
959            //   signal that the calling process will get when its parent dies.
960            let signal =
961                if arg2 == 0 { None } else { Some(Signal::try_from(UncheckedSignal::new(arg2))?) };
962            current_task.thread_group().write().set_parent_death_signal(signal);
963            Ok(().into())
964        }
965        PR_GET_PDEATHSIG => {
966            // From <https://man7.org/linux/man-pages/man2/PR_GET_PDEATHSIG.2const.html>:
967            //
968            //   Return the parent-death signal number of the calling process, in the
969            //   location pointed to by sig.
970            let signal_ptr = UserRef::<i32>::new(UserAddress::from(arg2));
971            let parent_death_signal = current_task.thread_group().read().parent_death_signal();
972            let signal_number = parent_death_signal.map_or(0, |signal| signal.number() as i32);
973            current_task.write_object(signal_ptr, &signal_number)?;
974            Ok(().into())
975        }
976        PR_SET_NAME => {
977            let addr = UserAddress::from(arg2);
978            let name = TaskCommand::new(&current_task.read_memory_to_array::<16>(addr)?);
979            current_task.set_command_name(name);
980            if current_task.tid == current_task.pid {
981                current_task.thread_group.sync_syscall_log_level();
982            }
983            Ok(0.into())
984        }
985        PR_GET_NAME => {
986            let addr = UserAddress::from(arg2);
987            let name = current_task.command().prctl_name();
988            current_task.write_memory(addr, &name[..])?;
989            Ok(().into())
990        }
991        PR_SET_PTRACER => {
992            let allowed_ptracers = if arg2 == PR_SET_PTRACER_ANY
993                || (current_task.is_arch32() && arg2 == PR_SET_PTRACER_ANY_ARCH32)
994            {
995                PtraceAllowedPtracers::Any
996            } else if arg2 == 0 {
997                PtraceAllowedPtracers::None
998            } else {
999                let task = current_task.get_task(arg2 as i32).map_err(|_| errno!(EINVAL))?;
1000                PtraceAllowedPtracers::Some(task.pid.clone())
1001            };
1002            current_task.thread_group().write().allowed_ptracers = allowed_ptracers;
1003            Ok(().into())
1004        }
1005        PR_GET_KEEPCAPS => {
1006            Ok(current_task.current_creds().securebits.contains(SecureBits::KEEP_CAPS).into())
1007        }
1008        PR_SET_KEEPCAPS => {
1009            if arg2 != 0 && arg2 != 1 {
1010                return error!(EINVAL);
1011            }
1012            // Per PR_SET_KEEPCAPS(2const):
1013            //   EPERM: The caller's SECBIT_KEEP_CAPS_LOCKED flag is set.
1014            if current_task.current_creds().securebits.contains(SecureBits::KEEP_CAPS_LOCKED) {
1015                return error!(EPERM);
1016            }
1017            let mut creds = Credentials::clone(&current_task.current_creds());
1018            creds.securebits.set(SecureBits::KEEP_CAPS, arg2 != 0);
1019            current_task.set_creds(creds);
1020            Ok(().into())
1021        }
1022        PR_SET_NO_NEW_PRIVS => {
1023            // If any args are set other than arg2 to 1, this should return einval
1024            if arg2 != 1 || arg3 != 0 || arg4 != 0 || arg5 != 0 {
1025                return error!(EINVAL);
1026            }
1027            current_task.write().enable_no_new_privs();
1028            Ok(().into())
1029        }
1030        PR_GET_NO_NEW_PRIVS => {
1031            // If any args are set, this should return einval
1032            if arg2 != 0 || arg3 != 0 || arg4 != 0 {
1033                return error!(EINVAL);
1034            }
1035            Ok(current_task.read().no_new_privs().into())
1036        }
1037        PR_GET_SECCOMP => {
1038            if current_task.seccomp_filter_state.get() == SeccompStateValue::None {
1039                Ok(0.into())
1040            } else {
1041                Ok(2.into())
1042            }
1043        }
1044        PR_SET_SECCOMP => {
1045            if arg2 == SECCOMP_MODE_STRICT as u64 {
1046                return sys_seccomp(current_task, SECCOMP_SET_MODE_STRICT, 0, UserAddress::NULL);
1047            } else if arg2 == SECCOMP_MODE_FILTER as u64 {
1048                return sys_seccomp(current_task, SECCOMP_SET_MODE_FILTER, 0, arg3.into());
1049            }
1050            Ok(().into())
1051        }
1052        PR_GET_CHILD_SUBREAPER => {
1053            let addr = UserAddress::from(arg2);
1054            #[allow(clippy::bool_to_int_with_if)]
1055            let value: i32 =
1056                if current_task.thread_group().read().is_child_subreaper { 1 } else { 0 };
1057            current_task.write_object(addr.into(), &value)?;
1058            Ok(().into())
1059        }
1060        PR_SET_CHILD_SUBREAPER => {
1061            current_task.thread_group().write().is_child_subreaper = arg2 != 0;
1062            Ok(().into())
1063        }
1064        PR_GET_SECUREBITS => Ok(current_task.current_creds().securebits.bits().into()),
1065        PR_SET_SECUREBITS => {
1066            security::check_task_capable(current_task, CAP_SETPCAP)?;
1067
1068            // Observed: Linux returns EPERM (not EINVAL) when unrecognized bits are set in flags.
1069            let flags = u32::try_from(arg2).map_err(|_| errno!(EPERM))?;
1070            let securebits = SecureBits::from_bits(flags).ok_or_else(|| errno!(EPERM))?;
1071
1072            let mut creds = Credentials::clone(&current_task.current_creds());
1073            creds.set_securebits(securebits)?;
1074            current_task.set_creds(creds);
1075            Ok(().into())
1076        }
1077        PR_CAPBSET_READ => {
1078            let cap = Capabilities::try_from(arg2)?;
1079            Ok(current_task.current_creds().cap_bounding.contains(cap).into())
1080        }
1081        PR_CAPBSET_DROP => {
1082            let mut creds = Credentials::clone(&current_task.current_creds());
1083            security::check_task_capable(current_task, CAP_SETPCAP)?;
1084
1085            creds.cap_bounding.remove(Capabilities::try_from(arg2)?);
1086            current_task.set_creds(creds);
1087            Ok(().into())
1088        }
1089        PR_CAP_AMBIENT => {
1090            let operation = arg2 as u32;
1091            let capability_arg = Capabilities::try_from(arg3)?;
1092            if arg4 != 0 || arg5 != 0 {
1093                return error!(EINVAL);
1094            }
1095
1096            // TODO(security): We don't currently validate capabilities, but this should return an
1097            // error if the capability_arg is invalid.
1098            match operation {
1099                PR_CAP_AMBIENT_RAISE => {
1100                    let mut creds = Credentials::clone(&current_task.current_creds());
1101                    if !(creds.cap_permitted.contains(capability_arg)
1102                        && creds.cap_inheritable.contains(capability_arg))
1103                    {
1104                        return error!(EPERM);
1105                    }
1106                    if creds.securebits.contains(SecureBits::NO_CAP_AMBIENT_RAISE) {
1107                        return error!(EPERM);
1108                    }
1109
1110                    creds.cap_ambient.insert(capability_arg);
1111                    current_task.set_creds(creds);
1112                    Ok(().into())
1113                }
1114                PR_CAP_AMBIENT_LOWER => {
1115                    let mut creds = Credentials::clone(&current_task.current_creds());
1116                    creds.cap_ambient.remove(capability_arg);
1117                    current_task.set_creds(creds);
1118                    Ok(().into())
1119                }
1120                PR_CAP_AMBIENT_IS_SET => {
1121                    Ok(current_task.current_creds().cap_ambient.contains(capability_arg).into())
1122                }
1123                PR_CAP_AMBIENT_CLEAR_ALL => {
1124                    if arg3 != 0 {
1125                        return error!(EINVAL);
1126                    }
1127
1128                    let mut creds = Credentials::clone(&current_task.current_creds());
1129                    creds.cap_ambient = Capabilities::empty();
1130                    current_task.set_creds(creds);
1131                    Ok(().into())
1132                }
1133                _ => error!(EINVAL),
1134            }
1135        }
1136        PR_SET_TIMERSLACK => {
1137            current_task.write().set_timerslack_ns(arg2);
1138            Ok(().into())
1139        }
1140        PR_GET_TIMERSLACK => Ok(current_task.read().timerslack_ns.into()),
1141        #[cfg(target_arch = "aarch64")]
1142        PR_GET_TAGGED_ADDR_CTRL => {
1143            track_stub!(TODO("https://fxbug.dev/408554469"), "PR_GET_TAGGED_ADDR_CTRL");
1144            Ok(0.into())
1145        }
1146        #[cfg(target_arch = "aarch64")]
1147        PR_SET_TAGGED_ADDR_CTRL => match u32::try_from(arg2).map_err(|_| errno!(EINVAL))? {
1148            // Only untagged pointers are allowed, the default.
1149            0 => Ok(().into()),
1150            PR_TAGGED_ADDR_ENABLE => {
1151                track_stub!(TODO("https://fxbug.dev/408554469"), "PR_TAGGED_ADDR_ENABLE");
1152                error!(EINVAL)
1153            }
1154            unknown_mode => {
1155                track_stub!(
1156                    TODO("https://fxbug.dev/408554469"),
1157                    "PR_SET_TAGGED_ADDR_CTRL unknown mode",
1158                    unknown_mode,
1159                );
1160                error!(EINVAL)
1161            }
1162        },
1163        _ => {
1164            track_stub!(TODO("https://fxbug.dev/322874733"), "prctl fallthrough", option);
1165            error!(ENOSYS)
1166        }
1167    }
1168}
1169
1170pub fn sys_ptrace(
1171    current_task: &mut CurrentTask,
1172    request: u32,
1173    pid: pid_t,
1174    addr: UserAddress,
1175    data: UserAddress,
1176) -> Result<SyscallResult, Errno> {
1177    if request == PTRACE_TRACEME {
1178        return ptrace_traceme(current_task);
1179    }
1180    let pid = current_task.kernel().pids.get(pid)?;
1181    match request {
1182        PTRACE_ATTACH => ptrace_attach(current_task, &pid, PtraceAttachType::Attach, data),
1183        PTRACE_SEIZE => ptrace_attach(current_task, &pid, PtraceAttachType::Seize, data),
1184        _ => ptrace_dispatch(current_task, request, &pid, addr, data),
1185    }
1186}
1187
1188pub fn sys_set_tid_address(
1189    current_task: &CurrentTask,
1190    user_tid: UserRef<pid_t>,
1191) -> Result<pid_t, Errno> {
1192    current_task.write().clear_child_tid = user_tid;
1193    Ok(current_task.get_tid())
1194}
1195
1196pub fn sys_getrusage(
1197    current_task: &CurrentTask,
1198    who: i32,
1199    user_usage: RUsagePtr,
1200) -> Result<(), Errno> {
1201    const RUSAGE_SELF: i32 = starnix_uapi::uapi::RUSAGE_SELF as i32;
1202    const RUSAGE_THREAD: i32 = starnix_uapi::uapi::RUSAGE_THREAD as i32;
1203    track_stub!(TODO("https://fxbug.dev/297370242"), "real rusage");
1204    let time_stats = match who {
1205        RUSAGE_CHILDREN => current_task.task.thread_group().read().children_time_stats,
1206        RUSAGE_SELF => current_task.task.thread_group().time_stats(),
1207        RUSAGE_THREAD => current_task.task.time_stats(),
1208        _ => return error!(EINVAL),
1209    };
1210
1211    let usage = rusage {
1212        ru_utime: timeval_from_duration(time_stats.user_time),
1213        ru_stime: timeval_from_duration(time_stats.system_time),
1214        ..rusage::default()
1215    };
1216    current_task.write_multi_arch_object(user_usage, usage)?;
1217
1218    Ok(())
1219}
1220
1221type PrLimitRef = MultiArchUserRef<uapi::rlimit, uapi::arch32::rlimit>;
1222
1223pub fn sys_getrlimit(
1224    current_task: &CurrentTask,
1225    resource: u32,
1226    user_rlimit: PrLimitRef,
1227) -> Result<(), Errno> {
1228    do_prlimit64(current_task, 0, resource, PrLimitRef::null(current_task), user_rlimit)
1229}
1230
1231pub fn sys_setrlimit(
1232    current_task: &CurrentTask,
1233    resource: u32,
1234    user_rlimit: PrLimitRef,
1235) -> Result<(), Errno> {
1236    do_prlimit64(current_task, 0, resource, user_rlimit, PrLimitRef::null(current_task))
1237}
1238
1239pub fn sys_prlimit64(
1240    current_task: &CurrentTask,
1241    pid: pid_t,
1242    user_resource: u32,
1243    new_limit_ref: UserRef<uapi::rlimit>,
1244    old_limit_ref: UserRef<uapi::rlimit>,
1245) -> Result<(), Errno> {
1246    do_prlimit64::<uapi::rlimit>(
1247        current_task,
1248        pid,
1249        user_resource,
1250        new_limit_ref.into(),
1251        old_limit_ref.into(),
1252    )
1253}
1254
1255pub fn do_prlimit64<T>(
1256    current_task: &CurrentTask,
1257    pid: pid_t,
1258    user_resource: u32,
1259    new_limit_ref: MultiArchUserRef<uapi::rlimit, T>,
1260    old_limit_ref: MultiArchUserRef<uapi::rlimit, T>,
1261) -> Result<(), Errno>
1262where
1263    T: FromBytes + IntoBytes + Immutable + From<uapi::rlimit> + Into<uapi::rlimit>,
1264{
1265    let target_task = get_task_or_current(current_task, pid)?;
1266
1267    // To get or set the resource of a process other than itself, the caller must have either:
1268    // * the same `uid`, `euid`, `saved_uid`, `gid`, `egid`, `saved_gid` as the target.
1269    // * the CAP_SYS_RESOURCE
1270    if current_task.get_pid() != target_task.get_pid() {
1271        let self_creds = current_task.current_creds();
1272        let target_creds = target_task.real_creds();
1273        if self_creds.uid != target_creds.uid
1274            || self_creds.euid != target_creds.euid
1275            || self_creds.saved_uid != target_creds.saved_uid
1276            || self_creds.gid != target_creds.gid
1277            || self_creds.egid != target_creds.egid
1278            || self_creds.saved_gid != target_creds.saved_gid
1279        {
1280            security::check_task_capable(current_task, CAP_SYS_RESOURCE)?;
1281        }
1282        security::task_prlimit(
1283            current_task,
1284            &target_task,
1285            !old_limit_ref.is_null(),
1286            !new_limit_ref.is_null(),
1287        )?;
1288    }
1289
1290    let resource = Resource::from_raw(user_resource)?;
1291
1292    let old_limit = match resource {
1293        // TODO: Integrate Resource::STACK with generic ResourceLimits machinery.
1294        Resource::STACK => {
1295            if !new_limit_ref.is_null() {
1296                track_stub!(
1297                    TODO("https://fxbug.dev/322874791"),
1298                    "prlimit64 cannot set RLIMIT_STACK"
1299                );
1300            }
1301            // The stack size is fixed at the moment, but
1302            // if MAP_GROWSDOWN is implemented this should
1303            // report the limit that it can be grown.
1304            let mm = target_task.mm()?;
1305            let mm_state = mm.state.read();
1306            let stack_size = mm_state.stack_size as u64;
1307            rlimit { rlim_cur: stack_size, rlim_max: stack_size }
1308        }
1309        _ => {
1310            let new_limit = if new_limit_ref.is_null() {
1311                None
1312            } else {
1313                let new_limit = current_task.read_multi_arch_object(new_limit_ref)?;
1314                if new_limit.rlim_cur > new_limit.rlim_max {
1315                    return error!(EINVAL);
1316                }
1317                Some(new_limit)
1318            };
1319            ThreadGroup::adjust_rlimits(current_task, &target_task, resource, new_limit)?
1320        }
1321    };
1322    if !old_limit_ref.is_null() {
1323        current_task.write_multi_arch_object(old_limit_ref, old_limit)?;
1324    }
1325    Ok(())
1326}
1327
1328pub fn sys_quotactl(
1329    _current_task: &CurrentTask,
1330    _cmd: i32,
1331    _special: UserRef<c_char>,
1332    _id: i32,
1333    _addr: UserRef<c_char>,
1334) -> Result<SyscallResult, Errno> {
1335    track_stub!(TODO("https://fxbug.dev/297302197"), "quotacl()");
1336    error!(ENOSYS)
1337}
1338
1339pub fn sys_capget(
1340    current_task: &CurrentTask,
1341    user_header: UserRef<__user_cap_header_struct>,
1342    user_data: UserRef<__user_cap_data_struct>,
1343) -> Result<(), Errno> {
1344    let mut header = current_task.read_object(user_header)?;
1345    let is_version_valid =
1346        [_LINUX_CAPABILITY_VERSION_1, _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_VERSION_3]
1347            .contains(&header.version);
1348    if !is_version_valid {
1349        header.version = _LINUX_CAPABILITY_VERSION_3;
1350        current_task.write_object(user_header, &header)?;
1351    }
1352    if user_data.is_null() {
1353        return Ok(());
1354    }
1355    if !is_version_valid || header.pid < 0 {
1356        return error!(EINVAL);
1357    }
1358
1359    let target_task = get_task_or_current(current_task, header.pid)?;
1360
1361    security::check_getcap_access(current_task, &target_task)?;
1362
1363    let (permitted, effective, inheritable) = {
1364        let creds = &target_task.real_creds();
1365        (creds.cap_permitted, creds.cap_effective, creds.cap_inheritable)
1366    };
1367
1368    match header.version {
1369        _LINUX_CAPABILITY_VERSION_1 => {
1370            let data: [__user_cap_data_struct; 1] = [__user_cap_data_struct {
1371                effective: effective.as_abi_v1(),
1372                inheritable: inheritable.as_abi_v1(),
1373                permitted: permitted.as_abi_v1(),
1374            }];
1375            current_task.write_objects(user_data, &data)?;
1376        }
1377        _LINUX_CAPABILITY_VERSION_2 | _LINUX_CAPABILITY_VERSION_3 => {
1378            // Return 64 bit capabilities as two sets of 32 bit capabilities, little endian
1379            let (permitted, effective, inheritable) =
1380                (permitted.as_abi_v3(), effective.as_abi_v3(), inheritable.as_abi_v3());
1381            let data: [__user_cap_data_struct; 2] = [
1382                __user_cap_data_struct {
1383                    effective: effective.0,
1384                    inheritable: inheritable.0,
1385                    permitted: permitted.0,
1386                },
1387                __user_cap_data_struct {
1388                    effective: effective.1,
1389                    inheritable: inheritable.1,
1390                    permitted: permitted.1,
1391                },
1392            ];
1393            current_task.write_objects(user_data, &data)?;
1394        }
1395        _ => {
1396            unreachable!("already returned if Linux capability version is not valid")
1397        }
1398    }
1399    Ok(())
1400}
1401
1402pub fn sys_capset(
1403    current_task: &CurrentTask,
1404    user_header: UserRef<__user_cap_header_struct>,
1405    user_data: UserRef<__user_cap_data_struct>,
1406) -> Result<(), Errno> {
1407    let mut header = current_task.read_object(user_header)?;
1408    let is_version_valid =
1409        [_LINUX_CAPABILITY_VERSION_1, _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_VERSION_3]
1410            .contains(&header.version);
1411    if !is_version_valid {
1412        header.version = _LINUX_CAPABILITY_VERSION_3;
1413        current_task.write_object(user_header, &header)?;
1414        return error!(EINVAL);
1415    }
1416    if header.pid != 0 && header.pid != current_task.tid.id {
1417        return error!(EPERM);
1418    }
1419
1420    let (new_permitted, new_effective, new_inheritable) = match header.version {
1421        _LINUX_CAPABILITY_VERSION_1 => {
1422            let data = current_task.read_object(user_data)?;
1423            (
1424                Capabilities::from_abi_v1(data.permitted),
1425                Capabilities::from_abi_v1(data.effective),
1426                Capabilities::from_abi_v1(data.inheritable),
1427            )
1428        }
1429        _LINUX_CAPABILITY_VERSION_2 | _LINUX_CAPABILITY_VERSION_3 => {
1430            let data =
1431                current_task.read_objects_to_array::<__user_cap_data_struct, 2>(user_data)?;
1432            (
1433                Capabilities::from_abi_v3((data[0].permitted, data[1].permitted)),
1434                Capabilities::from_abi_v3((data[0].effective, data[1].effective)),
1435                Capabilities::from_abi_v3((data[0].inheritable, data[1].inheritable)),
1436            )
1437        }
1438        _ => {
1439            unreachable!("already returned if Linux capability version is not valid")
1440        }
1441    };
1442
1443    // Permission checks. Copied out of TLPI section 39.7.
1444    let mut creds = Credentials::clone(&current_task.current_creds());
1445    {
1446        log_trace!(
1447            "Capabilities({{permitted={:?} from {:?}, effective={:?} from {:?}, inheritable={:?} from {:?}}}, bounding={:?})",
1448            new_permitted,
1449            creds.cap_permitted,
1450            new_effective,
1451            creds.cap_effective,
1452            new_inheritable,
1453            creds.cap_inheritable,
1454            creds.cap_bounding
1455        );
1456        if !creds.cap_inheritable.union(creds.cap_permitted).contains(new_inheritable) {
1457            security::check_task_capable(current_task, CAP_SETPCAP)?;
1458        }
1459
1460        if !creds.cap_inheritable.union(creds.cap_bounding).contains(new_inheritable) {
1461            return error!(EPERM);
1462        }
1463        if !creds.cap_permitted.contains(new_permitted) {
1464            return error!(EPERM);
1465        }
1466        if !new_permitted.contains(new_effective) {
1467            return error!(EPERM);
1468        }
1469    }
1470    let target_task = get_task_or_current(current_task, header.pid)?;
1471
1472    security::check_setcap_access(current_task, &target_task)?;
1473
1474    creds.cap_permitted = new_permitted;
1475    creds.cap_effective = new_effective;
1476    creds.cap_inheritable = new_inheritable;
1477    creds.cap_ambient = new_permitted & new_inheritable & creds.cap_ambient;
1478    current_task.set_creds(creds);
1479    Ok(())
1480}
1481
1482pub fn sys_seccomp(
1483    current_task: &mut CurrentTask,
1484    operation: u32,
1485    flags: u32,
1486    args: UserAddress,
1487) -> Result<SyscallResult, Errno> {
1488    match operation {
1489        SECCOMP_SET_MODE_STRICT => {
1490            if flags != 0 || args != UserAddress::NULL {
1491                return error!(EINVAL);
1492            }
1493            current_task.set_seccomp_state(SeccompStateValue::Strict)?;
1494            Ok(().into())
1495        }
1496        SECCOMP_SET_MODE_FILTER => {
1497            if flags
1498                & (SECCOMP_FILTER_FLAG_LOG
1499                    | SECCOMP_FILTER_FLAG_NEW_LISTENER
1500                    | SECCOMP_FILTER_FLAG_SPEC_ALLOW
1501                    | SECCOMP_FILTER_FLAG_TSYNC
1502                    | SECCOMP_FILTER_FLAG_TSYNC_ESRCH)
1503                != flags
1504            {
1505                return error!(EINVAL);
1506            }
1507            if (flags & SECCOMP_FILTER_FLAG_TSYNC == 0)
1508                && (flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH != 0)
1509            {
1510                return error!(EINVAL);
1511            }
1512            if (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER != 0)
1513                && (flags & SECCOMP_FILTER_FLAG_TSYNC != 0)
1514                && (flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH == 0)
1515            {
1516                return error!(EINVAL);
1517            }
1518            let fprog =
1519                current_task.read_multi_arch_object(SockFProgPtr::new(current_task, args))?;
1520            if fprog.len > BPF_MAXINSNS || fprog.len == 0 {
1521                return error!(EINVAL);
1522            }
1523            let code: Vec<sock_filter> =
1524                current_task.read_multi_arch_objects_to_vec(fprog.filter, fprog.len as usize)?;
1525
1526            if !current_task.read().no_new_privs() {
1527                security::check_task_capable(current_task, CAP_SYS_ADMIN)
1528                    .map_err(|_| errno!(EACCES))?;
1529            }
1530            current_task.add_seccomp_filter(code, flags)
1531        }
1532        SECCOMP_GET_ACTION_AVAIL => {
1533            if flags != 0 || args.is_null() {
1534                return error!(EINVAL);
1535            }
1536            let action: u32 = current_task.read_object(UserRef::new(args))?;
1537            SeccompAction::is_action_available(action)
1538        }
1539        SECCOMP_GET_NOTIF_SIZES => {
1540            if flags != 0 {
1541                return error!(EINVAL);
1542            }
1543            track_stub!(TODO("https://fxbug.dev/322874791"), "SECCOMP_GET_NOTIF_SIZES");
1544            error!(ENOSYS)
1545        }
1546        _ => {
1547            track_stub!(TODO("https://fxbug.dev/322874916"), "seccomp fallthrough", operation);
1548            error!(EINVAL)
1549        }
1550    }
1551}
1552
1553pub fn sys_setgroups(
1554    current_task: &CurrentTask,
1555    size: usize,
1556    groups_addr: UserAddress,
1557) -> Result<(), Errno> {
1558    if size > NGROUPS_MAX as usize {
1559        return error!(EINVAL);
1560    }
1561    let groups = current_task.read_objects_to_vec::<gid_t>(groups_addr.into(), size)?;
1562    security::check_task_capable(current_task, CAP_SETGID)?;
1563    let mut creds = Credentials::clone(&current_task.current_creds());
1564    creds.groups = groups;
1565    current_task.set_creds(creds);
1566    Ok(())
1567}
1568
1569pub fn sys_getgroups(
1570    current_task: &CurrentTask,
1571    size: usize,
1572    groups_addr: UserAddress,
1573) -> Result<usize, Errno> {
1574    if size > NGROUPS_MAX as usize {
1575        return error!(EINVAL);
1576    }
1577    let creds = current_task.current_creds();
1578    if size != 0 {
1579        if size < creds.groups.len() {
1580            return error!(EINVAL);
1581        }
1582        current_task.write_memory(groups_addr, creds.groups.as_slice().as_bytes())?;
1583    }
1584    Ok(creds.groups.len())
1585}
1586
1587pub fn sys_setsid(current_task: &CurrentTask) -> Result<pid_t, Errno> {
1588    current_task.thread_group().setsid()?;
1589    Ok(current_task.get_pid())
1590}
1591
1592// Note the asymmetry with sys_setpriority: this returns "kernel nice" which ranges
1593// from 1 (weakest) to 40 (strongest). (It is part of Linux history that this syscall
1594// deals with niceness but has "priority" in its name.)
1595pub fn sys_getpriority(current_task: &CurrentTask, which: u32, who: i32) -> Result<u8, Errno> {
1596    match which {
1597        PRIO_PROCESS => {}
1598        // TODO: https://fxbug.dev/287121196 - support PRIO_PGRP and PRIO_USER?
1599        _ => return error!(EINVAL),
1600    }
1601    track_stub!(TODO("https://fxbug.dev/322893809"), "getpriority permissions");
1602    let target_task = get_task_or_current(current_task, who)?;
1603    let state = target_task.read();
1604    Ok(state.scheduler_state.normal_priority.raw_priority())
1605}
1606
1607// Note the asymmetry with sys_getpriority: this call's `priority` parameter is a
1608// "user nice" which ranges from -20 (strongest) to 19 (weakest) (other values can be
1609// passed and are clamped to that range and interpretation). (It is part of Linux
1610// history that this syscall deals with niceness but has "priority" in its name.)
1611pub fn sys_setpriority(
1612    current_task: &CurrentTask,
1613    which: u32,
1614    who: i32,
1615    priority: i32,
1616) -> Result<(), Errno> {
1617    // Parse & validate the arguments.
1618    match which {
1619        PRIO_PROCESS => {}
1620        // TODO: https://fxbug.dev/287121196 - support PRIO_PGRP and PRIO_USER?
1621        _ => return error!(EINVAL),
1622    }
1623
1624    let target_task = get_task_or_current(current_task, who)?;
1625
1626    let normal_priority = NormalPriority::from_setpriority_syscall(priority);
1627
1628    // TODO: https://fxbug.dev/425143440 - we probably want to improve the locking here.
1629    let current_state = target_task.read().scheduler_state.clone();
1630
1631    // Check capabilities and permissions, if required, for the operation.
1632    let euid_friendly = current_task.is_euid_friendly_with(&target_task);
1633    let strengthening = current_state.normal_priority < normal_priority;
1634    let rlimited = strengthening
1635        && normal_priority.exceeds(target_task.thread_group().get_rlimit(Resource::NICE));
1636    if !euid_friendly {
1637        security::check_task_capable(current_task, CAP_SYS_NICE)?;
1638    } else if rlimited {
1639        security::check_task_capable(current_task, CAP_SYS_NICE).map_err(|_| errno!(EACCES))?;
1640    }
1641
1642    security::check_task_setnice_access(current_task, &target_task)?;
1643
1644    // Apply the new scheduler configuration to the task.
1645    target_task.set_scheduler_nice(normal_priority)?;
1646
1647    Ok(())
1648}
1649
1650pub fn sys_setns(current_task: &CurrentTask, ns_fd: FdNumber, ns_type: c_int) -> Result<(), Errno> {
1651    let file_handle = current_task.files().get(ns_fd)?;
1652
1653    // From man pages this is not quite right because some namespace types require more capabilities
1654    // or require this capability in multiple namespaces, but it should cover our current test
1655    // cases and we can make this more nuanced once more namespace types are supported.
1656    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1657
1658    if let Some(mount_ns) = file_handle.downcast_file::<MountNamespaceFile>() {
1659        if !(ns_type == 0 || ns_type == CLONE_NEWNS as i32) {
1660            log_trace!("invalid type");
1661            return error!(EINVAL);
1662        }
1663
1664        track_stub!(TODO("https://fxbug.dev/297312091"), "setns CLONE_FS limitations");
1665        current_task.fs().set_namespace(mount_ns.0.clone())?;
1666        return Ok(());
1667    }
1668
1669    if let Some(_pidfd) = file_handle.downcast_file::<PidFdFileObject>() {
1670        track_stub!(TODO("https://fxbug.dev/297312844"), "setns w/ pidfd");
1671        return error!(ENOSYS);
1672    }
1673
1674    track_stub!(TODO("https://fxbug.dev/322893829"), "unknown ns file for setns, see logs");
1675    log_info!("ns_fd was not a supported namespace file: {}", file_handle.ops_type_name());
1676    error!(EINVAL)
1677}
1678
1679pub fn sys_unshare(current_task: &CurrentTask, flags: u32) -> Result<(), Errno> {
1680    const IMPLEMENTED_FLAGS: u32 = CLONE_FILES | CLONE_FS | CLONE_NEWNS | CLONE_NEWUTS;
1681    if flags & !IMPLEMENTED_FLAGS != 0 {
1682        track_stub!(TODO("https://fxbug.dev/322893372"), "unshare", flags & !IMPLEMENTED_FLAGS);
1683        return error!(EINVAL);
1684    }
1685
1686    if (flags & CLONE_FILES) != 0 {
1687        current_task.running_state().unshare_files(current_task);
1688    }
1689
1690    if (flags & CLONE_FS) != 0 {
1691        current_task.unshare_fs();
1692    }
1693
1694    if (flags & CLONE_NEWNS) != 0 {
1695        security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1696        current_task.fs().unshare_namespace();
1697    }
1698
1699    if (flags & CLONE_NEWUTS) != 0 {
1700        security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1701        // Fork the UTS namespace.
1702        let mut task_state = current_task.write();
1703        let new_uts_ns = task_state.uts_ns.read().clone();
1704        task_state.uts_ns = Arc::new(new_uts_ns.into());
1705    }
1706
1707    Ok(())
1708}
1709
1710pub fn sys_swapon(
1711    current_task: &CurrentTask,
1712    user_path: UserCString,
1713    flags: u32,
1714) -> Result<(), Errno> {
1715    const MAX_SWAPFILES: usize = 32; // See https://man7.org/linux/man-pages/man2/swapon.2.html
1716    const SWAP_FLAGS_VALID: u32 = 0x7ffff; // Defined in include/linux/swap.h
1717
1718    if (flags & !SWAP_FLAGS_VALID) != 0 {
1719        return error!(EINVAL);
1720    }
1721
1722    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1723
1724    if flags != 0 {
1725        track_stub!(TODO("https://fxbug.dev/322893905"), "swapon flags", flags);
1726    }
1727
1728    let path = current_task.read_path(user_path)?;
1729    let file = current_task.open_file(path.as_ref(), OpenFlags::RDWR)?;
1730
1731    let node = file.node();
1732    let mode = node.info().mode;
1733    if !mode.is_reg() && !mode.is_blk() {
1734        return error!(EINVAL);
1735    }
1736
1737    // We determined this magic number by using the mkswap tool and the file tool. The mkswap tool
1738    // populates a few bytes in the file, including a UUID, which can be replaced with zeros while
1739    // still being recognized by the file tool. This string appears at a fixed offset
1740    // (MAGIC_OFFSET) in the file, which looks quite like a magic number.
1741    const MAGIC_OFFSET: usize = 0xff6;
1742    let swap_magic = b"SWAPSPACE2";
1743    let mut buffer = VecOutputBuffer::new(swap_magic.len());
1744    if file.read_at(current_task, MAGIC_OFFSET, &mut buffer)? != swap_magic.len()
1745        || buffer.data() != swap_magic
1746    {
1747        return error!(EINVAL);
1748    }
1749
1750    let mut swap_files = current_task.kernel().swap_files.lock();
1751    for swap_node in swap_files.iter() {
1752        if Arc::ptr_eq(swap_node, node) {
1753            return error!(EBUSY);
1754        }
1755    }
1756    if swap_files.len() >= MAX_SWAPFILES {
1757        return error!(EPERM);
1758    }
1759    swap_files.push(node.clone());
1760    Ok(())
1761}
1762
1763pub fn sys_swapoff(current_task: &CurrentTask, user_path: UserCString) -> Result<(), Errno> {
1764    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1765
1766    let path = current_task.read_path(user_path)?;
1767    let file = current_task.open_file(path.as_ref(), OpenFlags::RDWR)?;
1768    let node = file.node();
1769
1770    let mut swap_files = current_task.kernel().swap_files.lock();
1771    let original_length = swap_files.len();
1772    swap_files.retain(|swap_node| !Arc::ptr_eq(swap_node, node));
1773    if swap_files.len() == original_length {
1774        return error!(EINVAL);
1775    }
1776    Ok(())
1777}
1778
1779#[derive(Default, Debug, IntoBytes, KnownLayout, FromBytes, Immutable)]
1780#[repr(C)]
1781struct KcmpParams {
1782    mask: usize,
1783    shuffle: usize,
1784}
1785
1786static KCMP_PARAMS: LazyLock<KcmpParams> = LazyLock::new(|| {
1787    let mut params = KcmpParams::default();
1788    starnix_crypto::cprng_draw(params.as_mut_bytes());
1789    // Ensure the shuffle is odd so that multiplying a usize by this value is a permutation.
1790    params.shuffle |= 1;
1791    params
1792});
1793
1794fn obfuscate_value(value: usize) -> usize {
1795    let KcmpParams { mask, shuffle } = *KCMP_PARAMS;
1796    (value ^ mask).wrapping_mul(shuffle)
1797}
1798
1799fn obfuscate_ptr<T>(ptr: *const T) -> usize {
1800    obfuscate_value(ptr as usize)
1801}
1802
1803fn obfuscate_arc<T>(arc: &Arc<T>) -> usize {
1804    obfuscate_ptr(Arc::as_ptr(arc))
1805}
1806
1807pub fn sys_kcmp(
1808    current_task: &CurrentTask,
1809    pid1: pid_t,
1810    pid2: pid_t,
1811    resource_type: u32,
1812    index1: u64,
1813    index2: u64,
1814) -> Result<u32, Errno> {
1815    let task1 = current_task.get_task(pid1)?;
1816    let task2 = current_task.get_task(pid2)?;
1817
1818    current_task.check_ptrace_access_mode(PTRACE_MODE_READ_REALCREDS, &task1)?;
1819    current_task.check_ptrace_access_mode(PTRACE_MODE_READ_REALCREDS, &task2)?;
1820
1821    let resource_type = KcmpResource::from_raw(resource_type)?;
1822
1823    // Output encoding (see <https://man7.org/linux/man-pages/man2/kcmp.2.html>):
1824    //
1825    //   0  v1 is equal to v2; in other words, the two processes share the resource.
1826    //   1  v1 is less than v2.
1827    //   2  v1 is greater than v2.
1828    //   3  v1 is not equal to v2, but ordering information is unavailable.
1829    //
1830    fn encode_ordering(value: cmp::Ordering) -> u32 {
1831        match value {
1832            cmp::Ordering::Equal => 0,
1833            cmp::Ordering::Less => 1,
1834            cmp::Ordering::Greater => 2,
1835        }
1836    }
1837
1838    match resource_type {
1839        KcmpResource::FILE => {
1840            fn get_file(task: &Task, index: u64) -> Result<FileHandle, Errno> {
1841                // TODO: Test whether O_PATH is allowed here. Conceptually, seems like
1842                //       O_PATH should be allowed, but we haven't tested it yet.
1843                task.files()?.get_allowing_opath(FdNumber::from_raw(
1844                    index.try_into().map_err(|_| errno!(EBADF))?,
1845                ))
1846            }
1847            let file1 = get_file(&task1, index1)?;
1848            let file2 = get_file(&task2, index2)?;
1849            Ok(encode_ordering(obfuscate_arc(&file1).cmp(&obfuscate_arc(&file2))))
1850        }
1851        KcmpResource::FILES => {
1852            let files1 = task1.files()?.id();
1853            let files2 = task2.files()?.id();
1854            Ok(encode_ordering(obfuscate_value(files1.raw()).cmp(&obfuscate_value(files2.raw()))))
1855        }
1856        KcmpResource::FS => {
1857            let fs1 = task1.fs()?;
1858            let fs2 = task2.fs()?;
1859            Ok(encode_ordering(obfuscate_arc(&fs1).cmp(&obfuscate_arc(&fs2))))
1860        }
1861        KcmpResource::SIGHAND => Ok(encode_ordering(
1862            obfuscate_arc(&task1.thread_group().signal_actions)
1863                .cmp(&obfuscate_arc(&task2.thread_group().signal_actions)),
1864        )),
1865        KcmpResource::VM => {
1866            Ok(encode_ordering(obfuscate_arc(&task1.mm()?).cmp(&obfuscate_arc(&task2.mm()?))))
1867        }
1868        KcmpResource::SYSVSEM => error!(EOPNOTSUPP),
1869        _ => error!(EINVAL),
1870    }
1871}
1872
1873pub fn sys_syslog(
1874    current_task: &CurrentTask,
1875    action_type: i32,
1876    address: UserAddress,
1877    length: i32,
1878) -> Result<i32, Errno> {
1879    let action = SyslogAction::try_from(action_type)?;
1880    let syslog =
1881        current_task.kernel().syslog.access(&current_task, SyslogAccess::Syscall(action))?;
1882    match action {
1883        SyslogAction::Read => {
1884            if address.is_null() || length < 0 {
1885                return error!(EINVAL);
1886            }
1887            let mut output_buffer =
1888                UserBuffersOutputBuffer::unified_new_at(current_task, address, length as usize)?;
1889            syslog.blocking_read(current_task, &mut output_buffer)
1890        }
1891        SyslogAction::ReadAll => {
1892            if address.is_null() || length < 0 {
1893                return error!(EINVAL);
1894            }
1895            let mut output_buffer =
1896                UserBuffersOutputBuffer::unified_new_at(current_task, address, length as usize)?;
1897            syslog.read_all(current_task.kernel(), &mut output_buffer)
1898        }
1899        SyslogAction::SizeUnread => syslog.size_unread(),
1900        SyslogAction::SizeBuffer => syslog.size_buffer(),
1901        SyslogAction::Close | SyslogAction::Open => Ok(0),
1902        SyslogAction::ReadClear => {
1903            track_stub!(TODO("https://fxbug.dev/322894145"), "syslog: read clear");
1904            Ok(0)
1905        }
1906        SyslogAction::Clear => {
1907            track_stub!(TODO("https://fxbug.dev/322893673"), "syslog: clear");
1908            Ok(0)
1909        }
1910        SyslogAction::ConsoleOff => {
1911            track_stub!(TODO("https://fxbug.dev/322894399"), "syslog: console off");
1912            Ok(0)
1913        }
1914        SyslogAction::ConsoleOn => {
1915            track_stub!(TODO("https://fxbug.dev/322894106"), "syslog: console on");
1916            Ok(0)
1917        }
1918        SyslogAction::ConsoleLevel => {
1919            if length <= 0 || length >= 8 {
1920                return error!(EINVAL);
1921            }
1922            track_stub!(TODO("https://fxbug.dev/322894199"), "syslog: console level");
1923            Ok(0)
1924        }
1925    }
1926}
1927
1928pub fn sys_vhangup(current_task: &CurrentTask) -> Result<(), Errno> {
1929    security::check_task_capable(current_task, CAP_SYS_TTY_CONFIG)?;
1930    track_stub!(TODO("https://fxbug.dev/324079257"), "vhangup");
1931    Ok(())
1932}
1933
1934// Syscalls for arch32 usage
1935#[cfg(target_arch = "aarch64")]
1936mod arch32 {
1937    pub use super::{
1938        sys_execve as sys_arch32_execve, sys_execveat as sys_arch32_execveat,
1939        sys_getegid as sys_arch32_getegid32, sys_geteuid as sys_arch32_geteuid32,
1940        sys_getgid as sys_arch32_getgid32, sys_getgroups as sys_arch32_getgroups32,
1941        sys_getpgid as sys_arch32_getpgid, sys_getppid as sys_arch32_getppid,
1942        sys_getpriority as sys_arch32_getpriority, sys_getresgid as sys_arch32_getresgid32,
1943        sys_getresuid as sys_arch32_getresuid32, sys_getrlimit as sys_arch32_ugetrlimit,
1944        sys_getrusage as sys_arch32_getrusage, sys_getuid as sys_arch32_getuid32,
1945        sys_ioprio_set as sys_arch32_ioprio_set, sys_kcmp as sys_arch32_kcmp,
1946        sys_ptrace as sys_arch32_ptrace, sys_quotactl as sys_arch32_quotactl,
1947        sys_sched_get_priority_max as sys_arch32_sched_get_priority_max,
1948        sys_sched_get_priority_min as sys_arch32_sched_get_priority_min,
1949        sys_sched_getaffinity as sys_arch32_sched_getaffinity,
1950        sys_sched_getparam as sys_arch32_sched_getparam,
1951        sys_sched_setaffinity as sys_arch32_sched_setaffinity,
1952        sys_sched_setparam as sys_arch32_sched_setparam,
1953        sys_sched_setscheduler as sys_arch32_sched_setscheduler, sys_seccomp as sys_arch32_seccomp,
1954        sys_setfsgid as sys_arch32_setfsgid, sys_setfsgid as sys_arch32_setfsgid32,
1955        sys_setfsuid as sys_arch32_setfsuid, sys_setfsuid as sys_arch32_setfsuid32,
1956        sys_setgid as sys_arch32_setgid32, sys_setgroups as sys_arch32_setgroups32,
1957        sys_setns as sys_arch32_setns, sys_setpgid as sys_arch32_setpgid,
1958        sys_setpriority as sys_arch32_setpriority, sys_setregid as sys_arch32_setregid32,
1959        sys_setresgid as sys_arch32_setresgid32, sys_setresuid as sys_arch32_setresuid32,
1960        sys_setreuid as sys_arch32_setreuid32, sys_setreuid as sys_arch32_setreuid,
1961        sys_setrlimit as sys_arch32_setrlimit, sys_setsid as sys_arch32_setsid,
1962        sys_syslog as sys_arch32_syslog, sys_unshare as sys_arch32_unshare,
1963    };
1964}
1965
1966#[cfg(target_arch = "aarch64")]
1967pub use arch32::*;
1968
1969#[cfg(test)]
1970mod tests {
1971    use super::*;
1972    use crate::mm::syscalls::sys_munmap;
1973    use crate::testing::{AutoReleasableTask, map_memory, spawn_kernel_and_run};
1974    use starnix_syscalls::SUCCESS;
1975    use starnix_task_command::TaskCommand;
1976    use starnix_uapi::auth::Credentials;
1977    use starnix_uapi::signals::SIGCHLD;
1978    use starnix_uapi::uapi::{kcmp_type_KCMP_SYSVSEM, kcmp_type_KCMP_TYPES, kcmp_type_KCMP_VM};
1979    use starnix_uapi::{SCHED_FIFO, SCHED_NORMAL};
1980    use std::ffi::CString;
1981
1982    #[::fuchsia::test]
1983    async fn test_prctl_set_vma_anon_name() {
1984        spawn_kernel_and_run(async |current_task| {
1985            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
1986            let name_addr = (mapped_address + 128u64).unwrap();
1987            let name = "test-name\0";
1988            current_task.write_memory(name_addr, name.as_bytes()).expect("failed to write name");
1989            sys_prctl(
1990                current_task,
1991                PR_SET_VMA,
1992                PR_SET_VMA_ANON_NAME as u64,
1993                mapped_address.ptr() as u64,
1994                32,
1995                name_addr.ptr() as u64,
1996            )
1997            .expect("failed to set name");
1998            assert_eq!(
1999                "test-name",
2000                current_task
2001                    .mm()
2002                    .unwrap()
2003                    .get_mapping_name((mapped_address + 24u64).unwrap())
2004                    .expect("failed to get address")
2005                    .unwrap()
2006                    .to_string(),
2007            );
2008
2009            sys_munmap(&current_task, mapped_address, *PAGE_SIZE as usize)
2010                .expect("failed to unmap memory");
2011            assert_eq!(
2012                error!(EFAULT),
2013                current_task.mm().unwrap().get_mapping_name((mapped_address + 24u64).unwrap())
2014            );
2015        })
2016        .await;
2017    }
2018
2019    #[::fuchsia::test]
2020    async fn test_set_vma_name_special_chars() {
2021        spawn_kernel_and_run(async |current_task| {
2022            let name_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2023
2024            let mapping_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2025
2026            for c in 1..255 {
2027                let vma_name = CString::new([c]).unwrap();
2028                current_task.write_memory(name_addr, vma_name.as_bytes_with_nul()).unwrap();
2029
2030                let result = sys_prctl(
2031                    current_task,
2032                    PR_SET_VMA,
2033                    PR_SET_VMA_ANON_NAME as u64,
2034                    mapping_addr.ptr() as u64,
2035                    *PAGE_SIZE,
2036                    name_addr.ptr() as u64,
2037                );
2038
2039                if c > 0x1f
2040                    && c < 0x7f
2041                    && c != b'\\'
2042                    && c != b'`'
2043                    && c != b'$'
2044                    && c != b'['
2045                    && c != b']'
2046                {
2047                    assert_eq!(result, Ok(SUCCESS));
2048                } else {
2049                    assert_eq!(result, error!(EINVAL));
2050                }
2051            }
2052        })
2053        .await;
2054    }
2055
2056    #[::fuchsia::test]
2057    async fn test_set_vma_name_long() {
2058        spawn_kernel_and_run(async |current_task| {
2059            let name_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2060
2061            let mapping_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2062
2063            let name_too_long = CString::new(vec![b'a'; 256]).unwrap();
2064
2065            current_task.write_memory(name_addr, name_too_long.as_bytes_with_nul()).unwrap();
2066
2067            assert_eq!(
2068                sys_prctl(
2069                    current_task,
2070                    PR_SET_VMA,
2071                    PR_SET_VMA_ANON_NAME as u64,
2072                    mapping_addr.ptr() as u64,
2073                    *PAGE_SIZE,
2074                    name_addr.ptr() as u64,
2075                ),
2076                error!(EINVAL)
2077            );
2078
2079            let name_just_long_enough = CString::new(vec![b'a'; 255]).unwrap();
2080
2081            current_task
2082                .write_memory(name_addr, name_just_long_enough.as_bytes_with_nul())
2083                .unwrap();
2084
2085            assert_eq!(
2086                sys_prctl(
2087                    current_task,
2088                    PR_SET_VMA,
2089                    PR_SET_VMA_ANON_NAME as u64,
2090                    mapping_addr.ptr() as u64,
2091                    *PAGE_SIZE,
2092                    name_addr.ptr() as u64,
2093                ),
2094                Ok(SUCCESS)
2095            );
2096        })
2097        .await;
2098    }
2099
2100    #[::fuchsia::test]
2101    async fn test_set_vma_name_misaligned() {
2102        spawn_kernel_and_run(async |current_task| {
2103            let name_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2104
2105            let mapping_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2106
2107            let name = CString::new("name").unwrap();
2108            current_task.write_memory(name_addr, name.as_bytes_with_nul()).unwrap();
2109
2110            // Passing a misaligned pointer to the start of the named region fails.
2111            assert_eq!(
2112                sys_prctl(
2113                    current_task,
2114                    PR_SET_VMA,
2115                    PR_SET_VMA_ANON_NAME as u64,
2116                    1 + mapping_addr.ptr() as u64,
2117                    *PAGE_SIZE - 1,
2118                    name_addr.ptr() as u64,
2119                ),
2120                error!(EINVAL)
2121            );
2122
2123            // Passing an unaligned length does work, however.
2124            assert_eq!(
2125                sys_prctl(
2126                    current_task,
2127                    PR_SET_VMA,
2128                    PR_SET_VMA_ANON_NAME as u64,
2129                    mapping_addr.ptr() as u64,
2130                    *PAGE_SIZE - 1,
2131                    name_addr.ptr() as u64,
2132                ),
2133                Ok(SUCCESS)
2134            );
2135        })
2136        .await;
2137    }
2138
2139    #[::fuchsia::test]
2140    async fn test_prctl_get_set_dumpable() {
2141        spawn_kernel_and_run(async |current_task| {
2142            assert_eq!(sys_prctl(current_task, PR_GET_DUMPABLE, 0, 0, 0, 0), Ok(1.into()));
2143
2144            sys_prctl(current_task, PR_SET_DUMPABLE, 0, 0, 0, 0).expect("failed to set dumpable");
2145            assert_eq!(sys_prctl(current_task, PR_GET_DUMPABLE, 0, 0, 0, 0), Ok(0.into()));
2146
2147            sys_prctl(current_task, PR_SET_DUMPABLE, 1, 0, 0, 0).expect("failed to set dumpable");
2148            assert_eq!(sys_prctl(current_task, PR_GET_DUMPABLE, 0, 0, 0, 0), Ok(1.into()));
2149
2150            // SUID_DUMP_ROOT not supported.
2151            assert_eq!(sys_prctl(current_task, PR_SET_DUMPABLE, 2, 0, 0, 0), error!(EINVAL));
2152            assert_eq!(sys_prctl(current_task, PR_GET_DUMPABLE, 0, 0, 0, 0), Ok(1.into()));
2153        })
2154        .await;
2155    }
2156
2157    #[::fuchsia::test]
2158    async fn test_sys_getsid() {
2159        spawn_kernel_and_run(async |current_task| {
2160            let kernel = current_task.kernel();
2161            assert_eq!(
2162                current_task.get_tid(),
2163                sys_getsid(&current_task, 0).expect("failed to get sid")
2164            );
2165
2166            let second_task = crate::execution::create_init_child_process(
2167                &kernel.weak_self.upgrade().unwrap(),
2168                TaskCommand::new(b"second task"),
2169                Credentials::with_ids(0, 0),
2170                None,
2171            )
2172            .expect("failed to create second task");
2173            let second_current = AutoReleasableTask::from(second_task);
2174
2175            assert_eq!(
2176                second_current.get_tid(),
2177                sys_getsid(&current_task, second_current.get_tid()).expect("failed to get sid")
2178            );
2179        })
2180        .await;
2181    }
2182
2183    #[::fuchsia::test]
2184    async fn test_get_affinity_size() {
2185        spawn_kernel_and_run(async |current_task| {
2186            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2187            let pid = current_task.get_pid();
2188            assert_eq!(sys_sched_getaffinity(&current_task, pid, 16, mapped_address), Ok(16));
2189            assert_eq!(
2190                sys_sched_getaffinity(&current_task, pid, 1024, mapped_address),
2191                Ok(std::mem::size_of::<CpuSet>())
2192            );
2193            assert_eq!(
2194                sys_sched_getaffinity(&current_task, pid, 1, mapped_address),
2195                error!(EINVAL)
2196            );
2197            assert_eq!(
2198                sys_sched_getaffinity(&current_task, pid, 9, mapped_address),
2199                error!(EINVAL)
2200            );
2201        })
2202        .await;
2203    }
2204
2205    #[::fuchsia::test]
2206    async fn test_set_affinity_size() {
2207        spawn_kernel_and_run(async |current_task| {
2208            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2209            current_task.write_memory(mapped_address, &[0xffu8]).expect("failed to cpumask");
2210            let pid = current_task.get_pid();
2211            assert_eq!(
2212                sys_sched_setaffinity(&current_task, pid, *PAGE_SIZE as u32, mapped_address),
2213                Ok(())
2214            );
2215            assert_eq!(
2216                sys_sched_setaffinity(&current_task, pid, 1, mapped_address),
2217                error!(EINVAL)
2218            );
2219        })
2220        .await;
2221    }
2222
2223    #[::fuchsia::test]
2224    async fn test_task_name() {
2225        spawn_kernel_and_run(async |current_task| {
2226            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2227            let name = "my-task-name\0";
2228            current_task
2229                .write_memory(mapped_address, name.as_bytes())
2230                .expect("failed to write name");
2231
2232            let result =
2233                sys_prctl(current_task, PR_SET_NAME, mapped_address.ptr() as u64, 0, 0, 0).unwrap();
2234            assert_eq!(SUCCESS, result);
2235
2236            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2237            let result =
2238                sys_prctl(current_task, PR_GET_NAME, mapped_address.ptr() as u64, 0, 0, 0).unwrap();
2239            assert_eq!(SUCCESS, result);
2240
2241            let name_length = name.len();
2242
2243            let out_name = current_task.read_memory_to_vec(mapped_address, name_length).unwrap();
2244            assert_eq!(name.as_bytes(), &out_name);
2245        })
2246        .await;
2247    }
2248
2249    #[::fuchsia::test]
2250    async fn test_sched_get_priority_min_max() {
2251        spawn_kernel_and_run(async |current_task| {
2252            let non_rt_min = sys_sched_get_priority_min(&current_task, SCHED_NORMAL).unwrap();
2253            assert_eq!(non_rt_min, 0);
2254            let non_rt_max = sys_sched_get_priority_max(&current_task, SCHED_NORMAL).unwrap();
2255            assert_eq!(non_rt_max, 0);
2256
2257            let rt_min = sys_sched_get_priority_min(&current_task, SCHED_FIFO).unwrap();
2258            assert_eq!(rt_min, 1);
2259            let rt_max = sys_sched_get_priority_max(&current_task, SCHED_FIFO).unwrap();
2260            assert_eq!(rt_max, 99);
2261
2262            let min_bad_policy_error =
2263                sys_sched_get_priority_min(&current_task, u32::MAX).unwrap_err();
2264            assert_eq!(min_bad_policy_error, errno!(EINVAL));
2265
2266            let max_bad_policy_error =
2267                sys_sched_get_priority_max(&current_task, u32::MAX).unwrap_err();
2268            assert_eq!(max_bad_policy_error, errno!(EINVAL));
2269        })
2270        .await;
2271    }
2272
2273    #[::fuchsia::test]
2274    async fn test_sched_setscheduler() {
2275        spawn_kernel_and_run(async |current_task| {
2276            current_task
2277                .thread_group()
2278                .limits
2279                .lock()
2280                .set(Resource::RTPRIO, rlimit { rlim_cur: 255, rlim_max: 255 });
2281
2282            let scheduler = sys_sched_getscheduler(&current_task, 0).unwrap();
2283            assert_eq!(scheduler, SCHED_NORMAL, "tasks should have normal scheduler by default");
2284
2285            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2286            let requested_params = sched_param { sched_priority: 15 };
2287            current_task.write_object(mapped_address.into(), &requested_params).unwrap();
2288
2289            sys_sched_setscheduler(&current_task, 0, SCHED_FIFO, mapped_address.into()).unwrap();
2290
2291            let new_scheduler = sys_sched_getscheduler(&current_task, 0).unwrap();
2292            assert_eq!(new_scheduler, SCHED_FIFO, "task should have been assigned fifo scheduler");
2293
2294            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2295            sys_sched_getparam(&current_task, 0, mapped_address.into()).expect("sched_getparam");
2296            let param_value: sched_param =
2297                current_task.read_object(mapped_address.into()).expect("read_object");
2298            assert_eq!(param_value.sched_priority, 15);
2299        })
2300        .await;
2301    }
2302
2303    #[::fuchsia::test]
2304    async fn test_sched_getparam() {
2305        spawn_kernel_and_run(async |current_task| {
2306            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2307            sys_sched_getparam(&current_task, 0, mapped_address.into()).expect("sched_getparam");
2308            let param_value: sched_param =
2309                current_task.read_object(mapped_address.into()).expect("read_object");
2310            assert_eq!(param_value.sched_priority, 0);
2311        })
2312        .await;
2313    }
2314
2315    #[::fuchsia::test]
2316    async fn test_setuid() {
2317        spawn_kernel_and_run(async |current_task| {
2318            // Test for root.
2319            current_task.set_creds(Credentials::with_ids(0, 0));
2320            sys_setuid(&current_task, 42).expect("setuid");
2321            let mut creds = Credentials::clone(&current_task.current_creds());
2322            assert_eq!(creds.euid, 42);
2323            assert_eq!(creds.uid, 42);
2324            assert_eq!(creds.saved_uid, 42);
2325
2326            // Remove the CAP_SETUID capability to avoid overwriting permission checks.
2327            creds.cap_effective.remove(CAP_SETUID);
2328            current_task.set_creds(creds);
2329
2330            // Test for non root, which task now is.
2331            assert_eq!(sys_setuid(&current_task, 0), error!(EPERM));
2332            assert_eq!(sys_setuid(&current_task, 43), error!(EPERM));
2333
2334            sys_setuid(&current_task, 42).expect("setuid");
2335            assert_eq!(current_task.current_creds().euid, 42);
2336            assert_eq!(current_task.current_creds().uid, 42);
2337            assert_eq!(current_task.current_creds().saved_uid, 42);
2338
2339            // Change uid and saved_uid, and check that one can set the euid to these.
2340            let mut creds = Credentials::clone(&current_task.current_creds());
2341            creds.uid = 41;
2342            creds.euid = 42;
2343            creds.saved_uid = 43;
2344            current_task.set_creds(creds);
2345
2346            sys_setuid(&current_task, 41).expect("setuid");
2347            assert_eq!(current_task.current_creds().euid, 41);
2348            assert_eq!(current_task.current_creds().uid, 41);
2349            assert_eq!(current_task.current_creds().saved_uid, 43);
2350
2351            let mut creds = Credentials::clone(&current_task.current_creds());
2352            creds.uid = 41;
2353            creds.euid = 42;
2354            creds.saved_uid = 43;
2355            current_task.set_creds(creds);
2356
2357            sys_setuid(&current_task, 43).expect("setuid");
2358            assert_eq!(current_task.current_creds().euid, 43);
2359            assert_eq!(current_task.current_creds().uid, 41);
2360            assert_eq!(current_task.current_creds().saved_uid, 43);
2361        })
2362        .await;
2363    }
2364
2365    #[::fuchsia::test]
2366    async fn test_read_c_string_vector() {
2367        spawn_kernel_and_run(async |current_task| {
2368            let arg_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2369            let arg = b"test-arg\0";
2370            current_task.write_memory(arg_addr, arg).expect("failed to write test arg");
2371            let arg_usercstr = UserCString::new(current_task, arg_addr);
2372            let null_usercstr = UserCString::null(current_task);
2373
2374            let argv_addr = UserCStringPtr::new(
2375                current_task,
2376                map_memory(&current_task, UserAddress::default(), *PAGE_SIZE),
2377            );
2378            current_task
2379                .write_multi_arch_ptr(argv_addr.addr(), arg_usercstr)
2380                .expect("failed to write UserCString");
2381            current_task
2382                .write_multi_arch_ptr(argv_addr.next().unwrap().addr(), null_usercstr)
2383                .expect("failed to write UserCString");
2384
2385            // The arguments size limit should include the null terminator.
2386            assert!(read_c_string_vector(&current_task, argv_addr, 100, arg.len()).is_ok());
2387            assert_eq!(
2388                read_c_string_vector(
2389                    &current_task,
2390                    argv_addr,
2391                    100,
2392                    std::str::from_utf8(arg).unwrap().trim_matches('\0').len()
2393                ),
2394                error!(E2BIG)
2395            );
2396        })
2397        .await;
2398    }
2399
2400    #[::fuchsia::test]
2401    async fn test_kcmp() {
2402        spawn_kernel_and_run(async |current_task| {
2403            let pid = current_task.get_pid();
2404            assert_eq!(sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_VM, 0, 0), Ok(0));
2405            assert_eq!(
2406                sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
2407                error!(EOPNOTSUPP)
2408            );
2409            assert_eq!(
2410                sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_SYSVSEM, 1, 2),
2411                error!(EOPNOTSUPP)
2412            );
2413            assert_eq!(
2414                sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_SYSVSEM, u64::MAX, u64::MAX),
2415                error!(EOPNOTSUPP)
2416            );
2417            assert_eq!(
2418                sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_TYPES, 0, 0),
2419                error!(EINVAL)
2420            );
2421            assert_eq!(sys_kcmp(&current_task, pid, pid, u32::MAX, 0, 0), error!(EINVAL));
2422            assert_eq!(
2423                sys_kcmp(&current_task, 0, pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
2424                error!(ESRCH)
2425            );
2426            assert_eq!(
2427                sys_kcmp(&current_task, pid, 0, kcmp_type_KCMP_SYSVSEM, 0, 0),
2428                error!(ESRCH)
2429            );
2430            assert_eq!(
2431                sys_kcmp(&current_task, -1, pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
2432                error!(ESRCH)
2433            );
2434            assert_eq!(
2435                sys_kcmp(&current_task, pid, -1, kcmp_type_KCMP_SYSVSEM, 0, 0),
2436                error!(ESRCH)
2437            );
2438
2439            // Verify ptrace access checks take precedence over resource type errors for both tasks.
2440            let target_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2441            let target_pid = target_task.get_pid();
2442            current_task.set_creds(Credentials::with_ids(1, 1));
2443            assert_eq!(
2444                sys_kcmp(&current_task, target_pid, target_pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
2445                error!(EPERM)
2446            );
2447            assert_eq!(
2448                sys_kcmp(&current_task, pid, target_pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
2449                error!(EPERM)
2450            );
2451            assert_eq!(
2452                sys_kcmp(&current_task, target_pid, pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
2453                error!(EPERM)
2454            );
2455        })
2456        .await;
2457    }
2458}