Skip to main content

starnix_core/task/
syscalls.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::execution::execute_task;
6use crate::mm::{DumpPolicy, MemoryAccessor, MemoryAccessorExt, PAGE_SIZE};
7use crate::ptrace::{
8    PR_SET_PTRACER_ANY, PR_SET_PTRACER_ANY_ARCH32, PtraceAllowedPtracers, PtraceAttachType,
9    PtraceOptions, ptrace_attach, ptrace_dispatch, ptrace_traceme,
10};
11use crate::security;
12use crate::signals::syscalls::RUsagePtr;
13use crate::task::{
14    CurrentTask, ExitStatus, NormalPriority, SchedulingPolicy, SeccompAction, SeccompStateValue,
15    SyslogAccess, Task, ThreadGroup, max_priority_for_sched_policy, min_priority_for_sched_policy,
16};
17use crate::vfs::{
18    FdNumber, FileHandle, MountNamespaceFile, PidFdFileObject, UserBuffersOutputBuffer,
19    VecOutputBuffer,
20};
21use starnix_logging::{log_error, log_info, log_trace, track_stub};
22use starnix_syscalls::SyscallResult;
23use starnix_task_command::TaskCommand;
24use starnix_types::time::timeval_from_duration;
25use starnix_uapi::auth::{
26    CAP_SETGID, CAP_SETPCAP, CAP_SETUID, CAP_SYS_ADMIN, CAP_SYS_NICE, CAP_SYS_RESOURCE,
27    CAP_SYS_TTY_CONFIG, Capabilities, Credentials, PTRACE_MODE_READ_REALCREDS, SecureBits,
28};
29use starnix_uapi::errors::{ENAMETOOLONG, Errno};
30use starnix_uapi::kcmp::KcmpResource;
31use starnix_uapi::open_flags::OpenFlags;
32use starnix_uapi::resource_limits::Resource;
33use starnix_uapi::signals::{Signal, UncheckedSignal};
34use starnix_uapi::syslog::SyslogAction;
35use starnix_uapi::user_address::{
36    ArchSpecific, MappingMultiArchUserRef, MultiArchUserRef, UserAddress, UserCString,
37    UserCStringPtr, UserRef,
38};
39use starnix_uapi::{
40    __user_cap_data_struct, __user_cap_header_struct, _LINUX_CAPABILITY_VERSION_1,
41    _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_VERSION_3, AT_EMPTY_PATH, AT_SYMLINK_NOFOLLOW,
42    BPF_MAXINSNS, CLONE_ARGS_SIZE_VER0, CLONE_ARGS_SIZE_VER1, CLONE_ARGS_SIZE_VER2, CLONE_FILES,
43    CLONE_FS, CLONE_NEWNS, CLONE_NEWUTS, CLONE_SETTLS, CLONE_VFORK, NGROUPS_MAX, PR_CAP_AMBIENT,
44    PR_CAP_AMBIENT_CLEAR_ALL, PR_CAP_AMBIENT_IS_SET, PR_CAP_AMBIENT_LOWER, PR_CAP_AMBIENT_RAISE,
45    PR_CAPBSET_DROP, PR_CAPBSET_READ, PR_GET_CHILD_SUBREAPER, PR_GET_DUMPABLE, PR_GET_KEEPCAPS,
46    PR_GET_NAME, PR_GET_NO_NEW_PRIVS, PR_GET_PDEATHSIG, PR_GET_SECCOMP, PR_GET_SECUREBITS,
47    PR_GET_TIMERSLACK, PR_SET_CHILD_SUBREAPER, PR_SET_DUMPABLE, PR_SET_KEEPCAPS, PR_SET_NAME,
48    PR_SET_NO_NEW_PRIVS, PR_SET_PDEATHSIG, PR_SET_PTRACER, PR_SET_SECCOMP, PR_SET_SECUREBITS,
49    PR_SET_TIMERSLACK, PR_SET_VMA, PR_SET_VMA_ANON_NAME, PRIO_PROCESS, PTRACE_ATTACH, PTRACE_SEIZE,
50    PTRACE_TRACEME, RUSAGE_CHILDREN, SCHED_RESET_ON_FORK, SECCOMP_FILTER_FLAG_LOG,
51    SECCOMP_FILTER_FLAG_NEW_LISTENER, SECCOMP_FILTER_FLAG_SPEC_ALLOW, SECCOMP_FILTER_FLAG_TSYNC,
52    SECCOMP_FILTER_FLAG_TSYNC_ESRCH, SECCOMP_GET_ACTION_AVAIL, SECCOMP_GET_NOTIF_SIZES,
53    SECCOMP_MODE_FILTER, SECCOMP_MODE_STRICT, SECCOMP_SET_MODE_FILTER, SECCOMP_SET_MODE_STRICT,
54    c_char, c_int, clone_args, errno, error, gid_t, pid_t, rusage, sched_param, sock_filter, uapi,
55    uid_t,
56};
57use static_assertions::const_assert;
58use std::cmp;
59use std::ffi::CString;
60use std::sync::{Arc, LazyLock};
61use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
62
63#[cfg(target_arch = "aarch64")]
64use starnix_uapi::{PR_GET_TAGGED_ADDR_CTRL, PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE};
65
66pub type SockFProgPtr =
67    MappingMultiArchUserRef<SockFProg, uapi::sock_fprog, uapi::arch32::sock_fprog>;
68pub type SockFilterPtr = MultiArchUserRef<uapi::sock_filter, uapi::arch32::sock_filter>;
69
70pub struct SockFProg {
71    pub len: u32,
72    pub filter: SockFilterPtr,
73}
74
75uapi::arch_map_data! {
76    BidiTryFrom<SockFProg, sock_fprog> {
77        len = len;
78        filter = filter;
79    }
80}
81
82uapi::check_arch_independent_layout! {
83    sched_param {
84        sched_priority,
85    }
86}
87
88pub fn do_clone(current_task: &mut CurrentTask, args: &clone_args) -> Result<pid_t, Errno> {
89    security::check_task_create_access(current_task)?;
90
91    let child_exit_signal = if args.exit_signal == 0 {
92        None
93    } else {
94        Some(Signal::try_from(UncheckedSignal::new(args.exit_signal))?)
95    };
96
97    let mut new_task = current_task.clone_task(
98        args.flags,
99        child_exit_signal,
100        UserRef::<pid_t>::new(UserAddress::from(args.parent_tid)),
101        UserRef::<pid_t>::new(UserAddress::from(args.child_tid)),
102        UserRef::<FdNumber>::new(UserAddress::from(args.pidfd)),
103    )?;
104
105    // Set the result register to 0 for the return value from clone in the
106    // cloned process.
107    new_task.thread_state.registers.set_return_register(0);
108
109    if args.stack != 0 {
110        // In clone() the `stack` argument points to the top of the stack, while in clone3()
111        // `stack` points to the bottom of the stack. Therefore, in clone3() we need to add
112        // `stack_size` to calculate the stack pointer. Note that in clone() `stack_size` is 0.
113        new_task
114            .thread_state
115            .registers
116            .set_stack_pointer_register(args.stack.wrapping_add(args.stack_size));
117    }
118
119    if args.flags & (CLONE_SETTLS as u64) != 0 {
120        new_task.thread_state.registers.set_thread_pointer_register(args.tls);
121    }
122
123    let tid = new_task.task.tid.id;
124    let task_ref = Arc::downgrade(&new_task.task);
125    let trace_kind = current_task.ptrace().on_clone(args, &new_task.task);
126    execute_task(new_task, |_| Ok(()), |_| {})?;
127
128    current_task.ptrace().on_event(trace_kind, tid as u64);
129
130    if args.flags & (CLONE_VFORK as u64) != 0 {
131        current_task.wait_for_execve(task_ref)?;
132        current_task.ptrace().on_event(PtraceOptions::TRACEVFORKDONE, tid as u64);
133    }
134
135    Ok(tid)
136}
137
138pub fn sys_clone3(
139    current_task: &mut CurrentTask,
140    user_clone_args: UserRef<clone_args>,
141    user_clone_args_size: usize,
142) -> Result<pid_t, Errno> {
143    // Only these specific sized versions are supported.
144    if !(user_clone_args_size == CLONE_ARGS_SIZE_VER0 as usize
145        || user_clone_args_size == CLONE_ARGS_SIZE_VER1 as usize
146        || user_clone_args_size == CLONE_ARGS_SIZE_VER2 as usize)
147    {
148        return error!(EINVAL);
149    }
150
151    // The most recent version of the struct size should match our definition.
152    const_assert!(std::mem::size_of::<clone_args>() == CLONE_ARGS_SIZE_VER2 as usize);
153
154    let clone_args = current_task.read_object_partial(user_clone_args, user_clone_args_size)?;
155    do_clone(current_task, &clone_args)
156}
157
158fn read_c_string_vector(
159    mm: &CurrentTask,
160    user_vector: UserCStringPtr,
161    elem_limit: usize,
162    vec_limit: usize,
163) -> Result<(Vec<CString>, usize), Errno> {
164    let mut user_current = user_vector;
165    let mut vector: Vec<CString> = vec![];
166    let mut vec_size: usize = 0;
167    loop {
168        let user_string = mm.read_multi_arch_ptr(user_current)?;
169        if user_string.is_null() {
170            break;
171        }
172        let string = mm
173            .read_c_string_to_vec(user_string, elem_limit)
174            .map_err(|e| if e.code == ENAMETOOLONG { errno!(E2BIG) } else { e })?;
175        let cstring = CString::new(string).map_err(|_| errno!(EINVAL))?;
176        vec_size =
177            vec_size.checked_add(cstring.as_bytes_with_nul().len()).ok_or_else(|| errno!(E2BIG))?;
178        if vec_size > vec_limit {
179            return error!(E2BIG);
180        }
181        vector.push(cstring);
182        user_current = user_current.next()?;
183    }
184    Ok((vector, vec_size))
185}
186
187pub fn sys_execve(
188    current_task: &mut CurrentTask,
189    user_path: UserCString,
190    user_argv: UserCStringPtr,
191    user_environ: UserCStringPtr,
192) -> Result<(), Errno> {
193    sys_execveat(current_task, FdNumber::AT_FDCWD, user_path, user_argv, user_environ, 0)
194}
195
196pub fn sys_execveat(
197    current_task: &mut CurrentTask,
198    dir_fd: FdNumber,
199    user_path: UserCString,
200    user_argv: UserCStringPtr,
201    user_environ: UserCStringPtr,
202    flags: u32,
203) -> Result<(), Errno> {
204    if flags & !(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW) != 0 {
205        return error!(EINVAL);
206    }
207
208    // Calculate the limit for argv and environ size as 1/4 of the stack size, floored at 32 pages
209    // and capped at 3/4 of _STK_LIM.
210    // See the Limits sections in https://man7.org/linux/man-pages/man2/execve.2.html
211    const PAGE_LIMIT: usize = 32;
212    let page_limit_size: usize = PAGE_LIMIT * *PAGE_SIZE as usize;
213    let rlimit = current_task.thread_group().get_rlimit(Resource::STACK);
214    let stack_limit = cmp::min(rlimit / 4, uapi::_STK_LIM as u64 / 4 * 3);
215    let argv_env_limit = cmp::max(page_limit_size, stack_limit as usize);
216
217    // The limit per argument or environment variable is 32 pages.
218    // See the Limits sections in https://man7.org/linux/man-pages/man2/execve.2.html
219    let (argv, argv_size) = if user_argv.is_null() {
220        (Vec::new(), 0)
221    } else {
222        read_c_string_vector(current_task, user_argv, page_limit_size, argv_env_limit)?
223    };
224
225    let (environ, _) = if user_environ.is_null() {
226        (Vec::new(), 0)
227    } else {
228        read_c_string_vector(
229            current_task,
230            user_environ,
231            page_limit_size,
232            argv_env_limit - argv_size,
233        )?
234    };
235
236    let path = &current_task.read_path(user_path)?;
237
238    log_trace!(argv:?, environ:?, flags:?; "execveat({dir_fd}, {path})");
239
240    let mut open_flags = OpenFlags::empty();
241
242    if flags & AT_SYMLINK_NOFOLLOW != 0 {
243        open_flags |= OpenFlags::NOFOLLOW;
244    }
245
246    if path.is_empty() && flags & AT_EMPTY_PATH == 0 {
247        // If AT_EMPTY_PATH is not set, an empty path is an error.
248        return error!(ENOENT);
249    }
250
251    let executable = current_task.open_file_for_exec(dir_fd, path.as_ref(), open_flags)?;
252
253    // This path can affect script resolution (the path is appended to the script args)
254    // and the auxiliary value `AT_EXECFN` from the syscall `getauxval()`
255    let path = if dir_fd == FdNumber::AT_FDCWD {
256        // The file descriptor is CWD, so the path is exactly
257        // what the user specified.
258        path.to_vec()
259    } else {
260        // The path is `/dev/fd/N/P` where N is the file descriptor
261        // number and P is the user-provided path (if relative and non-empty).
262        //
263        // See https://man7.org/linux/man-pages/man2/execveat.2.html#NOTES
264        match path.first() {
265            Some(b'/') => {
266                // The user-provided path is absolute, so dir_fd is ignored.
267                path.to_vec()
268            }
269            Some(_) => {
270                // User-provided path is relative, append it.
271                let mut new_path = format!("/dev/fd/{}/", dir_fd.raw()).into_bytes();
272                new_path.append(&mut path.to_vec());
273                new_path
274            }
275            // User-provided path is empty
276            None => format!("/dev/fd/{}", dir_fd.raw()).into_bytes(),
277        }
278    };
279
280    let path = CString::new(path).map_err(|_| errno!(EINVAL))?;
281
282    current_task.exec(executable, path, argv, environ)?;
283    Ok(())
284}
285
286pub fn sys_getcpu(
287    current_task: &CurrentTask,
288    cpu_out: UserRef<u32>,
289    node_out: UserRef<u32>,
290) -> Result<(), Errno> {
291    // "When either cpu or node is NULL nothing is written to the respective pointer."
292    // from https://man7.org/linux/man-pages/man2/getcpu.2.html
293    if !cpu_out.is_null() {
294        let thread_stats = current_task
295            .running_state()
296            .thread
297            .get()
298            .expect("current thread is never None when executing")
299            .thread
300            .stats()
301            .map_err(|e| errno!(EINVAL, format!("getting thread stats failed {e:?}")))?;
302        current_task.write_object(cpu_out, &thread_stats.last_scheduled_cpu)?;
303    }
304    if !node_out.is_null() {
305        // Zircon does not yet have a concept of NUMA task scheduling, always tell userspace that
306        // it's on the "first" node which should be true for non-NUMA systems.
307        track_stub!(TODO("https://fxbug.dev/325643815"), "getcpu() numa node");
308        current_task.write_object(node_out, &0)?;
309    }
310    Ok(())
311}
312
313pub fn sys_getpid(current_task: &CurrentTask) -> Result<pid_t, Errno> {
314    Ok(current_task.get_pid())
315}
316
317pub fn sys_gettid(current_task: &CurrentTask) -> Result<pid_t, Errno> {
318    Ok(current_task.get_tid())
319}
320
321pub fn sys_getppid(current_task: &CurrentTask) -> Result<pid_t, Errno> {
322    Ok(current_task.thread_group().read().get_ppid())
323}
324
325fn get_task_or_current(current_task: &CurrentTask, pid: pid_t) -> Result<Arc<Task>, Errno> {
326    if pid == 0 { Ok(current_task.task.clone()) } else { current_task.get_task(pid) }
327}
328
329pub fn sys_getsid(current_task: &CurrentTask, pid: pid_t) -> Result<pid_t, Errno> {
330    let target_task = get_task_or_current(current_task, pid)?;
331    if pid != 0 {
332        security::check_task_getsid(current_task, &target_task)?;
333    }
334    let sid = target_task.thread_group().read().session.id;
335    Ok(sid)
336}
337
338pub fn sys_getpgid(current_task: &CurrentTask, pid: pid_t) -> Result<pid_t, Errno> {
339    let task = get_task_or_current(current_task, pid)?;
340
341    security::check_getpgid_access(current_task, &task)?;
342    Ok(task.thread_group().pgid().id)
343}
344
345pub fn sys_setpgid(current_task: &CurrentTask, pid: pid_t, pgid: pid_t) -> Result<(), Errno> {
346    if pgid < 0 {
347        return error!(EINVAL);
348    }
349    let task = get_task_or_current(current_task, pid)?;
350    let pgid = if pgid == 0 {
351        task.pid.clone()
352    } else {
353        current_task.kernel().pids.get(pgid).map_err(|_| errno!(EPERM))?
354    };
355
356    current_task.thread_group().setpgid(current_task, &task, &pgid)
357}
358
359impl CurrentTask {
360    /// Returns true if the `current_task`'s effective user ID (EUID) is the same as the
361    /// EUID or UID of the `target_task`. We describe this as the current task being
362    /// "EUID-friendly" to the target and it enables actions to be performed that would
363    /// otherwise require additional privileges.
364    ///
365    /// See "The caller needs an effective user ID equal to the real user ID or effective
366    /// user ID of the [target]" at sched_setaffinity(2), comparable language at
367    /// setpriority(2), more ambiguous language at sched_setscheduler(2), and no
368    /// particular specification at sched_setparam(2).
369    fn is_euid_friendly_with(&self, target_task: &Task) -> bool {
370        let self_creds = self.current_creds();
371        let target_creds = target_task.real_creds();
372        self_creds.euid == target_creds.uid || self_creds.euid == target_creds.euid
373    }
374}
375
376// A non-root process is allowed to set any of its three uids to the value of any other. The
377// CAP_SETUID capability bypasses these checks and allows setting any uid to any integer. Likewise
378// for gids.
379fn new_uid_allowed(current_task: &CurrentTask, uid: uid_t) -> bool {
380    let current_creds = current_task.current_creds();
381    uid == current_creds.uid
382        || uid == current_creds.euid
383        || uid == current_creds.saved_uid
384        || security::is_task_capable_noaudit(current_task, CAP_SETUID)
385}
386
387fn new_gid_allowed(current_task: &CurrentTask, gid: gid_t) -> bool {
388    let current_creds = current_task.current_creds();
389    gid == current_creds.gid
390        || gid == current_creds.egid
391        || gid == current_creds.saved_gid
392        || security::is_task_capable_noaudit(current_task, CAP_SETGID)
393}
394
395pub fn sys_getuid(current_task: &CurrentTask) -> Result<uid_t, Errno> {
396    Ok(current_task.current_creds().uid)
397}
398
399pub fn sys_getgid(current_task: &CurrentTask) -> Result<gid_t, Errno> {
400    Ok(current_task.current_creds().gid)
401}
402
403pub fn sys_setuid(current_task: &CurrentTask, uid: uid_t) -> Result<(), Errno> {
404    if uid == uid_t::MAX {
405        return error!(EINVAL);
406    }
407    if !new_uid_allowed(&current_task, uid) {
408        return error!(EPERM);
409    }
410
411    let prev = current_task.current_creds();
412    let mut creds = Credentials::clone(&prev);
413    creds.euid = uid;
414    creds.fsuid = uid;
415    if security::is_task_capable_noaudit(current_task, CAP_SETUID) {
416        creds.uid = uid;
417        creds.saved_uid = uid;
418    }
419
420    creds.update_capabilities(&prev);
421    std::mem::drop(prev);
422    current_task.set_creds(creds);
423    Ok(())
424}
425
426pub fn sys_setgid(current_task: &CurrentTask, gid: gid_t) -> Result<(), Errno> {
427    if gid == gid_t::MAX {
428        return error!(EINVAL);
429    }
430    if !new_gid_allowed(&current_task, gid) {
431        return error!(EPERM);
432    }
433
434    let mut creds = Credentials::clone(&current_task.current_creds());
435    creds.egid = gid;
436    creds.fsgid = gid;
437    if security::is_task_capable_noaudit(current_task, CAP_SETGID) {
438        creds.gid = gid;
439        creds.saved_gid = gid;
440    }
441    current_task.set_creds(creds);
442    Ok(())
443}
444
445pub fn sys_geteuid(current_task: &CurrentTask) -> Result<uid_t, Errno> {
446    Ok(current_task.current_creds().euid)
447}
448
449pub fn sys_getegid(current_task: &CurrentTask) -> Result<gid_t, Errno> {
450    Ok(current_task.current_creds().egid)
451}
452
453pub fn sys_setfsuid(current_task: &CurrentTask, fsuid: uid_t) -> Result<uid_t, Errno> {
454    let prev = current_task.current_creds();
455    let prev_fsuid = prev.fsuid;
456    if fsuid != u32::MAX && new_uid_allowed(&current_task, fsuid) {
457        let mut creds = Credentials::clone(&prev);
458        creds.fsuid = fsuid;
459        creds.update_capabilities(&prev);
460        std::mem::drop(prev);
461        current_task.set_creds(creds);
462    }
463
464    Ok(prev_fsuid)
465}
466
467pub fn sys_setfsgid(current_task: &CurrentTask, fsgid: gid_t) -> Result<gid_t, Errno> {
468    let prev = current_task.current_creds();
469    let prev_fsgid = prev.fsgid;
470
471    if fsgid != u32::MAX && new_gid_allowed(&current_task, fsgid) {
472        let mut creds = Credentials::clone(&prev);
473        creds.fsgid = fsgid;
474        creds.update_capabilities(&prev);
475        std::mem::drop(prev);
476        current_task.set_creds(creds);
477    }
478
479    Ok(prev_fsgid)
480}
481
482pub fn sys_getresuid(
483    current_task: &CurrentTask,
484    ruid_addr: UserRef<uid_t>,
485    euid_addr: UserRef<uid_t>,
486    suid_addr: UserRef<uid_t>,
487) -> Result<(), Errno> {
488    let creds = current_task.current_creds();
489    current_task.write_object(ruid_addr, &creds.uid)?;
490    current_task.write_object(euid_addr, &creds.euid)?;
491    current_task.write_object(suid_addr, &creds.saved_uid)?;
492    Ok(())
493}
494
495pub fn sys_getresgid(
496    current_task: &CurrentTask,
497    rgid_addr: UserRef<gid_t>,
498    egid_addr: UserRef<gid_t>,
499    sgid_addr: UserRef<gid_t>,
500) -> Result<(), Errno> {
501    let creds = current_task.current_creds();
502    current_task.write_object(rgid_addr, &creds.gid)?;
503    current_task.write_object(egid_addr, &creds.egid)?;
504    current_task.write_object(sgid_addr, &creds.saved_gid)?;
505    Ok(())
506}
507
508pub fn sys_setreuid(current_task: &CurrentTask, ruid: uid_t, euid: uid_t) -> Result<(), Errno> {
509    // Linux __sys_setreuid() uses asymmetric checks: ruid cannot be set
510    // to saved_uid, while euid can. This prevents regaining root via
511    // setreuid after a privilege drop when setresuid would be required.
512    let validate_ruid = |uid: uid_t| {
513        let creds = current_task.current_creds();
514        uid == u32::MAX
515            || uid == creds.uid
516            || uid == creds.euid
517            || security::is_task_capable_noaudit(current_task, CAP_SETUID)
518    };
519    let validate_euid = |uid: uid_t| {
520        let creds = current_task.current_creds();
521        uid == u32::MAX
522            || uid == creds.uid
523            || uid == creds.euid
524            || uid == creds.saved_uid
525            || security::is_task_capable_noaudit(current_task, CAP_SETUID)
526    };
527    if !validate_ruid(ruid) || !validate_euid(euid) {
528        return error!(EPERM);
529    }
530
531    let prev = current_task.current_creds();
532    let mut creds = Credentials::clone(&prev);
533    let is_ruid_set = ruid != u32::MAX;
534    if is_ruid_set {
535        creds.uid = ruid;
536    }
537    let is_euid_set = euid != u32::MAX;
538    if is_euid_set {
539        creds.euid = euid;
540        creds.fsuid = euid;
541    }
542
543    // If the real user ID is set (i.e., ruid is not -1) or the effective
544    // user ID is set to a value not equal to the previous real user ID,
545    // the saved set-user-ID will be set to the new effective user ID.
546    if is_ruid_set || (is_euid_set && euid != prev.uid) {
547        creds.saved_uid = creds.euid;
548    }
549
550    creds.update_capabilities(&prev);
551    std::mem::drop(prev);
552    current_task.set_creds(creds);
553    Ok(())
554}
555
556pub fn sys_setregid(current_task: &CurrentTask, rgid: gid_t, egid: gid_t) -> Result<(), Errno> {
557    // Same asymmetric permission model as setreuid - see above.
558    let validate_rgid = |gid: gid_t| {
559        let creds = current_task.current_creds();
560        gid == u32::MAX
561            || gid == creds.gid
562            || gid == creds.egid
563            || security::is_task_capable_noaudit(current_task, CAP_SETGID)
564    };
565    let validate_egid = |gid: gid_t| {
566        let creds = current_task.current_creds();
567        gid == u32::MAX
568            || gid == creds.gid
569            || gid == creds.egid
570            || gid == creds.saved_gid
571            || security::is_task_capable_noaudit(current_task, CAP_SETGID)
572    };
573    if !validate_rgid(rgid) || !validate_egid(egid) {
574        return error!(EPERM);
575    }
576
577    let mut creds = Credentials::clone(&current_task.current_creds());
578    let previous_rgid = creds.gid;
579    let is_rgid_set = rgid != u32::MAX;
580    if is_rgid_set {
581        creds.gid = rgid;
582    }
583    let is_egid_set = egid != u32::MAX;
584    if is_egid_set {
585        creds.egid = egid;
586        creds.fsgid = egid;
587    }
588
589    // If the real group ID is set (i.e., rgid is not -1) or the effective
590    // group ID is set to a value not equal to the previous real group ID,
591    // the saved set-group-ID will be set to the new effective group ID.
592    if is_rgid_set || (is_egid_set && egid != previous_rgid) {
593        creds.saved_gid = creds.egid;
594    }
595
596    current_task.set_creds(creds);
597    Ok(())
598}
599
600pub fn sys_setresuid(
601    current_task: &CurrentTask,
602    ruid: uid_t,
603    euid: uid_t,
604    suid: uid_t,
605) -> Result<(), Errno> {
606    let allowed = |uid| uid == u32::MAX || new_uid_allowed(&current_task, uid);
607    if !allowed(ruid) || !allowed(euid) || !allowed(suid) {
608        return error!(EPERM);
609    }
610
611    let prev = current_task.current_creds();
612    let mut creds = Credentials::clone(&prev);
613    if ruid != u32::MAX {
614        creds.uid = ruid;
615    }
616    if euid != u32::MAX {
617        creds.euid = euid;
618        creds.fsuid = euid;
619    }
620    if suid != u32::MAX {
621        creds.saved_uid = suid;
622    }
623    creds.update_capabilities(&prev);
624    std::mem::drop(prev);
625    current_task.set_creds(creds);
626    Ok(())
627}
628
629pub fn sys_setresgid(
630    current_task: &CurrentTask,
631    rgid: gid_t,
632    egid: gid_t,
633    sgid: gid_t,
634) -> Result<(), Errno> {
635    let allowed = |gid| gid == u32::MAX || new_gid_allowed(&current_task, gid);
636    if !allowed(rgid) || !allowed(egid) || !allowed(sgid) {
637        return error!(EPERM);
638    }
639
640    let mut creds = Credentials::clone(&current_task.current_creds());
641    if rgid != u32::MAX {
642        creds.gid = rgid;
643    }
644    if egid != u32::MAX {
645        creds.egid = egid;
646        creds.fsgid = egid;
647    }
648    if sgid != u32::MAX {
649        creds.saved_gid = sgid;
650    }
651    current_task.set_creds(creds);
652    Ok(())
653}
654
655pub fn sys_exit(current_task: &CurrentTask, code: i32) -> Result<(), Errno> {
656    // Only change the current exit status if this has not been already set by exit_group, as
657    // otherwise it has priority.
658    current_task.write().set_exit_status_if_not_already(ExitStatus::Exit(code as u8));
659    Ok(())
660}
661
662pub fn sys_exit_group(current_task: &mut CurrentTask, code: i32) -> Result<(), Errno> {
663    current_task.kill_thread_group(ExitStatus::Exit(code as u8));
664    Ok(())
665}
666
667pub fn sys_sched_getscheduler(current_task: &CurrentTask, pid: pid_t) -> Result<u32, Errno> {
668    if pid < 0 {
669        return error!(EINVAL);
670    }
671
672    let target_task = get_task_or_current(current_task, pid)?;
673    security::check_task_getscheduler_access(current_task, target_task.as_ref())?;
674    Ok(target_task.scheduler_state.lock().policy_for_sched_getscheduler())
675}
676
677pub fn sys_sched_setscheduler(
678    current_task: &CurrentTask,
679    pid: pid_t,
680    policy: u32,
681    param: UserRef<sched_param>,
682) -> Result<(), Errno> {
683    // Parse & validate the arguments.
684    if pid < 0 || param.is_null() {
685        return error!(EINVAL);
686    }
687
688    let target_task = get_task_or_current(current_task, pid)?;
689
690    let reset_on_fork = policy & SCHED_RESET_ON_FORK != 0;
691
692    let policy = SchedulingPolicy::try_from(policy & !SCHED_RESET_ON_FORK)?;
693    let realtime_priority =
694        policy.realtime_priority_from(current_task.read_object(param)?.sched_priority)?;
695
696    // TODO: https://fxbug.dev/425143440 - we probably want to improve the locking here.
697    let (current_policy, current_realtime_priority, current_normal_priority, current_reset_on_fork) = {
698        let state = target_task.scheduler_state.lock();
699        (state.policy, state.realtime_priority, state.normal_priority, state.reset_on_fork)
700    };
701
702    // Check capabilities and permissions, if required, for the operation.
703    let euid_friendly = current_task.is_euid_friendly_with(&target_task);
704    let strengthening = current_realtime_priority < realtime_priority;
705    let rlimited = strengthening
706        && realtime_priority.exceeds(target_task.thread_group().get_rlimit(Resource::RTPRIO));
707    let clearing_reset_on_fork = current_reset_on_fork && !reset_on_fork;
708    let caught_in_idle_trap = current_policy == SchedulingPolicy::Idle
709        && policy != SchedulingPolicy::Idle
710        && current_normal_priority.exceeds(target_task.thread_group().get_rlimit(Resource::NICE));
711    if !euid_friendly || rlimited || clearing_reset_on_fork || caught_in_idle_trap {
712        security::check_task_capable(current_task, CAP_SYS_NICE)?;
713    }
714
715    security::check_task_setscheduler_access(current_task, &target_task)?;
716
717    // Apply the new scheduler configuration to the task.
718    target_task.set_scheduler_policy_priority_and_reset_on_fork(
719        policy,
720        realtime_priority,
721        reset_on_fork,
722    )?;
723
724    Ok(())
725}
726
727const CPU_SET_SIZE: usize = 128;
728
729#[repr(C)]
730#[derive(Debug, Copy, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
731pub struct CpuSet {
732    bits: [u8; CPU_SET_SIZE],
733}
734
735impl Default for CpuSet {
736    fn default() -> Self {
737        Self { bits: [0; CPU_SET_SIZE] }
738    }
739}
740
741fn check_cpu_set_alignment(current_task: &CurrentTask, cpusetsize: u32) -> Result<(), Errno> {
742    let alignment = if current_task.is_arch32() { 4 } else { 8 };
743    if cpusetsize < alignment || cpusetsize % alignment != 0 {
744        return error!(EINVAL);
745    }
746    Ok(())
747}
748
749fn get_default_cpu_set() -> CpuSet {
750    let mut result = CpuSet::default();
751    let mut cpus_count = zx::system_get_num_cpus();
752    let cpus_count_max = (CPU_SET_SIZE * 8) as u32;
753    if cpus_count > cpus_count_max {
754        log_error!("cpus_count={cpus_count}, greater than the {cpus_count_max} max supported.");
755        cpus_count = cpus_count_max;
756    }
757    let mut index = 0;
758    while cpus_count > 0 {
759        let count = std::cmp::min(cpus_count, 8);
760        let (shl, overflow) = 1_u8.overflowing_shl(count);
761        let mask = if overflow { u8::MAX } else { shl - 1 };
762        result.bits[index] = mask;
763        index += 1;
764        cpus_count -= count;
765    }
766    result
767}
768
769pub fn sys_sched_getaffinity(
770    current_task: &CurrentTask,
771    pid: pid_t,
772    cpusetsize: u32,
773    user_mask: UserAddress,
774) -> Result<usize, Errno> {
775    if pid < 0 {
776        return error!(EINVAL);
777    }
778
779    check_cpu_set_alignment(current_task, cpusetsize)?;
780
781    let target_task = get_task_or_current(current_task, pid)?;
782    security::check_task_getscheduler_access(current_task, &target_task)?;
783
784    // sched_setaffinity() is not implemented. Fake affinity mask based on the number of CPUs.
785    let mask = get_default_cpu_set();
786    let mask_size = std::cmp::min(cpusetsize as usize, CPU_SET_SIZE);
787    current_task.write_memory(user_mask, &mask.bits[..mask_size])?;
788    track_stub!(TODO("https://fxbug.dev/322874659"), "sched_getaffinity");
789    Ok(mask_size)
790}
791
792pub fn sys_sched_setaffinity(
793    current_task: &CurrentTask,
794    pid: pid_t,
795    cpusetsize: u32,
796    user_mask: UserAddress,
797) -> Result<(), Errno> {
798    if pid < 0 {
799        return error!(EINVAL);
800    }
801    let target_task = get_task_or_current(current_task, pid)?;
802
803    check_cpu_set_alignment(current_task, cpusetsize)?;
804
805    let mask_size = std::cmp::min(cpusetsize as usize, CPU_SET_SIZE);
806    let mut mask = CpuSet::default();
807    current_task.read_memory_to_slice(user_mask, &mut mask.bits[..mask_size])?;
808
809    // Specified mask must include at least one valid CPU.
810    let max_mask = get_default_cpu_set();
811    let mut has_valid_cpu_in_mask = false;
812    for (l1, l2) in std::iter::zip(max_mask.bits, mask.bits) {
813        has_valid_cpu_in_mask = has_valid_cpu_in_mask || (l1 & l2 > 0);
814    }
815    if !has_valid_cpu_in_mask {
816        return error!(EINVAL);
817    }
818
819    if !current_task.is_euid_friendly_with(&target_task) {
820        security::check_task_capable(current_task, CAP_SYS_NICE)?;
821    }
822
823    security::check_task_setscheduler_access(current_task, &target_task)?;
824
825    // Currently, we ignore the mask and act as if the system reset the mask
826    // immediately to allowing all CPUs.
827    track_stub!(TODO("https://fxbug.dev/322874889"), "sched_setaffinity");
828    Ok(())
829}
830
831pub fn sys_sched_getparam(
832    current_task: &CurrentTask,
833    pid: pid_t,
834    param: UserRef<sched_param>,
835) -> Result<(), Errno> {
836    if pid < 0 || param.is_null() {
837        return error!(EINVAL);
838    }
839
840    let target_task = get_task_or_current(current_task, pid)?;
841    let param_value = target_task.scheduler_state.lock().get_sched_param();
842    current_task.write_object(param, &param_value)?;
843    Ok(())
844}
845
846pub fn sys_sched_setparam(
847    current_task: &CurrentTask,
848    pid: pid_t,
849    param: UserRef<sched_param>,
850) -> Result<(), Errno> {
851    // Parse & validate the arguments.
852    if pid < 0 || param.is_null() {
853        return error!(EINVAL);
854    }
855    let target_task = get_task_or_current(current_task, pid)?;
856    let param = current_task.read_object(param)?;
857
858    // TODO: https://fxbug.dev/425143440 - we probably want to improve the locking here.
859    let (policy, current_priority) = {
860        let state = target_task.scheduler_state.lock();
861        (state.policy, state.realtime_priority)
862    };
863
864    let realtime_priority = policy.realtime_priority_from(param.sched_priority)?;
865
866    // Check capabilities and permissions, if required, for the operation.
867    let euid_friendly = current_task.is_euid_friendly_with(&target_task);
868    let strengthening = current_priority < realtime_priority;
869    let rlimited = strengthening
870        && realtime_priority.exceeds(target_task.thread_group().get_rlimit(Resource::RTPRIO));
871    if !euid_friendly || rlimited {
872        security::check_task_capable(current_task, CAP_SYS_NICE)?;
873    }
874
875    security::check_task_setscheduler_access(current_task, &target_task)?;
876
877    // Apply the new scheduler configuration to the task.
878    target_task.set_scheduler_priority(realtime_priority)?;
879
880    Ok(())
881}
882
883pub fn sys_sched_get_priority_min(_ctx: &CurrentTask, policy: u32) -> Result<u8, Errno> {
884    min_priority_for_sched_policy(policy)
885}
886
887pub fn sys_sched_get_priority_max(_ctx: &CurrentTask, policy: u32) -> Result<u8, Errno> {
888    max_priority_for_sched_policy(policy)
889}
890
891pub fn sys_ioprio_set(
892    _current_task: &mut CurrentTask,
893    _which: i32,
894    _who: i32,
895    _ioprio: i32,
896) -> Result<(), Errno> {
897    track_stub!(TODO("https://fxbug.dev/297591758"), "ioprio_set()");
898    error!(ENOSYS)
899}
900
901pub fn sys_prctl(
902    current_task: &mut CurrentTask,
903    option: u32,
904    arg2: u64,
905    arg3: u64,
906    arg4: u64,
907    arg5: u64,
908) -> Result<SyscallResult, Errno> {
909    match option {
910        PR_SET_VMA => {
911            if arg2 != PR_SET_VMA_ANON_NAME as u64 {
912                track_stub!(TODO("https://fxbug.dev/322874826"), "prctl PR_SET_VMA", arg2);
913                return error!(ENOSYS);
914            }
915            let addr = UserAddress::from(arg3);
916            let length = arg4 as usize;
917            let name_addr = UserAddress::from(arg5);
918            let name = if name_addr.is_null() {
919                None
920            } else {
921                let name = UserCString::new(current_task, UserAddress::from(arg5));
922                let name = current_task.read_c_string_to_vec(name, 256).map_err(|e| {
923                    // An overly long name produces EINVAL and not ENAMETOOLONG in Linux 5.15.
924                    if e.code == ENAMETOOLONG { errno!(EINVAL) } else { e }
925                })?;
926                // Some characters are forbidden in VMA names.
927                if name.iter().any(|b| {
928                    matches!(b,
929                        0..=0x1f |
930                        0x7f..=0xff |
931                        b'\\' | b'`' | b'$' | b'[' | b']'
932                    )
933                }) {
934                    return error!(EINVAL);
935                }
936                Some(name)
937            };
938            current_task.mm()?.set_mapping_name(addr, length, name)?;
939            Ok(().into())
940        }
941        PR_SET_DUMPABLE => {
942            let mm = current_task.mm()?;
943            mm.dumpable.store(match arg2 {
944                0 => DumpPolicy::Disable,
945                1 => DumpPolicy::User,
946                _ => return error!(EINVAL),
947            });
948            Ok(().into())
949        }
950        PR_GET_DUMPABLE => {
951            let mm = current_task.mm()?;
952            Ok(match mm.dumpable.load() {
953                DumpPolicy::Disable => 0.into(),
954                DumpPolicy::User => 1.into(),
955            })
956        }
957        PR_SET_PDEATHSIG => {
958            // From <https://man7.org/linux/man-pages/man2/PR_SET_PDEATHSIG.2const.html>:
959            //
960            //   Set the parent-death signal of the calling process to sig (either a
961            //   signal value in the range [1, NSIG - 1], or 0 to clear).  This is the
962            //   signal that the calling process will get when its parent dies.
963            let signal =
964                if arg2 == 0 { None } else { Some(Signal::try_from(UncheckedSignal::new(arg2))?) };
965            current_task.thread_group().write().set_parent_death_signal(signal);
966            Ok(().into())
967        }
968        PR_GET_PDEATHSIG => {
969            // From <https://man7.org/linux/man-pages/man2/PR_GET_PDEATHSIG.2const.html>:
970            //
971            //   Return the parent-death signal number of the calling process, in the
972            //   location pointed to by sig.
973            let signal_ptr = UserRef::<i32>::new(UserAddress::from(arg2));
974            let parent_death_signal = current_task.thread_group().read().parent_death_signal();
975            let signal_number = parent_death_signal.map_or(0, |signal| signal.number() as i32);
976            current_task.write_object(signal_ptr, &signal_number)?;
977            Ok(().into())
978        }
979        PR_SET_NAME => {
980            let addr = UserAddress::from(arg2);
981            let name = TaskCommand::new(&current_task.read_memory_to_array::<16>(addr)?);
982            current_task.set_command_name(name);
983            if current_task.tid == current_task.pid {
984                current_task.thread_group.sync_syscall_log_level();
985            }
986            Ok(0.into())
987        }
988        PR_GET_NAME => {
989            let addr = UserAddress::from(arg2);
990            let name = current_task.command().prctl_name();
991            current_task.write_memory(addr, &name[..])?;
992            Ok(().into())
993        }
994        PR_SET_PTRACER => {
995            let allowed_ptracers = if arg2 == PR_SET_PTRACER_ANY
996                || (current_task.is_arch32() && arg2 == PR_SET_PTRACER_ANY_ARCH32)
997            {
998                PtraceAllowedPtracers::Any
999            } else if arg2 == 0 {
1000                PtraceAllowedPtracers::None
1001            } else {
1002                let task = current_task.get_task(arg2 as i32).map_err(|_| errno!(EINVAL))?;
1003                PtraceAllowedPtracers::Some(task.pid.clone())
1004            };
1005            current_task.thread_group().write().allowed_ptracers = allowed_ptracers;
1006            Ok(().into())
1007        }
1008        PR_GET_KEEPCAPS => {
1009            Ok(current_task.current_creds().securebits.contains(SecureBits::KEEP_CAPS).into())
1010        }
1011        PR_SET_KEEPCAPS => {
1012            if arg2 != 0 && arg2 != 1 {
1013                return error!(EINVAL);
1014            }
1015            // Per PR_SET_KEEPCAPS(2const):
1016            //   EPERM: The caller's SECBIT_KEEP_CAPS_LOCKED flag is set.
1017            if current_task.current_creds().securebits.contains(SecureBits::KEEP_CAPS_LOCKED) {
1018                return error!(EPERM);
1019            }
1020            let mut creds = Credentials::clone(&current_task.current_creds());
1021            creds.securebits.set(SecureBits::KEEP_CAPS, arg2 != 0);
1022            current_task.set_creds(creds);
1023            Ok(().into())
1024        }
1025        PR_SET_NO_NEW_PRIVS => {
1026            // If any args are set other than arg2 to 1, this should return einval
1027            if arg2 != 1 || arg3 != 0 || arg4 != 0 || arg5 != 0 {
1028                return error!(EINVAL);
1029            }
1030            current_task.write().enable_no_new_privs();
1031            Ok(().into())
1032        }
1033        PR_GET_NO_NEW_PRIVS => {
1034            // If any args are set, this should return einval
1035            if arg2 != 0 || arg3 != 0 || arg4 != 0 {
1036                return error!(EINVAL);
1037            }
1038            Ok(current_task.read().no_new_privs().into())
1039        }
1040        PR_GET_SECCOMP => {
1041            if current_task.seccomp_filter_state.get() == SeccompStateValue::None {
1042                Ok(0.into())
1043            } else {
1044                Ok(2.into())
1045            }
1046        }
1047        PR_SET_SECCOMP => {
1048            if arg2 == SECCOMP_MODE_STRICT as u64 {
1049                return sys_seccomp(current_task, SECCOMP_SET_MODE_STRICT, 0, UserAddress::NULL);
1050            } else if arg2 == SECCOMP_MODE_FILTER as u64 {
1051                return sys_seccomp(current_task, SECCOMP_SET_MODE_FILTER, 0, arg3.into());
1052            }
1053            Ok(().into())
1054        }
1055        PR_GET_CHILD_SUBREAPER => {
1056            let addr = UserAddress::from(arg2);
1057            #[allow(clippy::bool_to_int_with_if)]
1058            let value: i32 =
1059                if current_task.thread_group().read().is_child_subreaper { 1 } else { 0 };
1060            current_task.write_object(addr.into(), &value)?;
1061            Ok(().into())
1062        }
1063        PR_SET_CHILD_SUBREAPER => {
1064            current_task.thread_group().write().is_child_subreaper = arg2 != 0;
1065            Ok(().into())
1066        }
1067        PR_GET_SECUREBITS => Ok(current_task.current_creds().securebits.bits().into()),
1068        PR_SET_SECUREBITS => {
1069            security::check_task_capable(current_task, CAP_SETPCAP)?;
1070
1071            // Observed: Linux returns EPERM (not EINVAL) when unrecognized bits are set in flags.
1072            let flags = u32::try_from(arg2).map_err(|_| errno!(EPERM))?;
1073            let securebits = SecureBits::from_bits(flags).ok_or_else(|| errno!(EPERM))?;
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        PR_GET_TIMERSLACK => Ok(current_task.read().timerslack_ns.into()),
1144        #[cfg(target_arch = "aarch64")]
1145        PR_GET_TAGGED_ADDR_CTRL => {
1146            track_stub!(TODO("https://fxbug.dev/408554469"), "PR_GET_TAGGED_ADDR_CTRL");
1147            Ok(0.into())
1148        }
1149        #[cfg(target_arch = "aarch64")]
1150        PR_SET_TAGGED_ADDR_CTRL => match u32::try_from(arg2).map_err(|_| errno!(EINVAL))? {
1151            // Only untagged pointers are allowed, the default.
1152            0 => Ok(().into()),
1153            PR_TAGGED_ADDR_ENABLE => {
1154                track_stub!(TODO("https://fxbug.dev/408554469"), "PR_TAGGED_ADDR_ENABLE");
1155                error!(EINVAL)
1156            }
1157            unknown_mode => {
1158                track_stub!(
1159                    TODO("https://fxbug.dev/408554469"),
1160                    "PR_SET_TAGGED_ADDR_CTRL unknown mode",
1161                    unknown_mode,
1162                );
1163                error!(EINVAL)
1164            }
1165        },
1166        #[cfg(target_arch = "aarch64")]
1167        starnix_uapi::PR_SVE_GET_VL => {
1168            // Like Linux, ignore `arg2`..`arg5`: `prctl` is variadic, so callers leave the unused
1169            // arguments uninitialized.
1170            if !current_task.thread_state.extended_pstate.has_sve() {
1171                return error!(EINVAL);
1172            }
1173            Ok((sve_prctl_status(current_task) as usize).into())
1174        }
1175        #[cfg(target_arch = "aarch64")]
1176        starnix_uapi::PR_SVE_SET_VL => {
1177            // Like Linux, ignore `arg3`..`arg5`.
1178            if !current_task.thread_state.extended_pstate.has_sve() {
1179                return error!(EINVAL);
1180            }
1181            let arg = u32::try_from(arg2).map_err(|_| errno!(EINVAL))?;
1182            let valid_mask = starnix_uapi::PR_SVE_VL_LEN_MASK
1183                | starnix_uapi::PR_SVE_VL_INHERIT
1184                | starnix_uapi::PR_SVE_SET_VL_ONEXEC;
1185            if (arg & !valid_mask) != 0 {
1186                return error!(EINVAL);
1187            }
1188            let vl = arg & starnix_uapi::PR_SVE_VL_LEN_MASK;
1189            if vl < starnix_uapi::SVE_VL_MIN
1190                || vl > starnix_uapi::SVE_VL_MAX
1191                || vl % starnix_uapi::SVE_VQ_BYTES != 0
1192            {
1193                return error!(EINVAL);
1194            }
1195            // The only supported vector length is 128 bits (`SVE_VL_MIN`), so every valid request,
1196            // whether immediate or deferred with `PR_SVE_SET_VL_ONEXEC`, clamps to the thread's
1197            // current vector length. As in Linux's `vec_set_vector_length()`, setting the current
1198            // vector length leaves the SVE register state untouched, while the `PR_SVE_VL_INHERIT`
1199            // setting is always updated.
1200            current_task
1201                .thread_state
1202                .extended_pstate
1203                .set_sve_vl_inherit((arg & starnix_uapi::PR_SVE_VL_INHERIT) != 0);
1204            Ok((sve_prctl_status(current_task) as usize).into())
1205        }
1206        _ => {
1207            track_stub!(TODO("https://fxbug.dev/322874733"), "prctl fallthrough", option);
1208            error!(ENOSYS)
1209        }
1210    }
1211}
1212
1213/// Returns the value reported by `prctl(PR_SVE_GET_VL)` and `prctl(PR_SVE_SET_VL)`: the thread's
1214/// SVE vector length in bytes, which is always 128 bits (`SVE_VL_MIN`) on Starnix, OR-ed with
1215/// `PR_SVE_VL_INHERIT` when the vector length is inherited across `execve`.
1216#[cfg(target_arch = "aarch64")]
1217fn sve_prctl_status(current_task: &CurrentTask) -> u32 {
1218    let inherit = if current_task.thread_state.extended_pstate.sve_vl_inherit() {
1219        starnix_uapi::PR_SVE_VL_INHERIT
1220    } else {
1221        0
1222    };
1223    starnix_uapi::SVE_VL_MIN | inherit
1224}
1225
1226pub fn sys_ptrace(
1227    current_task: &mut CurrentTask,
1228    request: u32,
1229    pid: pid_t,
1230    addr: UserAddress,
1231    data: UserAddress,
1232) -> Result<SyscallResult, Errno> {
1233    if request == PTRACE_TRACEME {
1234        return ptrace_traceme(current_task);
1235    }
1236    let pid = current_task.kernel().pids.get(pid)?;
1237    match request {
1238        PTRACE_ATTACH => ptrace_attach(current_task, &pid, PtraceAttachType::Attach, data),
1239        PTRACE_SEIZE => ptrace_attach(current_task, &pid, PtraceAttachType::Seize, data),
1240        _ => ptrace_dispatch(current_task, request, &pid, addr, data),
1241    }
1242}
1243
1244pub fn sys_set_tid_address(
1245    current_task: &CurrentTask,
1246    user_tid: UserRef<pid_t>,
1247) -> Result<pid_t, Errno> {
1248    current_task.write().clear_child_tid = user_tid;
1249    Ok(current_task.get_tid())
1250}
1251
1252pub fn sys_getrusage(
1253    current_task: &CurrentTask,
1254    who: i32,
1255    user_usage: RUsagePtr,
1256) -> Result<(), Errno> {
1257    const RUSAGE_SELF: i32 = starnix_uapi::uapi::RUSAGE_SELF as i32;
1258    const RUSAGE_THREAD: i32 = starnix_uapi::uapi::RUSAGE_THREAD as i32;
1259    track_stub!(TODO("https://fxbug.dev/297370242"), "real rusage");
1260    let time_stats = match who {
1261        RUSAGE_CHILDREN => current_task.task.thread_group().read().children_time_stats,
1262        RUSAGE_SELF => current_task.task.thread_group().time_stats(),
1263        RUSAGE_THREAD => current_task.task.time_stats(),
1264        _ => return error!(EINVAL),
1265    };
1266
1267    let usage = rusage {
1268        ru_utime: timeval_from_duration(time_stats.user_time),
1269        ru_stime: timeval_from_duration(time_stats.system_time),
1270        ..rusage::default()
1271    };
1272    current_task.write_multi_arch_object(user_usage, usage)?;
1273
1274    Ok(())
1275}
1276
1277type PrLimitRef = MultiArchUserRef<uapi::rlimit, uapi::arch32::rlimit>;
1278
1279pub fn sys_getrlimit(
1280    current_task: &CurrentTask,
1281    resource: u32,
1282    user_rlimit: PrLimitRef,
1283) -> Result<(), Errno> {
1284    do_prlimit64(current_task, 0, resource, PrLimitRef::null(current_task), user_rlimit)
1285}
1286
1287pub fn sys_setrlimit(
1288    current_task: &CurrentTask,
1289    resource: u32,
1290    user_rlimit: PrLimitRef,
1291) -> Result<(), Errno> {
1292    do_prlimit64(current_task, 0, resource, user_rlimit, PrLimitRef::null(current_task))
1293}
1294
1295pub fn sys_prlimit64(
1296    current_task: &CurrentTask,
1297    pid: pid_t,
1298    user_resource: u32,
1299    new_limit_ref: UserRef<uapi::rlimit>,
1300    old_limit_ref: UserRef<uapi::rlimit>,
1301) -> Result<(), Errno> {
1302    do_prlimit64::<uapi::rlimit>(
1303        current_task,
1304        pid,
1305        user_resource,
1306        new_limit_ref.into(),
1307        old_limit_ref.into(),
1308    )
1309}
1310
1311pub fn do_prlimit64<T>(
1312    current_task: &CurrentTask,
1313    pid: pid_t,
1314    user_resource: u32,
1315    new_limit_ref: MultiArchUserRef<uapi::rlimit, T>,
1316    old_limit_ref: MultiArchUserRef<uapi::rlimit, T>,
1317) -> Result<(), Errno>
1318where
1319    T: FromBytes + IntoBytes + Immutable + From<uapi::rlimit> + Into<uapi::rlimit>,
1320{
1321    let target_task = get_task_or_current(current_task, pid)?;
1322
1323    // To get or set the resource of a process other than itself, the caller must have either:
1324    // * the same `uid`, `euid`, `saved_uid`, `gid`, `egid`, `saved_gid` as the target.
1325    // * the CAP_SYS_RESOURCE
1326    if current_task.get_pid() != target_task.get_pid() {
1327        let self_creds = current_task.current_creds();
1328        let target_creds = target_task.real_creds();
1329        if self_creds.uid != target_creds.uid
1330            || self_creds.euid != target_creds.euid
1331            || self_creds.saved_uid != target_creds.saved_uid
1332            || self_creds.gid != target_creds.gid
1333            || self_creds.egid != target_creds.egid
1334            || self_creds.saved_gid != target_creds.saved_gid
1335        {
1336            security::check_task_capable(current_task, CAP_SYS_RESOURCE)?;
1337        }
1338        security::task_prlimit(
1339            current_task,
1340            &target_task,
1341            !old_limit_ref.is_null(),
1342            !new_limit_ref.is_null(),
1343        )?;
1344    }
1345
1346    let resource = Resource::from_raw(user_resource)?;
1347
1348    let new_limit = if new_limit_ref.is_null() {
1349        None
1350    } else {
1351        let new_limit = current_task.read_multi_arch_object(new_limit_ref)?;
1352        if new_limit.rlim_cur > new_limit.rlim_max {
1353            return error!(EINVAL);
1354        }
1355        Some(new_limit)
1356    };
1357    let old_limit = ThreadGroup::adjust_rlimits(current_task, &target_task, resource, new_limit)?;
1358    // Note: Routing `Resource::STACK` through `ThreadGroup::adjust_rlimits` updates the stored
1359    // limit (visible via `getrlimit`/`prlimit64`, `/proc/[pid]/limits`, and `execve` argument size
1360    // checks), but Starnix does not yet enforce `RLIMIT_STACK` when creating or growing stack
1361    // mappings.
1362    if resource == Resource::STACK && new_limit.is_some() {
1363        track_stub!(
1364            TODO("https://fxbug.dev/571982693"),
1365            "prlimit64 does not enforce RLIMIT_STACK on stack growth"
1366        );
1367    }
1368    if !old_limit_ref.is_null() {
1369        current_task.write_multi_arch_object(old_limit_ref, old_limit)?;
1370    }
1371    Ok(())
1372}
1373
1374pub fn sys_quotactl(
1375    _current_task: &CurrentTask,
1376    _cmd: i32,
1377    _special: UserRef<c_char>,
1378    _id: i32,
1379    _addr: UserRef<c_char>,
1380) -> Result<SyscallResult, Errno> {
1381    track_stub!(TODO("https://fxbug.dev/297302197"), "quotacl()");
1382    error!(ENOSYS)
1383}
1384
1385pub fn sys_capget(
1386    current_task: &CurrentTask,
1387    user_header: UserRef<__user_cap_header_struct>,
1388    user_data: UserRef<__user_cap_data_struct>,
1389) -> Result<(), Errno> {
1390    let mut header = current_task.read_object(user_header)?;
1391    let is_version_valid =
1392        [_LINUX_CAPABILITY_VERSION_1, _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_VERSION_3]
1393            .contains(&header.version);
1394    if !is_version_valid {
1395        header.version = _LINUX_CAPABILITY_VERSION_3;
1396        current_task.write_object(user_header, &header)?;
1397    }
1398    if user_data.is_null() {
1399        return Ok(());
1400    }
1401    if !is_version_valid || header.pid < 0 {
1402        return error!(EINVAL);
1403    }
1404
1405    let target_task = get_task_or_current(current_task, header.pid)?;
1406
1407    security::check_getcap_access(current_task, &target_task)?;
1408
1409    let (permitted, effective, inheritable) = {
1410        let creds = &target_task.real_creds();
1411        (creds.cap_permitted, creds.cap_effective, creds.cap_inheritable)
1412    };
1413
1414    match header.version {
1415        _LINUX_CAPABILITY_VERSION_1 => {
1416            let data: [__user_cap_data_struct; 1] = [__user_cap_data_struct {
1417                effective: effective.as_abi_v1(),
1418                inheritable: inheritable.as_abi_v1(),
1419                permitted: permitted.as_abi_v1(),
1420            }];
1421            current_task.write_objects(user_data, &data)?;
1422        }
1423        _LINUX_CAPABILITY_VERSION_2 | _LINUX_CAPABILITY_VERSION_3 => {
1424            // Return 64 bit capabilities as two sets of 32 bit capabilities, little endian
1425            let (permitted, effective, inheritable) =
1426                (permitted.as_abi_v3(), effective.as_abi_v3(), inheritable.as_abi_v3());
1427            let data: [__user_cap_data_struct; 2] = [
1428                __user_cap_data_struct {
1429                    effective: effective.0,
1430                    inheritable: inheritable.0,
1431                    permitted: permitted.0,
1432                },
1433                __user_cap_data_struct {
1434                    effective: effective.1,
1435                    inheritable: inheritable.1,
1436                    permitted: permitted.1,
1437                },
1438            ];
1439            current_task.write_objects(user_data, &data)?;
1440        }
1441        _ => {
1442            unreachable!("already returned if Linux capability version is not valid")
1443        }
1444    }
1445    Ok(())
1446}
1447
1448pub fn sys_capset(
1449    current_task: &CurrentTask,
1450    user_header: UserRef<__user_cap_header_struct>,
1451    user_data: UserRef<__user_cap_data_struct>,
1452) -> Result<(), Errno> {
1453    let mut header = current_task.read_object(user_header)?;
1454    let is_version_valid =
1455        [_LINUX_CAPABILITY_VERSION_1, _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_VERSION_3]
1456            .contains(&header.version);
1457    if !is_version_valid {
1458        header.version = _LINUX_CAPABILITY_VERSION_3;
1459        current_task.write_object(user_header, &header)?;
1460        return error!(EINVAL);
1461    }
1462    if header.pid != 0 && header.pid != current_task.tid.id {
1463        return error!(EPERM);
1464    }
1465
1466    let (new_permitted, new_effective, new_inheritable) = match header.version {
1467        _LINUX_CAPABILITY_VERSION_1 => {
1468            let data = current_task.read_object(user_data)?;
1469            (
1470                Capabilities::from_abi_v1(data.permitted),
1471                Capabilities::from_abi_v1(data.effective),
1472                Capabilities::from_abi_v1(data.inheritable),
1473            )
1474        }
1475        _LINUX_CAPABILITY_VERSION_2 | _LINUX_CAPABILITY_VERSION_3 => {
1476            let data =
1477                current_task.read_objects_to_array::<__user_cap_data_struct, 2>(user_data)?;
1478            (
1479                Capabilities::from_abi_v3((data[0].permitted, data[1].permitted)),
1480                Capabilities::from_abi_v3((data[0].effective, data[1].effective)),
1481                Capabilities::from_abi_v3((data[0].inheritable, data[1].inheritable)),
1482            )
1483        }
1484        _ => {
1485            unreachable!("already returned if Linux capability version is not valid")
1486        }
1487    };
1488
1489    // Permission checks. Copied out of TLPI section 39.7.
1490    let mut creds = Credentials::clone(&current_task.current_creds());
1491    {
1492        log_trace!(
1493            "Capabilities({{permitted={:?} from {:?}, effective={:?} from {:?}, inheritable={:?} from {:?}}}, bounding={:?})",
1494            new_permitted,
1495            creds.cap_permitted,
1496            new_effective,
1497            creds.cap_effective,
1498            new_inheritable,
1499            creds.cap_inheritable,
1500            creds.cap_bounding
1501        );
1502        if !creds.cap_inheritable.union(creds.cap_permitted).contains(new_inheritable) {
1503            security::check_task_capable(current_task, CAP_SETPCAP)?;
1504        }
1505
1506        if !creds.cap_inheritable.union(creds.cap_bounding).contains(new_inheritable) {
1507            return error!(EPERM);
1508        }
1509        if !creds.cap_permitted.contains(new_permitted) {
1510            return error!(EPERM);
1511        }
1512        if !new_permitted.contains(new_effective) {
1513            return error!(EPERM);
1514        }
1515    }
1516    let target_task = get_task_or_current(current_task, header.pid)?;
1517
1518    security::check_setcap_access(current_task, &target_task)?;
1519
1520    creds.cap_permitted = new_permitted;
1521    creds.cap_effective = new_effective;
1522    creds.cap_inheritable = new_inheritable;
1523    creds.cap_ambient = new_permitted & new_inheritable & creds.cap_ambient;
1524    current_task.set_creds(creds);
1525    Ok(())
1526}
1527
1528pub fn sys_seccomp(
1529    current_task: &mut CurrentTask,
1530    operation: u32,
1531    flags: u32,
1532    args: UserAddress,
1533) -> Result<SyscallResult, Errno> {
1534    match operation {
1535        SECCOMP_SET_MODE_STRICT => {
1536            if flags != 0 || args != UserAddress::NULL {
1537                return error!(EINVAL);
1538            }
1539            current_task.set_seccomp_state(SeccompStateValue::Strict)?;
1540            Ok(().into())
1541        }
1542        SECCOMP_SET_MODE_FILTER => {
1543            if flags
1544                & (SECCOMP_FILTER_FLAG_LOG
1545                    | SECCOMP_FILTER_FLAG_NEW_LISTENER
1546                    | SECCOMP_FILTER_FLAG_SPEC_ALLOW
1547                    | SECCOMP_FILTER_FLAG_TSYNC
1548                    | SECCOMP_FILTER_FLAG_TSYNC_ESRCH)
1549                != flags
1550            {
1551                return error!(EINVAL);
1552            }
1553            if (flags & SECCOMP_FILTER_FLAG_TSYNC == 0)
1554                && (flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH != 0)
1555            {
1556                return error!(EINVAL);
1557            }
1558            if (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER != 0)
1559                && (flags & SECCOMP_FILTER_FLAG_TSYNC != 0)
1560                && (flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH == 0)
1561            {
1562                return error!(EINVAL);
1563            }
1564            let fprog =
1565                current_task.read_multi_arch_object(SockFProgPtr::new(current_task, args))?;
1566            if fprog.len > BPF_MAXINSNS || fprog.len == 0 {
1567                return error!(EINVAL);
1568            }
1569            let code: Vec<sock_filter> =
1570                current_task.read_multi_arch_objects_to_vec(fprog.filter, fprog.len as usize)?;
1571
1572            if !current_task.read().no_new_privs() {
1573                security::check_task_capable(current_task, CAP_SYS_ADMIN)
1574                    .map_err(|_| errno!(EACCES))?;
1575            }
1576            current_task.add_seccomp_filter(code, flags)
1577        }
1578        SECCOMP_GET_ACTION_AVAIL => {
1579            if flags != 0 || args.is_null() {
1580                return error!(EINVAL);
1581            }
1582            let action: u32 = current_task.read_object(UserRef::new(args))?;
1583            SeccompAction::is_action_available(action)
1584        }
1585        SECCOMP_GET_NOTIF_SIZES => {
1586            if flags != 0 {
1587                return error!(EINVAL);
1588            }
1589            track_stub!(TODO("https://fxbug.dev/322874791"), "SECCOMP_GET_NOTIF_SIZES");
1590            error!(ENOSYS)
1591        }
1592        _ => {
1593            track_stub!(TODO("https://fxbug.dev/322874916"), "seccomp fallthrough", operation);
1594            error!(EINVAL)
1595        }
1596    }
1597}
1598
1599pub fn sys_setgroups(
1600    current_task: &CurrentTask,
1601    size: usize,
1602    groups_addr: UserAddress,
1603) -> Result<(), Errno> {
1604    if size > NGROUPS_MAX as usize {
1605        return error!(EINVAL);
1606    }
1607    let groups = current_task.read_objects_to_vec::<gid_t>(groups_addr.into(), size)?;
1608    security::check_task_capable(current_task, CAP_SETGID)?;
1609    let mut creds = Credentials::clone(&current_task.current_creds());
1610    creds.groups = groups;
1611    current_task.set_creds(creds);
1612    Ok(())
1613}
1614
1615pub fn sys_getgroups(
1616    current_task: &CurrentTask,
1617    size: usize,
1618    groups_addr: UserAddress,
1619) -> Result<usize, Errno> {
1620    if size > NGROUPS_MAX as usize {
1621        return error!(EINVAL);
1622    }
1623    let creds = current_task.current_creds();
1624    if size != 0 {
1625        if size < creds.groups.len() {
1626            return error!(EINVAL);
1627        }
1628        current_task.write_memory(groups_addr, creds.groups.as_slice().as_bytes())?;
1629    }
1630    Ok(creds.groups.len())
1631}
1632
1633pub fn sys_setsid(current_task: &CurrentTask) -> Result<pid_t, Errno> {
1634    current_task.thread_group().setsid()?;
1635    Ok(current_task.get_pid())
1636}
1637
1638// Note the asymmetry with sys_setpriority: this returns "kernel nice" which ranges
1639// from 1 (weakest) to 40 (strongest). (It is part of Linux history that this syscall
1640// deals with niceness but has "priority" in its name.)
1641pub fn sys_getpriority(current_task: &CurrentTask, which: u32, who: i32) -> Result<u8, Errno> {
1642    match which {
1643        PRIO_PROCESS => {}
1644        // TODO: https://fxbug.dev/287121196 - support PRIO_PGRP and PRIO_USER?
1645        _ => return error!(EINVAL),
1646    }
1647    track_stub!(TODO("https://fxbug.dev/322893809"), "getpriority permissions");
1648    let target_task = get_task_or_current(current_task, who)?;
1649    Ok(target_task.scheduler_state.lock().normal_priority.raw_priority())
1650}
1651
1652// Note the asymmetry with sys_getpriority: this call's `priority` parameter is a
1653// "user nice" which ranges from -20 (strongest) to 19 (weakest) (other values can be
1654// passed and are clamped to that range and interpretation). (It is part of Linux
1655// history that this syscall deals with niceness but has "priority" in its name.)
1656pub fn sys_setpriority(
1657    current_task: &CurrentTask,
1658    which: u32,
1659    who: i32,
1660    priority: i32,
1661) -> Result<(), Errno> {
1662    // Parse & validate the arguments.
1663    match which {
1664        PRIO_PROCESS => {}
1665        // TODO: https://fxbug.dev/287121196 - support PRIO_PGRP and PRIO_USER?
1666        _ => return error!(EINVAL),
1667    }
1668
1669    let target_task = get_task_or_current(current_task, who)?;
1670
1671    let normal_priority = NormalPriority::from_setpriority_syscall(priority);
1672
1673    // TODO: https://fxbug.dev/425143440 - we probably want to improve the locking here.
1674    let current_priority = target_task.scheduler_state.lock().normal_priority;
1675
1676    // Check capabilities and permissions, if required, for the operation.
1677    let euid_friendly = current_task.is_euid_friendly_with(&target_task);
1678    let strengthening = current_priority < normal_priority;
1679    let rlimited = strengthening
1680        && normal_priority.exceeds(target_task.thread_group().get_rlimit(Resource::NICE));
1681    if !euid_friendly {
1682        security::check_task_capable(current_task, CAP_SYS_NICE)?;
1683    } else if rlimited {
1684        security::check_task_capable(current_task, CAP_SYS_NICE).map_err(|_| errno!(EACCES))?;
1685    }
1686
1687    security::check_task_setnice_access(current_task, &target_task)?;
1688
1689    // Apply the new scheduler configuration to the task.
1690    target_task.set_scheduler_nice(normal_priority)?;
1691
1692    Ok(())
1693}
1694
1695pub fn sys_setns(current_task: &CurrentTask, ns_fd: FdNumber, ns_type: c_int) -> Result<(), Errno> {
1696    let file_handle = current_task.files().get(ns_fd)?;
1697
1698    // From man pages this is not quite right because some namespace types require more capabilities
1699    // or require this capability in multiple namespaces, but it should cover our current test
1700    // cases and we can make this more nuanced once more namespace types are supported.
1701    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1702
1703    if let Some(mount_ns) = file_handle.downcast_file::<MountNamespaceFile>() {
1704        if !(ns_type == 0 || ns_type == CLONE_NEWNS as i32) {
1705            log_trace!("invalid type");
1706            return error!(EINVAL);
1707        }
1708
1709        track_stub!(TODO("https://fxbug.dev/297312091"), "setns CLONE_FS limitations");
1710        current_task.fs().set_namespace(mount_ns.0.clone())?;
1711        return Ok(());
1712    }
1713
1714    if let Some(_pidfd) = file_handle.downcast_file::<PidFdFileObject>() {
1715        track_stub!(TODO("https://fxbug.dev/297312844"), "setns w/ pidfd");
1716        return error!(ENOSYS);
1717    }
1718
1719    track_stub!(TODO("https://fxbug.dev/322893829"), "unknown ns file for setns, see logs");
1720    log_info!("ns_fd was not a supported namespace file: {}", file_handle.ops_type_name());
1721    error!(EINVAL)
1722}
1723
1724pub fn sys_unshare(current_task: &CurrentTask, flags: u32) -> Result<(), Errno> {
1725    const IMPLEMENTED_FLAGS: u32 = CLONE_FILES | CLONE_FS | CLONE_NEWNS | CLONE_NEWUTS;
1726    if flags & !IMPLEMENTED_FLAGS != 0 {
1727        track_stub!(TODO("https://fxbug.dev/322893372"), "unshare", flags & !IMPLEMENTED_FLAGS);
1728        return error!(EINVAL);
1729    }
1730
1731    if (flags & CLONE_FILES) != 0 {
1732        current_task.running_state().unshare_files(current_task);
1733    }
1734
1735    if (flags & CLONE_FS) != 0 {
1736        current_task.unshare_fs();
1737    }
1738
1739    if (flags & CLONE_NEWNS) != 0 {
1740        security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1741        current_task.fs().unshare_namespace();
1742    }
1743
1744    if (flags & CLONE_NEWUTS) != 0 {
1745        security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1746        // Fork the UTS namespace.
1747        let mut task_state = current_task.write();
1748        let new_uts_ns = task_state.uts_ns.read().clone();
1749        task_state.uts_ns = Arc::new(new_uts_ns.into());
1750    }
1751
1752    Ok(())
1753}
1754
1755pub fn sys_swapon(
1756    current_task: &CurrentTask,
1757    user_path: UserCString,
1758    flags: u32,
1759) -> Result<(), Errno> {
1760    const MAX_SWAPFILES: usize = 32; // See https://man7.org/linux/man-pages/man2/swapon.2.html
1761    const SWAP_FLAGS_VALID: u32 = 0x7ffff; // Defined in include/linux/swap.h
1762
1763    if (flags & !SWAP_FLAGS_VALID) != 0 {
1764        return error!(EINVAL);
1765    }
1766
1767    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1768
1769    if flags != 0 {
1770        track_stub!(TODO("https://fxbug.dev/322893905"), "swapon flags", flags);
1771    }
1772
1773    let path = current_task.read_path(user_path)?;
1774    let file = current_task.open_file(path.as_ref(), OpenFlags::RDWR)?;
1775
1776    let node = file.node();
1777    let mode = node.info().mode;
1778    if !mode.is_reg() && !mode.is_blk() {
1779        return error!(EINVAL);
1780    }
1781
1782    // We determined this magic number by using the mkswap tool and the file tool. The mkswap tool
1783    // populates a few bytes in the file, including a UUID, which can be replaced with zeros while
1784    // still being recognized by the file tool. This string appears at a fixed offset
1785    // (MAGIC_OFFSET) in the file, which looks quite like a magic number.
1786    const MAGIC_OFFSET: usize = 0xff6;
1787    let swap_magic = b"SWAPSPACE2";
1788    let mut buffer = VecOutputBuffer::new(swap_magic.len());
1789    if file.read_at(current_task, MAGIC_OFFSET, &mut buffer)? != swap_magic.len()
1790        || buffer.data() != swap_magic
1791    {
1792        return error!(EINVAL);
1793    }
1794
1795    let mut swap_files = current_task.kernel().swap_files.lock();
1796    for swap_node in swap_files.iter() {
1797        if Arc::ptr_eq(swap_node, node) {
1798            return error!(EBUSY);
1799        }
1800    }
1801    if swap_files.len() >= MAX_SWAPFILES {
1802        return error!(EPERM);
1803    }
1804    swap_files.push(node.clone());
1805    Ok(())
1806}
1807
1808pub fn sys_swapoff(current_task: &CurrentTask, user_path: UserCString) -> Result<(), Errno> {
1809    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1810
1811    let path = current_task.read_path(user_path)?;
1812    let file = current_task.open_file(path.as_ref(), OpenFlags::RDWR)?;
1813    let node = file.node();
1814
1815    let mut swap_files = current_task.kernel().swap_files.lock();
1816    let original_length = swap_files.len();
1817    swap_files.retain(|swap_node| !Arc::ptr_eq(swap_node, node));
1818    if swap_files.len() == original_length {
1819        return error!(EINVAL);
1820    }
1821    Ok(())
1822}
1823
1824#[derive(Default, Debug, IntoBytes, KnownLayout, FromBytes, Immutable)]
1825#[repr(C)]
1826struct KcmpParams {
1827    mask: usize,
1828    shuffle: usize,
1829}
1830
1831static KCMP_PARAMS: LazyLock<KcmpParams> = LazyLock::new(|| {
1832    let mut params = KcmpParams::default();
1833    starnix_crypto::cprng_draw(params.as_mut_bytes());
1834    // Ensure the shuffle is odd so that multiplying a usize by this value is a permutation.
1835    params.shuffle |= 1;
1836    params
1837});
1838
1839fn obfuscate_value(value: usize) -> usize {
1840    let KcmpParams { mask, shuffle } = *KCMP_PARAMS;
1841    (value ^ mask).wrapping_mul(shuffle)
1842}
1843
1844fn obfuscate_ptr<T>(ptr: *const T) -> usize {
1845    obfuscate_value(ptr as usize)
1846}
1847
1848fn obfuscate_arc<T>(arc: &Arc<T>) -> usize {
1849    obfuscate_ptr(Arc::as_ptr(arc))
1850}
1851
1852pub fn sys_kcmp(
1853    current_task: &CurrentTask,
1854    pid1: pid_t,
1855    pid2: pid_t,
1856    resource_type: u32,
1857    index1: u64,
1858    index2: u64,
1859) -> Result<u32, Errno> {
1860    let task1 = current_task.get_task(pid1)?;
1861    let task2 = current_task.get_task(pid2)?;
1862
1863    current_task.check_ptrace_access_mode(PTRACE_MODE_READ_REALCREDS, &task1)?;
1864    current_task.check_ptrace_access_mode(PTRACE_MODE_READ_REALCREDS, &task2)?;
1865
1866    let resource_type = KcmpResource::from_raw(resource_type)?;
1867
1868    // Output encoding (see <https://man7.org/linux/man-pages/man2/kcmp.2.html>):
1869    //
1870    //   0  v1 is equal to v2; in other words, the two processes share the resource.
1871    //   1  v1 is less than v2.
1872    //   2  v1 is greater than v2.
1873    //   3  v1 is not equal to v2, but ordering information is unavailable.
1874    //
1875    fn encode_ordering(value: cmp::Ordering) -> u32 {
1876        match value {
1877            cmp::Ordering::Equal => 0,
1878            cmp::Ordering::Less => 1,
1879            cmp::Ordering::Greater => 2,
1880        }
1881    }
1882
1883    match resource_type {
1884        KcmpResource::FILE => {
1885            fn get_file(task: &Task, index: u64) -> Result<FileHandle, Errno> {
1886                // TODO: Test whether O_PATH is allowed here. Conceptually, seems like
1887                //       O_PATH should be allowed, but we haven't tested it yet.
1888                task.files()?.get_allowing_opath(FdNumber::from_raw(
1889                    index.try_into().map_err(|_| errno!(EBADF))?,
1890                ))
1891            }
1892            let file1 = get_file(&task1, index1)?;
1893            let file2 = get_file(&task2, index2)?;
1894            Ok(encode_ordering(obfuscate_arc(&file1).cmp(&obfuscate_arc(&file2))))
1895        }
1896        KcmpResource::FILES => {
1897            let files1 = task1.files()?.id();
1898            let files2 = task2.files()?.id();
1899            Ok(encode_ordering(obfuscate_value(files1.raw()).cmp(&obfuscate_value(files2.raw()))))
1900        }
1901        KcmpResource::FS => {
1902            let fs1 = task1.fs()?;
1903            let fs2 = task2.fs()?;
1904            Ok(encode_ordering(obfuscate_arc(&fs1).cmp(&obfuscate_arc(&fs2))))
1905        }
1906        KcmpResource::SIGHAND => Ok(encode_ordering(
1907            obfuscate_arc(&task1.thread_group().signal_actions)
1908                .cmp(&obfuscate_arc(&task2.thread_group().signal_actions)),
1909        )),
1910        KcmpResource::VM => {
1911            Ok(encode_ordering(obfuscate_arc(&task1.mm()?).cmp(&obfuscate_arc(&task2.mm()?))))
1912        }
1913        KcmpResource::SYSVSEM => error!(EOPNOTSUPP),
1914        _ => error!(EINVAL),
1915    }
1916}
1917
1918pub fn sys_syslog(
1919    current_task: &CurrentTask,
1920    action_type: i32,
1921    address: UserAddress,
1922    length: i32,
1923) -> Result<i32, Errno> {
1924    let action = SyslogAction::try_from(action_type)?;
1925    let syslog =
1926        current_task.kernel().syslog.access(&current_task, SyslogAccess::Syscall(action))?;
1927    match action {
1928        SyslogAction::Read => {
1929            if address.is_null() || length < 0 {
1930                return error!(EINVAL);
1931            }
1932            let mut output_buffer =
1933                UserBuffersOutputBuffer::unified_new_at(current_task, address, length as usize)?;
1934            syslog.blocking_read(current_task, &mut output_buffer)
1935        }
1936        SyslogAction::ReadAll => {
1937            if address.is_null() || length < 0 {
1938                return error!(EINVAL);
1939            }
1940            let mut output_buffer =
1941                UserBuffersOutputBuffer::unified_new_at(current_task, address, length as usize)?;
1942            syslog.read_all(current_task.kernel(), &mut output_buffer)
1943        }
1944        SyslogAction::SizeUnread => syslog.size_unread(),
1945        SyslogAction::SizeBuffer => syslog.size_buffer(),
1946        SyslogAction::Close | SyslogAction::Open => Ok(0),
1947        SyslogAction::ReadClear => {
1948            track_stub!(TODO("https://fxbug.dev/322894145"), "syslog: read clear");
1949            Ok(0)
1950        }
1951        SyslogAction::Clear => {
1952            track_stub!(TODO("https://fxbug.dev/322893673"), "syslog: clear");
1953            Ok(0)
1954        }
1955        SyslogAction::ConsoleOff => {
1956            track_stub!(TODO("https://fxbug.dev/322894399"), "syslog: console off");
1957            Ok(0)
1958        }
1959        SyslogAction::ConsoleOn => {
1960            track_stub!(TODO("https://fxbug.dev/322894106"), "syslog: console on");
1961            Ok(0)
1962        }
1963        SyslogAction::ConsoleLevel => {
1964            if length <= 0 || length >= 8 {
1965                return error!(EINVAL);
1966            }
1967            track_stub!(TODO("https://fxbug.dev/322894199"), "syslog: console level");
1968            Ok(0)
1969        }
1970    }
1971}
1972
1973pub fn sys_vhangup(current_task: &CurrentTask) -> Result<(), Errno> {
1974    security::check_task_capable(current_task, CAP_SYS_TTY_CONFIG)?;
1975    track_stub!(TODO("https://fxbug.dev/324079257"), "vhangup");
1976    Ok(())
1977}
1978
1979// Syscalls for arch32 usage
1980#[cfg(target_arch = "aarch64")]
1981mod arch32 {
1982    pub use super::{
1983        sys_execve as sys_arch32_execve, sys_execveat as sys_arch32_execveat,
1984        sys_getegid as sys_arch32_getegid32, sys_geteuid as sys_arch32_geteuid32,
1985        sys_getgid as sys_arch32_getgid32, sys_getgroups as sys_arch32_getgroups32,
1986        sys_getpgid as sys_arch32_getpgid, sys_getppid as sys_arch32_getppid,
1987        sys_getpriority as sys_arch32_getpriority, sys_getresgid as sys_arch32_getresgid32,
1988        sys_getresuid as sys_arch32_getresuid32, sys_getrlimit as sys_arch32_ugetrlimit,
1989        sys_getrusage as sys_arch32_getrusage, sys_getuid as sys_arch32_getuid32,
1990        sys_ioprio_set as sys_arch32_ioprio_set, sys_kcmp as sys_arch32_kcmp,
1991        sys_ptrace as sys_arch32_ptrace, sys_quotactl as sys_arch32_quotactl,
1992        sys_sched_get_priority_max as sys_arch32_sched_get_priority_max,
1993        sys_sched_get_priority_min as sys_arch32_sched_get_priority_min,
1994        sys_sched_getaffinity as sys_arch32_sched_getaffinity,
1995        sys_sched_getparam as sys_arch32_sched_getparam,
1996        sys_sched_setaffinity as sys_arch32_sched_setaffinity,
1997        sys_sched_setparam as sys_arch32_sched_setparam,
1998        sys_sched_setscheduler as sys_arch32_sched_setscheduler, sys_seccomp as sys_arch32_seccomp,
1999        sys_setfsgid as sys_arch32_setfsgid, sys_setfsgid as sys_arch32_setfsgid32,
2000        sys_setfsuid as sys_arch32_setfsuid, sys_setfsuid as sys_arch32_setfsuid32,
2001        sys_setgid as sys_arch32_setgid32, sys_setgroups as sys_arch32_setgroups32,
2002        sys_setns as sys_arch32_setns, sys_setpgid as sys_arch32_setpgid,
2003        sys_setpriority as sys_arch32_setpriority, sys_setregid as sys_arch32_setregid32,
2004        sys_setresgid as sys_arch32_setresgid32, sys_setresuid as sys_arch32_setresuid32,
2005        sys_setreuid as sys_arch32_setreuid32, sys_setreuid as sys_arch32_setreuid,
2006        sys_setrlimit as sys_arch32_setrlimit, sys_setsid as sys_arch32_setsid,
2007        sys_syslog as sys_arch32_syslog, sys_unshare as sys_arch32_unshare,
2008    };
2009}
2010
2011#[cfg(target_arch = "aarch64")]
2012pub use arch32::*;
2013
2014#[cfg(test)]
2015mod tests {
2016    use super::*;
2017    use crate::mm::syscalls::sys_munmap;
2018    use crate::testing::{AutoReleasableTask, map_memory, spawn_kernel_and_run};
2019    use starnix_syscalls::SUCCESS;
2020    use starnix_task_command::TaskCommand;
2021    use starnix_uapi::auth::Credentials;
2022    use starnix_uapi::signals::SIGCHLD;
2023    use starnix_uapi::uapi::{kcmp_type_KCMP_SYSVSEM, kcmp_type_KCMP_TYPES, kcmp_type_KCMP_VM};
2024    use starnix_uapi::{SCHED_FIFO, SCHED_NORMAL};
2025    use std::ffi::CString;
2026
2027    #[::fuchsia::test]
2028    async fn test_prctl_set_vma_anon_name() {
2029        spawn_kernel_and_run(async |current_task| {
2030            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2031            let name_addr = (mapped_address + 128u64).unwrap();
2032            let name = "test-name\0";
2033            current_task.write_memory(name_addr, name.as_bytes()).expect("failed to write name");
2034            sys_prctl(
2035                current_task,
2036                PR_SET_VMA,
2037                PR_SET_VMA_ANON_NAME as u64,
2038                mapped_address.ptr() as u64,
2039                32,
2040                name_addr.ptr() as u64,
2041            )
2042            .expect("failed to set name");
2043            assert_eq!(
2044                "test-name",
2045                current_task
2046                    .mm()
2047                    .unwrap()
2048                    .get_mapping_name((mapped_address + 24u64).unwrap())
2049                    .expect("failed to get address")
2050                    .unwrap()
2051                    .to_string(),
2052            );
2053
2054            sys_munmap(&current_task, mapped_address, *PAGE_SIZE as usize)
2055                .expect("failed to unmap memory");
2056            assert_eq!(
2057                error!(EFAULT),
2058                current_task.mm().unwrap().get_mapping_name((mapped_address + 24u64).unwrap())
2059            );
2060        })
2061        .await;
2062    }
2063
2064    #[::fuchsia::test]
2065    async fn test_set_vma_name_special_chars() {
2066        spawn_kernel_and_run(async |current_task| {
2067            let name_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2068
2069            let mapping_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2070
2071            for c in 1..255 {
2072                let vma_name = CString::new([c]).unwrap();
2073                current_task.write_memory(name_addr, vma_name.as_bytes_with_nul()).unwrap();
2074
2075                let result = sys_prctl(
2076                    current_task,
2077                    PR_SET_VMA,
2078                    PR_SET_VMA_ANON_NAME as u64,
2079                    mapping_addr.ptr() as u64,
2080                    *PAGE_SIZE,
2081                    name_addr.ptr() as u64,
2082                );
2083
2084                if c > 0x1f
2085                    && c < 0x7f
2086                    && c != b'\\'
2087                    && c != b'`'
2088                    && c != b'$'
2089                    && c != b'['
2090                    && c != b']'
2091                {
2092                    assert_eq!(result, Ok(SUCCESS));
2093                } else {
2094                    assert_eq!(result, error!(EINVAL));
2095                }
2096            }
2097        })
2098        .await;
2099    }
2100
2101    #[::fuchsia::test]
2102    async fn test_set_vma_name_long() {
2103        spawn_kernel_and_run(async |current_task| {
2104            let name_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2105
2106            let mapping_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2107
2108            let name_too_long = CString::new(vec![b'a'; 256]).unwrap();
2109
2110            current_task.write_memory(name_addr, name_too_long.as_bytes_with_nul()).unwrap();
2111
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,
2119                    name_addr.ptr() as u64,
2120                ),
2121                error!(EINVAL)
2122            );
2123
2124            let name_just_long_enough = CString::new(vec![b'a'; 255]).unwrap();
2125
2126            current_task
2127                .write_memory(name_addr, name_just_long_enough.as_bytes_with_nul())
2128                .unwrap();
2129
2130            assert_eq!(
2131                sys_prctl(
2132                    current_task,
2133                    PR_SET_VMA,
2134                    PR_SET_VMA_ANON_NAME as u64,
2135                    mapping_addr.ptr() as u64,
2136                    *PAGE_SIZE,
2137                    name_addr.ptr() as u64,
2138                ),
2139                Ok(SUCCESS)
2140            );
2141        })
2142        .await;
2143    }
2144
2145    #[::fuchsia::test]
2146    async fn test_set_vma_name_misaligned() {
2147        spawn_kernel_and_run(async |current_task| {
2148            let name_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2149
2150            let mapping_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2151
2152            let name = CString::new("name").unwrap();
2153            current_task.write_memory(name_addr, name.as_bytes_with_nul()).unwrap();
2154
2155            // Passing a misaligned pointer to the start of the named region fails.
2156            assert_eq!(
2157                sys_prctl(
2158                    current_task,
2159                    PR_SET_VMA,
2160                    PR_SET_VMA_ANON_NAME as u64,
2161                    1 + mapping_addr.ptr() as u64,
2162                    *PAGE_SIZE - 1,
2163                    name_addr.ptr() as u64,
2164                ),
2165                error!(EINVAL)
2166            );
2167
2168            // Passing an unaligned length does work, however.
2169            assert_eq!(
2170                sys_prctl(
2171                    current_task,
2172                    PR_SET_VMA,
2173                    PR_SET_VMA_ANON_NAME as u64,
2174                    mapping_addr.ptr() as u64,
2175                    *PAGE_SIZE - 1,
2176                    name_addr.ptr() as u64,
2177                ),
2178                Ok(SUCCESS)
2179            );
2180        })
2181        .await;
2182    }
2183
2184    #[::fuchsia::test]
2185    async fn test_prctl_get_set_dumpable() {
2186        spawn_kernel_and_run(async |current_task| {
2187            assert_eq!(sys_prctl(current_task, PR_GET_DUMPABLE, 0, 0, 0, 0), Ok(1.into()));
2188
2189            sys_prctl(current_task, PR_SET_DUMPABLE, 0, 0, 0, 0).expect("failed to set dumpable");
2190            assert_eq!(sys_prctl(current_task, PR_GET_DUMPABLE, 0, 0, 0, 0), Ok(0.into()));
2191
2192            sys_prctl(current_task, PR_SET_DUMPABLE, 1, 0, 0, 0).expect("failed to set dumpable");
2193            assert_eq!(sys_prctl(current_task, PR_GET_DUMPABLE, 0, 0, 0, 0), Ok(1.into()));
2194
2195            // SUID_DUMP_ROOT not supported.
2196            assert_eq!(sys_prctl(current_task, PR_SET_DUMPABLE, 2, 0, 0, 0), error!(EINVAL));
2197            assert_eq!(sys_prctl(current_task, PR_GET_DUMPABLE, 0, 0, 0, 0), Ok(1.into()));
2198        })
2199        .await;
2200    }
2201
2202    #[cfg(target_arch = "aarch64")]
2203    #[::fuchsia::test]
2204    async fn test_prctl_sve_vl() {
2205        use crate::task::ArchExtendedPstateStorage;
2206        use extended_pstate::ExtendedPstateState;
2207        use starnix_uapi::{
2208            PR_SVE_GET_VL, PR_SVE_SET_VL, PR_SVE_SET_VL_ONEXEC, PR_SVE_VL_INHERIT, SVE_VL_MAX,
2209            SVE_VL_MIN,
2210        };
2211
2212        fn vl_result(value: u32) -> Result<SyscallResult, Errno> {
2213            Ok((value as usize).into())
2214        }
2215
2216        fn assert_sve_regs(current_task: &CurrentTask, p_regs: &[u16; 16], ffr: u16) {
2217            let ArchExtendedPstateStorage::State64(state) =
2218                &current_task.thread_state.extended_pstate
2219            else {
2220                unreachable!()
2221            };
2222            assert_eq!(state.get_arm64_p_regs(), p_regs);
2223            assert_eq!(state.get_arm64_ffr(), ffr);
2224        }
2225
2226        spawn_kernel_and_run(async |current_task| {
2227            // When SVE is not supported (`with_sve(false)`), both PR_SVE_GET_VL and PR_SVE_SET_VL
2228            // must fail with EINVAL.
2229            current_task.thread_state.extended_pstate =
2230                ArchExtendedPstateStorage::State64(Box::new(ExtendedPstateState::with_sve(false)));
2231            assert_eq!(sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0), error!(EINVAL));
2232            assert_eq!(
2233                sys_prctl(current_task, PR_SVE_SET_VL, SVE_VL_MIN as u64, 0, 0, 0),
2234                error!(EINVAL)
2235            );
2236
2237            // On a 32-bit compat task (`State32`), both must fail with EINVAL.
2238            current_task.thread_state.extended_pstate =
2239                ArchExtendedPstateStorage::State32(Box::default());
2240            assert_eq!(sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0), error!(EINVAL));
2241            assert_eq!(
2242                sys_prctl(current_task, PR_SVE_SET_VL, SVE_VL_MIN as u64, 0, 0, 0),
2243                error!(EINVAL)
2244            );
2245
2246            // Enable SVE with initial predicate and FFR values.
2247            let pregs = [0x5a5au16; 16];
2248            let mut pstate = ExtendedPstateState::with_sve(true);
2249            pstate.set_arm64_sve_state(&pregs, 0x00ff);
2250            current_task.thread_state.extended_pstate =
2251                ArchExtendedPstateStorage::State64(Box::new(pstate));
2252
2253            // PR_SVE_GET_VL returns 16 (SVE_VL_MIN) without PR_SVE_VL_INHERIT. Like Linux, the
2254            // unused trailing arguments are ignored: `prctl` is variadic and callers leave them
2255            // uninitialized.
2256            assert_eq!(sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0), vl_result(SVE_VL_MIN));
2257            assert_eq!(sys_prctl(current_task, PR_SVE_GET_VL, 1, 2, 3, 4), vl_result(SVE_VL_MIN));
2258
2259            // PR_SVE_SET_VL rejects unknown flags and out-of-range/unaligned VLs.
2260            assert_eq!(
2261                sys_prctl(current_task, PR_SVE_SET_VL, (SVE_VL_MIN | (1 << 20)) as u64, 0, 0, 0),
2262                error!(EINVAL)
2263            );
2264            assert_eq!(
2265                sys_prctl(current_task, PR_SVE_SET_VL, (SVE_VL_MIN as u64) | (1 << 32), 0, 0, 0),
2266                error!(EINVAL)
2267            );
2268            for bad_vl in [0u32, 8, 24, SVE_VL_MAX + 16] {
2269                assert_eq!(
2270                    sys_prctl(current_task, PR_SVE_SET_VL, bad_vl as u64, 0, 0, 0),
2271                    error!(EINVAL),
2272                    "bad_vl={} should fail",
2273                    bad_vl
2274                );
2275            }
2276            // Rejected requests leave the SVE registers and PR_SVE_VL_INHERIT untouched.
2277            assert_sve_regs(current_task, &pregs, 0x00ff);
2278            assert_eq!(sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0), vl_result(SVE_VL_MIN));
2279
2280            // An immediate PR_SVE_SET_VL clamps the VL to 16, which is already the thread's
2281            // current vector length, so, as on Linux, P0-P15 and FFR are preserved. The unused
2282            // trailing arguments are ignored.
2283            assert_eq!(sys_prctl(current_task, PR_SVE_SET_VL, 64, 1, 2, 3), vl_result(SVE_VL_MIN));
2284            assert_sve_regs(current_task, &pregs, 0x00ff);
2285
2286            // PR_SVE_VL_INHERIT is recorded, echoed back by PR_SVE_SET_VL, reported by
2287            // PR_SVE_GET_VL, and preserved across `reset` (execve).
2288            assert_eq!(
2289                sys_prctl(
2290                    current_task,
2291                    PR_SVE_SET_VL,
2292                    (SVE_VL_MAX | PR_SVE_VL_INHERIT) as u64,
2293                    0,
2294                    0,
2295                    0
2296                ),
2297                vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2298            );
2299            assert_sve_regs(current_task, &pregs, 0x00ff);
2300            assert_eq!(
2301                sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0),
2302                vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2303            );
2304            current_task.thread_state.extended_pstate.reset();
2305            assert_sve_regs(current_task, &[0u16; 16], 0);
2306            assert_eq!(
2307                sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0),
2308                vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2309            );
2310
2311            // PR_SVE_SET_VL_ONEXEC defers the (unchanged) vector length to the next execve, but
2312            // still updates PR_SVE_VL_INHERIT immediately, as on Linux.
2313            assert_eq!(
2314                sys_prctl(current_task, PR_SVE_SET_VL, (32 | PR_SVE_SET_VL_ONEXEC) as u64, 0, 0, 0),
2315                vl_result(SVE_VL_MIN)
2316            );
2317            assert_eq!(sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0), vl_result(SVE_VL_MIN));
2318            assert_eq!(
2319                sys_prctl(
2320                    current_task,
2321                    PR_SVE_SET_VL,
2322                    (32 | PR_SVE_SET_VL_ONEXEC | PR_SVE_VL_INHERIT) as u64,
2323                    0,
2324                    0,
2325                    0
2326                ),
2327                vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2328            );
2329            assert_eq!(
2330                sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0),
2331                vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2332            );
2333
2334            // A PR_SVE_SET_VL without PR_SVE_VL_INHERIT clears the setting again.
2335            assert_eq!(
2336                sys_prctl(current_task, PR_SVE_SET_VL, SVE_VL_MIN as u64, 0, 0, 0),
2337                vl_result(SVE_VL_MIN)
2338            );
2339            assert_eq!(sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0), vl_result(SVE_VL_MIN));
2340        })
2341        .await;
2342    }
2343
2344    /// The live register state used by the `PR_SVE_*` tests below.
2345    #[cfg(target_arch = "aarch64")]
2346    struct SveTestRegs {
2347        qregs: [u128; 32],
2348        fpsr: u32,
2349        fpcr: u32,
2350        pregs: [u16; 16],
2351        ffr: u16,
2352    }
2353
2354    #[cfg(target_arch = "aarch64")]
2355    impl SveTestRegs {
2356        fn sample() -> Self {
2357            let mut qregs = [0u128; 32];
2358            for (i, q) in qregs.iter_mut().enumerate() {
2359                *q = (i as u128 + 1) * 0x0102030405060708_u128;
2360            }
2361            let mut pregs = [0u16; 16];
2362            for (i, p) in pregs.iter_mut().enumerate() {
2363                *p = 0x5a00 | (i as u16 + 1);
2364            }
2365            Self { qregs, fpsr: 0x10, fpcr: 0x20, pregs, ffr: 0x00ff }
2366        }
2367
2368        fn install(&self, current_task: &mut CurrentTask, has_sve: bool) {
2369            use crate::task::ArchExtendedPstateStorage;
2370            use extended_pstate::ExtendedPstateState;
2371            let mut pstate = ExtendedPstateState::with_sve(has_sve);
2372            pstate.set_arm64_state(&self.qregs, self.fpsr, self.fpcr);
2373            pstate.set_arm64_sve_state(&self.pregs, self.ffr);
2374            current_task.thread_state.extended_pstate =
2375                ArchExtendedPstateStorage::State64(Box::new(pstate));
2376        }
2377
2378        fn assert_live(&self, current_task: &CurrentTask, context: &str) {
2379            use crate::task::ArchExtendedPstateStorage;
2380            let ArchExtendedPstateStorage::State64(state) =
2381                &current_task.thread_state.extended_pstate
2382            else {
2383                panic!("{context}: not 64-bit state");
2384            };
2385            assert_eq!(state.get_arm64_qregs(), &self.qregs, "{context}");
2386            assert_eq!(state.get_arm64_fpsr(), self.fpsr, "{context}");
2387            assert_eq!(state.get_arm64_fpcr(), self.fpcr, "{context}");
2388            assert_eq!(state.get_arm64_p_regs(), &self.pregs, "{context}");
2389            assert_eq!(state.get_arm64_ffr(), self.ffr, "{context}");
2390        }
2391    }
2392
2393    #[cfg(target_arch = "aarch64")]
2394    fn sve_vl_result(value: u32) -> Result<SyscallResult, Errno> {
2395        Ok((value as usize).into())
2396    }
2397
2398    #[cfg(target_arch = "aarch64")]
2399    #[::fuchsia::test]
2400    async fn test_prctl_sve_vl_ignores_trailing_args_and_preserves_registers() {
2401        use starnix_uapi::{
2402            PR_SVE_GET_VL, PR_SVE_SET_VL, PR_SVE_SET_VL_ONEXEC, PR_SVE_VL_INHERIT, SVE_VL_MAX,
2403            SVE_VL_MIN,
2404        };
2405        const INHERIT: u32 = PR_SVE_VL_INHERIT;
2406        const ONEXEC: u32 = PR_SVE_SET_VL_ONEXEC;
2407
2408        spawn_kernel_and_run(async |current_task| {
2409            let regs = SveTestRegs::sample();
2410            regs.install(current_task, true);
2411
2412            // `prctl` is variadic, so Linux's `PR_SVE_GET_VL` ignores `arg2`..`arg5` (and
2413            // `PR_SVE_SET_VL` ignores `arg3`..`arg5`) entirely: any garbage left in the unused
2414            // argument registers by the caller must not make the call fail.
2415            let junk_args: [[u64; 4]; 5] = [
2416                [0, 0, 0, 0],
2417                [u64::MAX, u64::MAX, u64::MAX, u64::MAX],
2418                [1 << 63, 0, 0, 0],
2419                [0, 0, 0, u64::MAX],
2420                [0xdead_beef, 0xcafe_babe, 0xfeed_face, 0xbaad_f00d],
2421            ];
2422            for [arg2, arg3, arg4, arg5] in junk_args {
2423                assert_eq!(
2424                    sys_prctl(current_task, PR_SVE_GET_VL, arg2, arg3, arg4, arg5),
2425                    sve_vl_result(SVE_VL_MIN),
2426                    "PR_SVE_GET_VL({arg2:#x}, {arg3:#x}, {arg4:#x}, {arg5:#x})"
2427                );
2428            }
2429            regs.assert_live(current_task, "after PR_SVE_GET_VL");
2430
2431            // Every valid `PR_SVE_SET_VL` request clamps to the thread's current (and only)
2432            // 128-bit vector length. As on Linux, setting the current vector length, whether
2433            // immediately or deferred with `PR_SVE_SET_VL_ONEXEC`, never touches the live
2434            // Q0-Q31/FPSR/FPCR/P0-P15/FFR state, while `PR_SVE_VL_INHERIT` is always updated
2435            // immediately and reported by both the call itself and `PR_SVE_GET_VL`.
2436            let set_vl_cases: [(u32, [u64; 3], u32); 9] = [
2437                (SVE_VL_MIN, [0, 0, 0], SVE_VL_MIN),
2438                (SVE_VL_MIN, [u64::MAX, u64::MAX, u64::MAX], SVE_VL_MIN),
2439                (SVE_VL_MAX | INHERIT, [u64::MAX, 1, 0], SVE_VL_MIN | INHERIT),
2440                (32 | ONEXEC, [u64::MAX, u64::MAX, u64::MAX], SVE_VL_MIN),
2441                (
2442                    SVE_VL_MIN | ONEXEC | INHERIT,
2443                    [u64::MAX, u64::MAX, u64::MAX],
2444                    SVE_VL_MIN | INHERIT,
2445                ),
2446                (4096 | ONEXEC | INHERIT, [7, 8, 9], SVE_VL_MIN | INHERIT),
2447                (SVE_VL_MIN | ONEXEC, [0, 0, 0], SVE_VL_MIN),
2448                (256 | INHERIT, [0, 0, 1 << 63], SVE_VL_MIN | INHERIT),
2449                (SVE_VL_MIN, [1, 2, 3], SVE_VL_MIN),
2450            ];
2451            for (arg2, [arg3, arg4, arg5], expected) in set_vl_cases {
2452                let context = format!("PR_SVE_SET_VL({arg2:#x}, {arg3:#x}, {arg4:#x}, {arg5:#x})");
2453                assert_eq!(
2454                    sys_prctl(current_task, PR_SVE_SET_VL, arg2 as u64, arg3, arg4, arg5),
2455                    sve_vl_result(expected),
2456                    "{context}"
2457                );
2458                regs.assert_live(current_task, &context);
2459                assert_eq!(
2460                    current_task.thread_state.extended_pstate.sve_vl_inherit(),
2461                    (arg2 & INHERIT) != 0,
2462                    "{context}"
2463                );
2464                for [garg2, garg3, garg4, garg5] in junk_args {
2465                    assert_eq!(
2466                        sys_prctl(current_task, PR_SVE_GET_VL, garg2, garg3, garg4, garg5),
2467                        sve_vl_result(expected),
2468                        "{context}"
2469                    );
2470                }
2471            }
2472
2473            // Invalid `arg2` values fail with EINVAL regardless of the trailing arguments, and
2474            // leave both the registers and the `PR_SVE_VL_INHERIT` setting untouched.
2475            assert_eq!(
2476                sys_prctl(current_task, PR_SVE_SET_VL, (SVE_VL_MIN | INHERIT) as u64, 0, 0, 0),
2477                sve_vl_result(SVE_VL_MIN | INHERIT)
2478            );
2479            let invalid_arg2: [u64; 7] = [
2480                u64::MAX,
2481                (SVE_VL_MIN as u64) | (1 << 32),
2482                0,
2483                8,
2484                (SVE_VL_MAX + 16) as u64,
2485                (SVE_VL_MIN | ONEXEC | INHERIT | (1 << 16)) as u64,
2486                (SVE_VL_MIN | (1 << 31)) as u64,
2487            ];
2488            for arg2 in invalid_arg2 {
2489                for [_, arg3, arg4, arg5] in junk_args {
2490                    let context =
2491                        format!("PR_SVE_SET_VL({arg2:#x}, {arg3:#x}, {arg4:#x}, {arg5:#x})");
2492                    assert_eq!(
2493                        sys_prctl(current_task, PR_SVE_SET_VL, arg2, arg3, arg4, arg5),
2494                        error!(EINVAL),
2495                        "{context}"
2496                    );
2497                    regs.assert_live(current_task, &context);
2498                    assert!(
2499                        current_task.thread_state.extended_pstate.sve_vl_inherit(),
2500                        "{context}"
2501                    );
2502                }
2503            }
2504            assert_eq!(
2505                sys_prctl(current_task, PR_SVE_GET_VL, u64::MAX, u64::MAX, u64::MAX, u64::MAX),
2506                sve_vl_result(SVE_VL_MIN | INHERIT)
2507            );
2508        })
2509        .await;
2510    }
2511
2512    #[cfg(target_arch = "aarch64")]
2513    #[::fuchsia::test]
2514    async fn test_prctl_sve_vl_arch32() {
2515        use crate::task::ArchExtendedPstateStorage;
2516        use starnix_uapi::{
2517            PR_SVE_GET_VL, PR_SVE_SET_VL, PR_SVE_SET_VL_ONEXEC, PR_SVE_VL_INHERIT, SVE_VL_MAX,
2518            SVE_VL_MIN,
2519        };
2520
2521        spawn_kernel_and_run(async |current_task| {
2522            // Start from a 64-bit task with SVE and `PR_SVE_VL_INHERIT` set, then `execve` a
2523            // 32-bit binary following the `finish_exec` sequence: `registers.load(regs)` followed
2524            // by `extended_pstate.reset_with_arch(arch_width())`, which switches the storage to
2525            // `State32`.
2526            SveTestRegs::sample().install(current_task, true);
2527            assert_eq!(
2528                sys_prctl(
2529                    current_task,
2530                    PR_SVE_SET_VL,
2531                    (SVE_VL_MIN | PR_SVE_VL_INHERIT) as u64,
2532                    0,
2533                    0,
2534                    0
2535                ),
2536                sve_vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2537            );
2538            let mut regs32 = zx::sys::zx_restricted_state_t::default();
2539            regs32.cpsr = zx::sys::ZX_REG_CPSR_ARCH_32_MASK as u32;
2540            current_task.thread_state.registers.load(regs32);
2541            let arch_width = current_task.thread_state.arch_width();
2542            current_task.thread_state.extended_pstate.reset_with_arch(arch_width);
2543            assert!(current_task.is_arch32());
2544            assert!(matches!(
2545                current_task.thread_state.extended_pstate,
2546                ArchExtendedPstateStorage::State32(_)
2547            ));
2548            assert!(!current_task.thread_state.extended_pstate.has_sve());
2549            assert!(!current_task.thread_state.extended_pstate.sve_vl_inherit());
2550
2551            // Like Linux (where compat tasks have no SVE prctls), both `PR_SVE_GET_VL` and
2552            // `PR_SVE_SET_VL` fail with EINVAL on a 32-bit task, regardless of the arguments.
2553            let args: [[u64; 4]; 7] = [
2554                [0, 0, 0, 0],
2555                [SVE_VL_MIN as u64, 0, 0, 0],
2556                [(SVE_VL_MIN | PR_SVE_VL_INHERIT) as u64, 0, 0, 0],
2557                [(SVE_VL_MAX | PR_SVE_SET_VL_ONEXEC | PR_SVE_VL_INHERIT) as u64, 0, 0, 0],
2558                [SVE_VL_MIN as u64, u64::MAX, u64::MAX, u64::MAX],
2559                [u64::MAX, u64::MAX, u64::MAX, u64::MAX],
2560                [1, 2, 3, 4],
2561            ];
2562            for [arg2, arg3, arg4, arg5] in args {
2563                let context = format!("({arg2:#x}, {arg3:#x}, {arg4:#x}, {arg5:#x})");
2564                assert_eq!(
2565                    sys_prctl(current_task, PR_SVE_GET_VL, arg2, arg3, arg4, arg5),
2566                    error!(EINVAL),
2567                    "PR_SVE_GET_VL{context}"
2568                );
2569                assert_eq!(
2570                    sys_prctl(current_task, PR_SVE_SET_VL, arg2, arg3, arg4, arg5),
2571                    error!(EINVAL),
2572                    "PR_SVE_SET_VL{context}"
2573                );
2574            }
2575            // The 32-bit state is left untouched.
2576            let ArchExtendedPstateStorage::State32(state32) =
2577                &current_task.thread_state.extended_pstate
2578            else {
2579                unreachable!()
2580            };
2581            assert_eq!(state32.get_arm32_qregs(), &[0u128; 16]);
2582            assert_eq!(state32.get_arm32_fpsr(), 0);
2583            assert_eq!(state32.get_arm32_fpcr(), 0);
2584            assert!(!current_task.thread_state.extended_pstate.sve_vl_inherit());
2585
2586            // `execve` back into a 64-bit binary creates fresh 64-bit storage: the earlier
2587            // `PR_SVE_VL_INHERIT` setting did not survive the trip through the 32-bit task.
2588            current_task.thread_state.registers.load(zx::sys::zx_restricted_state_t::default());
2589            let arch_width = current_task.thread_state.arch_width();
2590            current_task.thread_state.extended_pstate.reset_with_arch(arch_width);
2591            assert!(!current_task.is_arch32());
2592            assert!(matches!(
2593                current_task.thread_state.extended_pstate,
2594                ArchExtendedPstateStorage::State64(_)
2595            ));
2596            assert!(!current_task.thread_state.extended_pstate.sve_vl_inherit());
2597            let expected = if current_task.thread_state.extended_pstate.has_sve() {
2598                sve_vl_result(SVE_VL_MIN)
2599            } else {
2600                error!(EINVAL)
2601            };
2602            assert_eq!(sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0), expected);
2603        })
2604        .await;
2605    }
2606
2607    #[cfg(target_arch = "aarch64")]
2608    #[::fuchsia::test]
2609    async fn test_prctl_sve_vl_inherited_by_clone() {
2610        use crate::task::ArchExtendedPstateStorage;
2611        use extended_pstate::ExtendedPstateState;
2612        use starnix_uapi::{PR_SVE_GET_VL, PR_SVE_SET_VL, PR_SVE_VL_INHERIT, SVE_VL_MIN};
2613
2614        spawn_kernel_and_run(async |current_task| {
2615            let regs = SveTestRegs::sample();
2616            regs.install(current_task, true);
2617            assert_eq!(
2618                sys_prctl(
2619                    current_task,
2620                    PR_SVE_SET_VL,
2621                    (SVE_VL_MIN | PR_SVE_VL_INHERIT) as u64,
2622                    0,
2623                    0,
2624                    0
2625                ),
2626                sve_vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2627            );
2628
2629            // fork/clone copies the parent's thread state (`ThreadState::snapshot`): the child
2630            // inherits `has_sve`, the `PR_SVE_VL_INHERIT` setting, and the live Q0-Q31/FPSR/FPCR
2631            // and P0-P15/FFR registers, as on Linux.
2632            let mut child = current_task.clone_task_for_test(0, Some(SIGCHLD));
2633            assert!(child.thread_state.extended_pstate.has_sve());
2634            assert!(child.thread_state.extended_pstate.sve_vl_inherit());
2635            regs.assert_live(&child, "child after clone");
2636            assert_eq!(
2637                sys_prctl(&mut child, PR_SVE_GET_VL, 0, 0, 0, 0),
2638                sve_vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2639            );
2640            let (
2641                ArchExtendedPstateStorage::State64(parent_state),
2642                ArchExtendedPstateStorage::State64(child_state),
2643            ) = (&current_task.thread_state.extended_pstate, &child.thread_state.extended_pstate)
2644            else {
2645                unreachable!()
2646            };
2647            assert!(!std::ptr::eq(parent_state.as_ref(), child_state.as_ref()));
2648
2649            // The child's `PR_SVE_SET_VL` does not affect the parent, and the parent's `execve`
2650            // (`reset`) does not affect the child.
2651            assert_eq!(
2652                sys_prctl(&mut child, PR_SVE_SET_VL, SVE_VL_MIN as u64, 0, 0, 0),
2653                sve_vl_result(SVE_VL_MIN)
2654            );
2655            assert_eq!(sys_prctl(&mut child, PR_SVE_GET_VL, 0, 0, 0, 0), sve_vl_result(SVE_VL_MIN));
2656            assert_eq!(
2657                sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0),
2658                sve_vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2659            );
2660            regs.assert_live(current_task, "parent after child's PR_SVE_SET_VL");
2661            current_task.thread_state.extended_pstate.reset();
2662            regs.assert_live(&child, "child after parent's reset");
2663            assert_eq!(
2664                sys_prctl(current_task, PR_SVE_GET_VL, 0, 0, 0, 0),
2665                sve_vl_result(SVE_VL_MIN | PR_SVE_VL_INHERIT)
2666            );
2667
2668            // A child cloned after `PR_SVE_SET_VL` without `PR_SVE_VL_INHERIT` does not inherit
2669            // the setting either.
2670            assert_eq!(
2671                sys_prctl(current_task, PR_SVE_SET_VL, SVE_VL_MIN as u64, 0, 0, 0),
2672                sve_vl_result(SVE_VL_MIN)
2673            );
2674            let mut child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2675            assert!(child2.thread_state.extended_pstate.has_sve());
2676            assert!(!child2.thread_state.extended_pstate.sve_vl_inherit());
2677            assert_eq!(
2678                sys_prctl(&mut child2, PR_SVE_GET_VL, 0, 0, 0, 0),
2679                sve_vl_result(SVE_VL_MIN)
2680            );
2681
2682            // A parent without SVE support forks a child without SVE support.
2683            current_task.thread_state.extended_pstate =
2684                ArchExtendedPstateStorage::State64(Box::new(ExtendedPstateState::with_sve(false)));
2685            let mut child3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2686            assert!(!child3.thread_state.extended_pstate.has_sve());
2687            assert_eq!(sys_prctl(&mut child3, PR_SVE_GET_VL, 0, 0, 0, 0), error!(EINVAL));
2688            assert_eq!(
2689                sys_prctl(&mut child3, PR_SVE_SET_VL, SVE_VL_MIN as u64, 0, 0, 0),
2690                error!(EINVAL)
2691            );
2692        })
2693        .await;
2694    }
2695
2696    #[::fuchsia::test]
2697    async fn test_sys_getsid() {
2698        spawn_kernel_and_run(async |current_task| {
2699            let kernel = current_task.kernel();
2700            assert_eq!(
2701                current_task.get_tid(),
2702                sys_getsid(&current_task, 0).expect("failed to get sid")
2703            );
2704
2705            let second_task = crate::execution::create_init_child_process(
2706                &kernel.weak_self.upgrade().unwrap(),
2707                TaskCommand::new(b"second task"),
2708                Credentials::with_ids(0, 0),
2709                None,
2710            )
2711            .expect("failed to create second task");
2712            let second_current = AutoReleasableTask::from(second_task);
2713
2714            assert_eq!(
2715                second_current.get_tid(),
2716                sys_getsid(&current_task, second_current.get_tid()).expect("failed to get sid")
2717            );
2718        })
2719        .await;
2720    }
2721
2722    #[::fuchsia::test]
2723    async fn test_get_affinity_size() {
2724        spawn_kernel_and_run(async |current_task| {
2725            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2726            let pid = current_task.get_pid();
2727            assert_eq!(sys_sched_getaffinity(&current_task, pid, 16, mapped_address), Ok(16));
2728            assert_eq!(
2729                sys_sched_getaffinity(&current_task, pid, 1024, mapped_address),
2730                Ok(std::mem::size_of::<CpuSet>())
2731            );
2732            assert_eq!(
2733                sys_sched_getaffinity(&current_task, pid, 1, mapped_address),
2734                error!(EINVAL)
2735            );
2736            assert_eq!(
2737                sys_sched_getaffinity(&current_task, pid, 9, mapped_address),
2738                error!(EINVAL)
2739            );
2740        })
2741        .await;
2742    }
2743
2744    #[::fuchsia::test]
2745    async fn test_set_affinity_size() {
2746        spawn_kernel_and_run(async |current_task| {
2747            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2748            current_task.write_memory(mapped_address, &[0xffu8]).expect("failed to cpumask");
2749            let pid = current_task.get_pid();
2750            assert_eq!(
2751                sys_sched_setaffinity(&current_task, pid, *PAGE_SIZE as u32, mapped_address),
2752                Ok(())
2753            );
2754            assert_eq!(
2755                sys_sched_setaffinity(&current_task, pid, 1, mapped_address),
2756                error!(EINVAL)
2757            );
2758        })
2759        .await;
2760    }
2761
2762    #[::fuchsia::test]
2763    async fn test_task_name() {
2764        spawn_kernel_and_run(async |current_task| {
2765            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2766            let name = "my-task-name\0";
2767            current_task
2768                .write_memory(mapped_address, name.as_bytes())
2769                .expect("failed to write name");
2770
2771            let result =
2772                sys_prctl(current_task, PR_SET_NAME, mapped_address.ptr() as u64, 0, 0, 0).unwrap();
2773            assert_eq!(SUCCESS, result);
2774
2775            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2776            let result =
2777                sys_prctl(current_task, PR_GET_NAME, mapped_address.ptr() as u64, 0, 0, 0).unwrap();
2778            assert_eq!(SUCCESS, result);
2779
2780            let name_length = name.len();
2781
2782            let out_name = current_task.read_memory_to_vec(mapped_address, name_length).unwrap();
2783            assert_eq!(name.as_bytes(), &out_name);
2784        })
2785        .await;
2786    }
2787
2788    #[::fuchsia::test]
2789    async fn test_sched_get_priority_min_max() {
2790        spawn_kernel_and_run(async |current_task| {
2791            let non_rt_min = sys_sched_get_priority_min(&current_task, SCHED_NORMAL).unwrap();
2792            assert_eq!(non_rt_min, 0);
2793            let non_rt_max = sys_sched_get_priority_max(&current_task, SCHED_NORMAL).unwrap();
2794            assert_eq!(non_rt_max, 0);
2795
2796            let rt_min = sys_sched_get_priority_min(&current_task, SCHED_FIFO).unwrap();
2797            assert_eq!(rt_min, 1);
2798            let rt_max = sys_sched_get_priority_max(&current_task, SCHED_FIFO).unwrap();
2799            assert_eq!(rt_max, 99);
2800
2801            let min_bad_policy_error =
2802                sys_sched_get_priority_min(&current_task, u32::MAX).unwrap_err();
2803            assert_eq!(min_bad_policy_error, errno!(EINVAL));
2804
2805            let max_bad_policy_error =
2806                sys_sched_get_priority_max(&current_task, u32::MAX).unwrap_err();
2807            assert_eq!(max_bad_policy_error, errno!(EINVAL));
2808        })
2809        .await;
2810    }
2811
2812    #[::fuchsia::test]
2813    async fn test_sched_setscheduler() {
2814        spawn_kernel_and_run(async |current_task| {
2815            current_task
2816                .thread_group()
2817                .limits
2818                .lock()
2819                .set(Resource::RTPRIO, uapi::rlimit { rlim_cur: 255, rlim_max: 255 });
2820
2821            let scheduler = sys_sched_getscheduler(&current_task, 0).unwrap();
2822            assert_eq!(scheduler, SCHED_NORMAL, "tasks should have normal scheduler by default");
2823
2824            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2825            let requested_params = sched_param { sched_priority: 15 };
2826            current_task.write_object(mapped_address.into(), &requested_params).unwrap();
2827
2828            sys_sched_setscheduler(&current_task, 0, SCHED_FIFO, mapped_address.into()).unwrap();
2829
2830            let new_scheduler = sys_sched_getscheduler(&current_task, 0).unwrap();
2831            assert_eq!(new_scheduler, SCHED_FIFO, "task should have been assigned fifo scheduler");
2832
2833            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2834            sys_sched_getparam(&current_task, 0, mapped_address.into()).expect("sched_getparam");
2835            let param_value: sched_param =
2836                current_task.read_object(mapped_address.into()).expect("read_object");
2837            assert_eq!(param_value.sched_priority, 15);
2838        })
2839        .await;
2840    }
2841
2842    #[::fuchsia::test]
2843    async fn test_sched_getparam() {
2844        spawn_kernel_and_run(async |current_task| {
2845            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2846            sys_sched_getparam(&current_task, 0, mapped_address.into()).expect("sched_getparam");
2847            let param_value: sched_param =
2848                current_task.read_object(mapped_address.into()).expect("read_object");
2849            assert_eq!(param_value.sched_priority, 0);
2850        })
2851        .await;
2852    }
2853
2854    #[::fuchsia::test]
2855    async fn test_setuid() {
2856        spawn_kernel_and_run(async |current_task| {
2857            // Test for root.
2858            current_task.set_creds(Credentials::with_ids(0, 0));
2859            sys_setuid(&current_task, 42).expect("setuid");
2860            let mut creds = Credentials::clone(&current_task.current_creds());
2861            assert_eq!(creds.euid, 42);
2862            assert_eq!(creds.uid, 42);
2863            assert_eq!(creds.saved_uid, 42);
2864
2865            // Remove the CAP_SETUID capability to avoid overwriting permission checks.
2866            creds.cap_effective.remove(CAP_SETUID);
2867            current_task.set_creds(creds);
2868
2869            // Test for non root, which task now is.
2870            assert_eq!(sys_setuid(&current_task, 0), error!(EPERM));
2871            assert_eq!(sys_setuid(&current_task, 43), error!(EPERM));
2872
2873            sys_setuid(&current_task, 42).expect("setuid");
2874            assert_eq!(current_task.current_creds().euid, 42);
2875            assert_eq!(current_task.current_creds().uid, 42);
2876            assert_eq!(current_task.current_creds().saved_uid, 42);
2877
2878            // Change uid and saved_uid, and check that one can set the euid to these.
2879            let mut creds = Credentials::clone(&current_task.current_creds());
2880            creds.uid = 41;
2881            creds.euid = 42;
2882            creds.saved_uid = 43;
2883            current_task.set_creds(creds);
2884
2885            sys_setuid(&current_task, 41).expect("setuid");
2886            assert_eq!(current_task.current_creds().euid, 41);
2887            assert_eq!(current_task.current_creds().uid, 41);
2888            assert_eq!(current_task.current_creds().saved_uid, 43);
2889
2890            let mut creds = Credentials::clone(&current_task.current_creds());
2891            creds.uid = 41;
2892            creds.euid = 42;
2893            creds.saved_uid = 43;
2894            current_task.set_creds(creds);
2895
2896            sys_setuid(&current_task, 43).expect("setuid");
2897            assert_eq!(current_task.current_creds().euid, 43);
2898            assert_eq!(current_task.current_creds().uid, 41);
2899            assert_eq!(current_task.current_creds().saved_uid, 43);
2900        })
2901        .await;
2902    }
2903
2904    #[::fuchsia::test]
2905    async fn test_prlimit64_stack() {
2906        spawn_kernel_and_run(async |current_task| {
2907            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2908            let old_limit_ref = UserRef::<uapi::rlimit>::new(mapped_address);
2909            let new_limit_ref = UserRef::<uapi::rlimit>::new(
2910                (mapped_address + std::mem::size_of::<uapi::rlimit>()).unwrap(),
2911            );
2912
2913            sys_prlimit64(current_task, 0, uapi::RLIMIT_STACK, UserRef::default(), old_limit_ref)
2914                .expect("prlimit64 get RLIMIT_STACK");
2915            let initial_limit: uapi::rlimit =
2916                current_task.read_object(old_limit_ref).expect("read initial limit");
2917            assert_eq!(
2918                (initial_limit.rlim_cur, initial_limit.rlim_max),
2919                (uapi::_STK_LIM as u64, uapi::RLIM_INFINITY as u64)
2920            );
2921
2922            let updated_limit =
2923                uapi::rlimit { rlim_cur: 4 * 1024 * 1024, rlim_max: 16 * 1024 * 1024 };
2924            current_task.write_object(new_limit_ref, &updated_limit).expect("write new limit");
2925            sys_prlimit64(current_task, 0, uapi::RLIMIT_STACK, new_limit_ref, old_limit_ref)
2926                .expect("prlimit64 set RLIMIT_STACK");
2927            let prev_limit: uapi::rlimit =
2928                current_task.read_object(old_limit_ref).expect("read previous limit");
2929            assert_eq!(
2930                (prev_limit.rlim_cur, prev_limit.rlim_max),
2931                (initial_limit.rlim_cur, initial_limit.rlim_max)
2932            );
2933            let current_limit = current_task.thread_group().limits.lock().get(Resource::STACK);
2934            assert_eq!(
2935                (current_limit.rlim_cur, current_limit.rlim_max),
2936                (updated_limit.rlim_cur, updated_limit.rlim_max)
2937            );
2938
2939            // Setting rlim_cur > rlim_max must fail with EINVAL without changing the limit.
2940            let invalid_limit =
2941                uapi::rlimit { rlim_cur: 32 * 1024 * 1024, rlim_max: 16 * 1024 * 1024 };
2942            current_task.write_object(new_limit_ref, &invalid_limit).expect("write invalid limit");
2943            assert_eq!(
2944                sys_prlimit64(
2945                    current_task,
2946                    0,
2947                    uapi::RLIMIT_STACK,
2948                    new_limit_ref,
2949                    UserRef::default()
2950                ),
2951                error!(EINVAL)
2952            );
2953            let current_limit = current_task.thread_group().limits.lock().get(Resource::STACK);
2954            assert_eq!(
2955                (current_limit.rlim_cur, current_limit.rlim_max),
2956                (updated_limit.rlim_cur, updated_limit.rlim_max)
2957            );
2958
2959            // Setting rlim_cur = 0 and invoking execve with a non-empty argv must not fail with
2960            // E2BIG, because sys_execveat floors argv_env_limit at 32 * PAGE_SIZE.
2961            let zero_soft_limit = uapi::rlimit { rlim_cur: 0, rlim_max: 16 * 1024 * 1024 };
2962            current_task
2963                .write_object(new_limit_ref, &zero_soft_limit)
2964                .expect("write zero soft limit");
2965            sys_prlimit64(current_task, 0, uapi::RLIMIT_STACK, new_limit_ref, UserRef::default())
2966                .expect("prlimit64 set zero RLIMIT_STACK");
2967            assert_eq!(current_task.thread_group().get_rlimit(Resource::STACK), 0);
2968
2969            let arg_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
2970            current_task.write_memory(arg_addr, b"test-arg\0").expect("write test arg");
2971            let empty_path_addr = (arg_addr + 16usize).unwrap();
2972            current_task.write_memory(empty_path_addr, b"\0").expect("write empty path");
2973            let argv_addr = UserCStringPtr::new(
2974                current_task,
2975                map_memory(&current_task, UserAddress::default(), *PAGE_SIZE),
2976            );
2977            current_task
2978                .write_multi_arch_ptr(argv_addr.addr(), UserCString::new(current_task, arg_addr))
2979                .expect("write argv[0]");
2980            current_task
2981                .write_multi_arch_ptr(
2982                    argv_addr.next().unwrap().addr(),
2983                    UserCString::null(current_task),
2984                )
2985                .expect("write argv[1]");
2986            assert_eq!(
2987                sys_execve(
2988                    current_task,
2989                    UserCString::new(current_task, empty_path_addr),
2990                    argv_addr,
2991                    UserCStringPtr::null(current_task),
2992                ),
2993                error!(ENOENT)
2994            );
2995        })
2996        .await;
2997    }
2998
2999    #[::fuchsia::test]
3000    async fn test_read_c_string_vector() {
3001        spawn_kernel_and_run(async |current_task| {
3002            let arg_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3003            let arg = b"test-arg\0";
3004            current_task.write_memory(arg_addr, arg).expect("failed to write test arg");
3005            let arg_usercstr = UserCString::new(current_task, arg_addr);
3006            let null_usercstr = UserCString::null(current_task);
3007
3008            let argv_addr = UserCStringPtr::new(
3009                current_task,
3010                map_memory(&current_task, UserAddress::default(), *PAGE_SIZE),
3011            );
3012            current_task
3013                .write_multi_arch_ptr(argv_addr.addr(), arg_usercstr)
3014                .expect("failed to write UserCString");
3015            current_task
3016                .write_multi_arch_ptr(argv_addr.next().unwrap().addr(), null_usercstr)
3017                .expect("failed to write UserCString");
3018
3019            // The arguments size limit should include the null terminator.
3020            assert!(read_c_string_vector(&current_task, argv_addr, 100, arg.len()).is_ok());
3021            assert_eq!(
3022                read_c_string_vector(
3023                    &current_task,
3024                    argv_addr,
3025                    100,
3026                    std::str::from_utf8(arg).unwrap().trim_matches('\0').len()
3027                ),
3028                error!(E2BIG)
3029            );
3030        })
3031        .await;
3032    }
3033
3034    #[::fuchsia::test]
3035    async fn test_kcmp() {
3036        spawn_kernel_and_run(async |current_task| {
3037            let pid = current_task.get_pid();
3038            assert_eq!(sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_VM, 0, 0), Ok(0));
3039            assert_eq!(
3040                sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
3041                error!(EOPNOTSUPP)
3042            );
3043            assert_eq!(
3044                sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_SYSVSEM, 1, 2),
3045                error!(EOPNOTSUPP)
3046            );
3047            assert_eq!(
3048                sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_SYSVSEM, u64::MAX, u64::MAX),
3049                error!(EOPNOTSUPP)
3050            );
3051            assert_eq!(
3052                sys_kcmp(&current_task, pid, pid, kcmp_type_KCMP_TYPES, 0, 0),
3053                error!(EINVAL)
3054            );
3055            assert_eq!(sys_kcmp(&current_task, pid, pid, u32::MAX, 0, 0), error!(EINVAL));
3056            assert_eq!(
3057                sys_kcmp(&current_task, 0, pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
3058                error!(ESRCH)
3059            );
3060            assert_eq!(
3061                sys_kcmp(&current_task, pid, 0, kcmp_type_KCMP_SYSVSEM, 0, 0),
3062                error!(ESRCH)
3063            );
3064            assert_eq!(
3065                sys_kcmp(&current_task, -1, pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
3066                error!(ESRCH)
3067            );
3068            assert_eq!(
3069                sys_kcmp(&current_task, pid, -1, kcmp_type_KCMP_SYSVSEM, 0, 0),
3070                error!(ESRCH)
3071            );
3072
3073            // Verify ptrace access checks take precedence over resource type errors for both tasks.
3074            let target_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
3075            let target_pid = target_task.get_pid();
3076            current_task.set_creds(Credentials::with_ids(1, 1));
3077            assert_eq!(
3078                sys_kcmp(&current_task, target_pid, target_pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
3079                error!(EPERM)
3080            );
3081            assert_eq!(
3082                sys_kcmp(&current_task, pid, target_pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
3083                error!(EPERM)
3084            );
3085            assert_eq!(
3086                sys_kcmp(&current_task, target_pid, pid, kcmp_type_KCMP_SYSVSEM, 0, 0),
3087                error!(EPERM)
3088            );
3089        })
3090        .await;
3091    }
3092}