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