Skip to main content

starnix_core/mm/
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::mm::barrier::{BarrierType, system_barrier};
6use crate::mm::debugger::notify_debugger_of_module_list;
7use crate::mm::{
8    DesiredAddress, FutexKey, IOVecPtr, MappingName, MappingOptions, MembarrierType,
9    MemoryAccessorExt, MremapFlags, MsyncFlags, PAGE_SIZE, PrivateFutexKey, ProtectionFlags,
10    SharedFutexKey,
11};
12use crate::security;
13use crate::syscalls::time::TimeSpecPtr;
14use crate::task::{CurrentTask, ProcessEntryRef};
15use crate::time::TargetTime;
16use crate::time::utc::estimate_boot_deadline_from_utc;
17use crate::vfs::buffers::{OutputBuffer, UserBuffersInputBuffer, UserBuffersOutputBuffer};
18use crate::vfs::{FdNumber, PidFdFileObject};
19use fuchsia_runtime::UtcTimeline;
20use linux_uapi::MLOCK_ONFAULT;
21use starnix_logging::{CATEGORY_STARNIX_MM, log_trace, track_stub};
22use starnix_syscalls::SyscallArg;
23use starnix_types::time::{duration_from_timespec, time_from_timespec};
24use starnix_uapi::auth::{PTRACE_MODE_ATTACH_REALCREDS, PTRACE_MODE_READ_REALCREDS};
25use starnix_uapi::errors::{EINTR, Errno};
26use starnix_uapi::personality::PersonalityFlags;
27use starnix_uapi::user_address::{UserAddress, UserRef};
28use starnix_uapi::user_value::UserValue;
29use starnix_uapi::{
30    FUTEX_BITSET_MATCH_ANY, FUTEX_CLOCK_REALTIME, FUTEX_CMD_MASK, FUTEX_CMP_REQUEUE,
31    FUTEX_CMP_REQUEUE_PI, FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_PRIVATE_FLAG, FUTEX_REQUEUE,
32    FUTEX_TRYLOCK_PI, FUTEX_UNLOCK_PI, FUTEX_WAIT, FUTEX_WAIT_BITSET, FUTEX_WAIT_REQUEUE_PI,
33    FUTEX_WAKE, FUTEX_WAKE_BITSET, FUTEX_WAKE_OP, MAP_ANONYMOUS, MAP_DENYWRITE, MAP_FIXED,
34    MAP_FIXED_NOREPLACE, MAP_GROWSDOWN, MAP_LOCKED, MAP_NORESERVE, MAP_POPULATE, MAP_PRIVATE,
35    MAP_SHARED, MAP_SHARED_VALIDATE, MAP_STACK, PROT_EXEC, errno, error, robust_list_head, tid_t,
36    uapi,
37};
38use std::ops::Deref as _;
39use zx;
40
41#[cfg(target_arch = "x86_64")]
42use starnix_uapi::MAP_32BIT;
43
44// Returns any platform-specific mmap flags. This is a separate function because as of this writing
45// "attributes on expressions are experimental."
46#[cfg(target_arch = "x86_64")]
47fn get_valid_platform_mmap_flags() -> u32 {
48    MAP_32BIT
49}
50#[cfg(not(target_arch = "x86_64"))]
51fn get_valid_platform_mmap_flags() -> u32 {
52    0
53}
54
55/// sys_mmap takes a mutable reference to current_task because it may modify the IP register.
56pub fn sys_mmap(
57    current_task: &mut CurrentTask,
58    addr: UserAddress,
59    length: usize,
60    prot: u32,
61    flags: u32,
62    fd: FdNumber,
63    offset: u64,
64) -> Result<UserAddress, Errno> {
65    let user_address = do_mmap(current_task, addr, length, prot, flags, fd, offset)?;
66    // Use the requested `prot` rather than the effective protection, which may include `PROT_EXEC`
67    // implied by `READ_IMPLIES_EXEC`: dynamic linkers request `PROT_EXEC` explicitly.
68    if prot & PROT_EXEC != 0 {
69        // Possibly loads a new module. Notify debugger for the change.
70        // We only care about dynamic linker loading modules for now, which uses mmap. In the future
71        // we might want to support unloading modules in munmap or JIT compilation in mprotect.
72        notify_debugger_of_module_list(current_task)?;
73    }
74    Ok(user_address)
75}
76
77/// Returns whether [`ProtectionFlags::READ`] implies [`ProtectionFlags::EXEC`] for
78/// `current_task`, i.e. whether the [`PersonalityFlags::READ_IMPLIES_EXEC`] personality flag is
79/// set.
80///
81/// `personality(2)`:
82/// > READ_IMPLIES_EXEC (since Linux 2.6.8)
83/// >        With this flag set, PROT_READ implies PROT_EXEC for mmap(2).
84///
85/// TODO(https://fxbug.dev/508555755): `READ_IMPLIES_EXEC` should be cleared when executing a
86/// program with more privileges (e.g. set-user-ID, or with an SELinux domain transition).
87fn read_implies_exec(current_task: &CurrentTask) -> bool {
88    current_task.thread_group().read().personality.contains(PersonalityFlags::READ_IMPLIES_EXEC)
89}
90
91pub fn do_mmap(
92    current_task: &CurrentTask,
93    addr: UserAddress,
94    length: usize,
95    prot: u32,
96    flags: u32,
97    fd: FdNumber,
98    offset: u64,
99) -> Result<UserAddress, Errno> {
100    let mut prot_flags = ProtectionFlags::from_access_bits(prot).ok_or_else(|| {
101        track_stub!(TODO("https://fxbug.dev/322874211"), "mmap parse protection", prot);
102        errno!(EINVAL)
103    })?;
104
105    let valid_flags: u32 = get_valid_platform_mmap_flags()
106        | MAP_PRIVATE
107        | MAP_SHARED
108        | MAP_SHARED_VALIDATE
109        | MAP_ANONYMOUS
110        | MAP_FIXED
111        | MAP_FIXED_NOREPLACE
112        | MAP_POPULATE
113        | MAP_NORESERVE
114        | MAP_STACK
115        | MAP_DENYWRITE
116        | MAP_GROWSDOWN
117        | MAP_LOCKED;
118    if flags & !valid_flags != 0 {
119        if flags & MAP_SHARED_VALIDATE != 0 {
120            return error!(EOPNOTSUPP);
121        }
122        track_stub!(TODO("https://fxbug.dev/322873638"), "mmap check flags", flags);
123        return error!(EINVAL);
124    }
125
126    let file = if flags & MAP_ANONYMOUS != 0 { None } else { Some(current_task.get_file(fd)?) };
127
128    // With `READ_IMPLIES_EXEC`, `PROT_READ` implies `PROT_EXEC` (see personality(2)). Files that
129    // cannot be executed (e.g. on a `noexec` mount), or whose `mmap()` can't map them executable
130    // (e.g. binder or io_uring files), are still mapped, without `PROT_EXEC`.
131    if prot_flags.contains(ProtectionFlags::READ)
132        && read_implies_exec(current_task)
133        && file.as_ref().is_none_or(|file| file.can_exec() && file.ops().can_map_executable())
134    {
135        prot_flags |= ProtectionFlags::EXEC;
136    }
137
138    if flags & (MAP_PRIVATE | MAP_SHARED) == 0
139        || flags & (MAP_PRIVATE | MAP_SHARED) == MAP_PRIVATE | MAP_SHARED
140    {
141        return error!(EINVAL);
142    }
143    if length == 0 {
144        return error!(EINVAL);
145    }
146    if offset % *PAGE_SIZE != 0 {
147        return error!(EINVAL);
148    }
149
150    let page_size = *PAGE_SIZE as usize;
151    let length_aligned =
152        length.checked_add(page_size - 1).ok_or_else(|| errno!(ENOMEM))? & !(page_size - 1);
153    let rlimit_as = current_task
154        .thread_group()
155        .get_rlimit(starnix_uapi::resource_limits::Resource::AS) as usize;
156    let current_usage: usize = current_task.mm()?.get_total_usage();
157
158    if current_usage.saturating_add(length_aligned) > rlimit_as {
159        return error!(ENOMEM);
160    }
161
162    // TODO(tbodt): should we consider MAP_NORESERVE?
163
164    let addr = match (addr, flags & MAP_FIXED != 0, flags & MAP_FIXED_NOREPLACE != 0) {
165        (UserAddress::NULL, false, false) => DesiredAddress::Any,
166        (UserAddress::NULL, true, _) | (UserAddress::NULL, _, true) => return error!(EINVAL),
167        (addr, false, false) => DesiredAddress::Hint(addr),
168        (addr, _, true) => DesiredAddress::Fixed(addr),
169        (addr, true, false) => DesiredAddress::FixedOverwrite(addr),
170    };
171
172    let memory_offset = if flags & MAP_ANONYMOUS != 0 { 0 } else { offset };
173
174    let mut options = MappingOptions::empty();
175    if flags & MAP_SHARED != 0 {
176        options |= MappingOptions::SHARED;
177    }
178    if flags & MAP_ANONYMOUS != 0 {
179        options |= MappingOptions::ANONYMOUS;
180    }
181    #[cfg(target_arch = "x86_64")]
182    if flags & MAP_FIXED == 0 && flags & MAP_32BIT != 0 {
183        options |= MappingOptions::LOWER_32BIT;
184    }
185    if flags & MAP_GROWSDOWN != 0 {
186        options |= MappingOptions::GROWSDOWN;
187    }
188    if flags & MAP_POPULATE != 0 {
189        options |= MappingOptions::POPULATE;
190    }
191    if flags & MAP_LOCKED != 0 {
192        // The kernel isn't expected to return an error if locking fails with this flag, so for now
193        // this implementation will always fail to lock memory even if mapping succeeds.
194        track_stub!(TODO("https://fxbug.dev/406377606"), "MAP_LOCKED");
195    }
196
197    security::mmap_file(current_task, file.as_ref(), prot_flags, options)?;
198
199    if flags & MAP_ANONYMOUS != 0 {
200        fuchsia_trace::duration!(
201            CATEGORY_STARNIX_MM,
202            "AnonymousMmap",
203            "prot" => prot,
204            "map_fixed" => (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE)) != 0,
205        );
206        current_task.mm()?.map_anonymous(addr, length, prot_flags, options, MappingName::None)
207    } else {
208        let file = file.expect("file retrieved above for file-backed mapping");
209        fuchsia_trace::duration!(
210            CATEGORY_STARNIX_MM,
211            "FileBackedMmap",
212            "fs" => std::str::from_utf8(file.fs.name()).unwrap_or("<non-utf8>"),
213            "prot" => prot,
214            "map_fixed" => (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE)) != 0,
215        );
216        // TODO(tbodt): maximize protection flags so that mprotect works
217        file.mmap(current_task, addr, memory_offset, length, prot_flags, options)
218    }
219}
220
221pub fn sys_mprotect(
222    current_task: &CurrentTask,
223    addr: UserAddress,
224    length: usize,
225    prot: u32,
226) -> Result<(), Errno> {
227    let prot_flags = ProtectionFlags::from_bits(prot).ok_or_else(|| {
228        track_stub!(TODO("https://fxbug.dev/322874672"), "mprotect parse protection", prot);
229        errno!(EINVAL)
230    })?;
231    current_task.mm()?.protect(current_task, addr, length, prot_flags)?;
232    Ok(())
233}
234
235pub fn sys_mremap(
236    current_task: &CurrentTask,
237    addr: UserAddress,
238    old_length: usize,
239    new_length: usize,
240    flags: u32,
241    new_addr: UserAddress,
242) -> Result<UserAddress, Errno> {
243    let flags = MremapFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL))?;
244    let addr =
245        current_task.mm()?.remap(current_task, addr, old_length, new_length, flags, new_addr)?;
246    Ok(addr)
247}
248
249pub fn sys_munmap(
250    current_task: &CurrentTask,
251    addr: UserAddress,
252    length: usize,
253) -> Result<(), Errno> {
254    current_task.mm()?.unmap(addr, length)?;
255    Ok(())
256}
257
258pub fn sys_msync(
259    current_task: &CurrentTask,
260    addr: UserAddress,
261    length: usize,
262    flags: u32,
263) -> Result<(), Errno> {
264    let flags = MsyncFlags::from_bits_retain(flags);
265    current_task.mm()?.msync(current_task, addr, length, flags)
266}
267
268pub fn sys_madvise(
269    current_task: &CurrentTask,
270    addr: UserAddress,
271    length: usize,
272    advice: u32,
273) -> Result<(), Errno> {
274    current_task.mm()?.madvise(current_task, addr, length, advice)?;
275    Ok(())
276}
277
278pub fn sys_process_madvise(
279    _current_task: &CurrentTask,
280    _pidfd: FdNumber,
281    _iovec_addr: IOVecPtr,
282    _iovec_count: UserValue<i32>,
283    _advice: UserValue<i32>,
284    _flags: UserValue<u32>,
285) -> Result<usize, Errno> {
286    track_stub!(TODO("https://fxbug.dev/409060664"), "process_madvise");
287    error!(ENOSYS)
288}
289
290pub fn sys_brk(current_task: &CurrentTask, addr: UserAddress) -> Result<UserAddress, Errno> {
291    current_task.mm()?.set_brk(current_task, addr)
292}
293
294pub fn sys_process_vm_readv(
295    current_task: &CurrentTask,
296    tid: tid_t,
297    local_iov_addr: IOVecPtr,
298    local_iov_count: UserValue<i32>,
299    remote_iov_addr: IOVecPtr,
300    remote_iov_count: UserValue<i32>,
301    flags: usize,
302) -> Result<usize, Errno> {
303    if flags != 0 {
304        return error!(EINVAL);
305    }
306
307    // Source and destination are allowed to be of different length. It is valid to use a nullptr if
308    // the associated length is 0. Thus, if either source or destination length is 0 and nullptr,
309    // make sure to return Ok(0) before doing any other validation/operations.
310    if (local_iov_count == 0 && local_iov_addr.is_null())
311        || (remote_iov_count == 0 && remote_iov_addr.is_null())
312    {
313        return Ok(0);
314    }
315
316    let remote_task = current_task.get_task(tid)?;
317
318    current_task.check_ptrace_access_mode(PTRACE_MODE_ATTACH_REALCREDS, &remote_task)?;
319
320    let local_iov = current_task.read_iovec(local_iov_addr, local_iov_count)?;
321    let remote_iov = current_task.read_iovec(remote_iov_addr, remote_iov_count)?;
322    log_trace!(
323        "process_vm_readv(tid={}, local_iov={:?}, remote_iov={:?})",
324        tid,
325        local_iov,
326        remote_iov
327    );
328
329    track_stub!(TODO("https://fxbug.dev/322874765"), "process_vm_readv single-copy");
330    // According to the man page, this syscall was added to Linux specifically to
331    // avoid doing two copies like other IPC mechanisms require. We should avoid this too at some
332    // point.
333    let mut output = UserBuffersOutputBuffer::unified_new(current_task, local_iov)?;
334    let remote_mm = remote_task.mm().ok();
335    if current_task.has_same_address_space(remote_mm.as_ref()) {
336        let mut input = UserBuffersInputBuffer::unified_new(current_task, remote_iov)?;
337        output.write_buffer(&mut input)
338    } else {
339        let mut input = UserBuffersInputBuffer::syscall_new(remote_task.deref(), remote_iov)?;
340        output.write_buffer(&mut input)
341    }
342}
343
344pub fn sys_process_vm_writev(
345    current_task: &CurrentTask,
346    tid: tid_t,
347    local_iov_addr: IOVecPtr,
348    local_iov_count: UserValue<i32>,
349    remote_iov_addr: IOVecPtr,
350    remote_iov_count: UserValue<i32>,
351    flags: usize,
352) -> Result<usize, Errno> {
353    if flags != 0 {
354        return error!(EINVAL);
355    }
356
357    // Source and destination are allowed to be of different length. It is valid to use a nullptr if
358    // the associated length is 0. Thus, if either source or destination length is 0 and nullptr,
359    // make sure to return Ok(0) before doing any other validation/operations.
360    if (local_iov_count == 0 && local_iov_addr.is_null())
361        || (remote_iov_count == 0 && remote_iov_addr.is_null())
362    {
363        return Ok(0);
364    }
365
366    let remote_task = current_task.get_task(tid)?;
367
368    current_task.check_ptrace_access_mode(PTRACE_MODE_ATTACH_REALCREDS, &remote_task)?;
369
370    let local_iov = current_task.read_iovec(local_iov_addr, local_iov_count)?;
371    let remote_iov = current_task.read_iovec(remote_iov_addr, remote_iov_count)?;
372    log_trace!(
373        "sys_process_vm_writev(tid={}, local_iov={:?}, remote_iov={:?})",
374        tid,
375        local_iov,
376        remote_iov
377    );
378
379    track_stub!(TODO("https://fxbug.dev/322874339"), "process_vm_writev single-copy");
380    // NB: According to the man page, this syscall was added to Linux specifically to
381    // avoid doing two copies like other IPC mechanisms require. We should avoid this too at some
382    // point.
383    let mut input = UserBuffersInputBuffer::unified_new(current_task, local_iov)?;
384    let remote_mm = remote_task.mm().ok();
385    if current_task.has_same_address_space(remote_mm.as_ref()) {
386        let mut output = UserBuffersOutputBuffer::unified_new(current_task, remote_iov)?;
387        output.write_buffer(&mut input)
388    } else {
389        let mut output = UserBuffersOutputBuffer::syscall_new(remote_task.deref(), remote_iov)?;
390        output.write_buffer(&mut input)
391    }
392}
393
394pub fn sys_process_mrelease(
395    current_task: &CurrentTask,
396    pidfd: FdNumber,
397    flags: u32,
398) -> Result<(), Errno> {
399    if flags != 0 {
400        return error!(EINVAL);
401    }
402    let file = current_task.get_file(pidfd)?;
403    // From <https://man7.org/linux/man-pages/man2/process_mrelease.2.html>:
404    //
405    //   The pidfd argument is a PID file descriptor (see pidfd_open(2)) that
406    //   refers to the process whose memory is to be released.
407    //   ...
408    //   EBADF  pidfd is not a valid PID file descriptor.
409    //
410    // Unlike `pidfd_send_signal(2)`, `process_mrelease(2)` only accepts PID file
411    // descriptors and rejects `/proc/<pid>` directory file descriptors.
412    let pidfd_file = file.downcast_file::<PidFdFileObject>().ok_or_else(|| errno!(EBADF))?;
413    let pid = pidfd_file.pid();
414    let Some(ProcessEntryRef::Process(thread_group)) = pid.get_process() else {
415        return error!(ESRCH);
416    };
417
418    let mm = {
419        let thread_group_state = thread_group.read();
420        // From <https://man7.org/linux/man-pages/man2/process_mrelease.2.html>:
421        //
422        //   EINVAL The memory of the task cannot be released because the
423        //          process is not exiting, or by other reasons.
424        if thread_group_state.is_running() {
425            return error!(EINVAL);
426        }
427
428        // Find any remaining task in the thread group that still references the shared
429        // `MemoryManager`. Once all tasks in the thread group have cleared their `mm` in
430        // `CurrentTask::exit` (including zombie processes), `process_mrelease(2)` fails with
431        // `ESRCH`.
432        thread_group_state
433            .tasks()
434            .into_iter()
435            .find_map(|task| task.mm().ok())
436            .ok_or_else(|| errno!(ESRCH))?
437    };
438    mm.mrelease()
439}
440
441pub fn sys_membarrier(
442    current_task: &CurrentTask,
443    cmd: uapi::membarrier_cmd,
444    _flags: u32,
445    _cpu_id: i32,
446) -> Result<u32, Errno> {
447    match cmd {
448        // This command returns a bit mask of all supported commands.
449        // We support everything except for the RSEQ family.
450        uapi::membarrier_cmd_MEMBARRIER_CMD_QUERY => Ok(uapi::membarrier_cmd_MEMBARRIER_CMD_GLOBAL
451            | uapi::membarrier_cmd_MEMBARRIER_CMD_GLOBAL_EXPEDITED
452            | uapi::membarrier_cmd_MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED
453            | uapi::membarrier_cmd_MEMBARRIER_CMD_PRIVATE_EXPEDITED
454            | uapi::membarrier_cmd_MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED
455            | uapi::membarrier_cmd_MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE
456            | uapi::membarrier_cmd_MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE),
457        // Global and global expedited barriers are treated identically. We don't track
458        // registration for global expedited barriers currently.
459        uapi::membarrier_cmd_MEMBARRIER_CMD_GLOBAL
460        | uapi::membarrier_cmd_MEMBARRIER_CMD_GLOBAL_EXPEDITED => {
461            system_barrier(BarrierType::DataMemory);
462            Ok(0)
463        }
464        // Global registration commands are ignored.
465        uapi::membarrier_cmd_MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED => Ok(0),
466        uapi::membarrier_cmd_MEMBARRIER_CMD_PRIVATE_EXPEDITED => {
467            // A private expedited barrier is only issued if the address space is registered
468            // for these barriers.
469            if current_task.mm()?.membarrier_private_expedited_registered(MembarrierType::Memory) {
470                // If a barrier is requested, issue a global barrier.
471                system_barrier(BarrierType::DataMemory);
472                Ok(0)
473            } else {
474                error!(EPERM)
475            }
476        }
477        // Private sync core barriers are treated as global instruction stream barriers.
478        uapi::membarrier_cmd_MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE => {
479            if current_task.mm()?.membarrier_private_expedited_registered(MembarrierType::SyncCore)
480            {
481                system_barrier(BarrierType::InstructionStream);
482                Ok(0)
483            } else {
484                error!(EPERM)
485            }
486        }
487        uapi::membarrier_cmd_MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED => {
488            let _ =
489                current_task.mm()?.register_membarrier_private_expedited(MembarrierType::Memory)?;
490            Ok(0)
491        }
492
493        uapi::membarrier_cmd_MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE => {
494            let _ = current_task
495                .mm()?
496                .register_membarrier_private_expedited(MembarrierType::SyncCore)?;
497            Ok(0)
498        }
499        uapi::membarrier_cmd_MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ => {
500            track_stub!(TODO("https://fxbug.dev/447158570"), "membarrier rseq");
501            error!(ENOSYS)
502        }
503        uapi::membarrier_cmd_MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ => {
504            track_stub!(TODO("https://fxbug.dev/447158570"), "membarrier rseq");
505            error!(ENOSYS)
506        }
507        _ => error!(EINVAL),
508    }
509}
510
511pub fn sys_futex(
512    current_task: &mut CurrentTask,
513    addr: UserAddress,
514    op: u32,
515    value: u32,
516    timeout_or_value2: SyscallArg,
517    addr2: UserAddress,
518    value3: u32,
519) -> Result<usize, Errno> {
520    if op & FUTEX_PRIVATE_FLAG != 0 {
521        do_futex::<PrivateFutexKey>(current_task, addr, op, value, timeout_or_value2, addr2, value3)
522    } else {
523        do_futex::<SharedFutexKey>(current_task, addr, op, value, timeout_or_value2, addr2, value3)
524    }
525}
526
527fn do_futex<Key: FutexKey>(
528    current_task: &mut CurrentTask,
529    addr: UserAddress,
530    op: u32,
531    value: u32,
532    timeout_or_value2: SyscallArg,
533    addr2: UserAddress,
534    value3: u32,
535) -> Result<usize, Errno> {
536    let futexes = &current_task.kernel().shared_futexes;
537    let cmd = op & (FUTEX_CMD_MASK as u32);
538
539    let is_realtime = match (cmd, op & FUTEX_CLOCK_REALTIME != 0) {
540        // This option bit can be employed only with the FUTEX_WAIT_BITSET, FUTEX_WAIT_REQUEUE_PI,
541        // (since Linux 4.5) FUTEX_WAIT, and (since Linux 5.14) FUTEX_LOCK_PI2 operations.
542        (FUTEX_WAIT_BITSET | FUTEX_WAIT_REQUEUE_PI | FUTEX_WAIT | FUTEX_LOCK_PI2, true) => true,
543        (_, true) => return error!(EINVAL),
544
545        // FUTEX_LOCK_PI always uses realtime.
546        (FUTEX_LOCK_PI, _) => true,
547
548        (_, false) => false,
549    };
550
551    // The timeout is interpreted differently by WAIT and WAIT_BITSET: WAIT takes a
552    // timeout and WAIT_BITSET takes a deadline.
553    let utime = TimeSpecPtr::new(current_task, timeout_or_value2);
554    let read_timespec = |current_task: &CurrentTask| {
555        if utime.is_null() {
556            Ok(None)
557        } else {
558            Ok(Some(current_task.read_multi_arch_object(utime)?))
559        }
560    };
561    let read_timeout = |current_task: &CurrentTask| -> Result<Option<zx::MonotonicInstant>, Errno> {
562        let Some(timespec) = read_timespec(current_task)? else {
563            return Ok(None);
564        };
565        let timeout = duration_from_timespec(timespec);
566        let deadline = zx::MonotonicInstant::after(timeout?);
567        if is_realtime {
568            // Since this is a timeout, waiting on the monotonic timeline before it's paused is
569            // just as good as actually estimating UTC here.
570            track_stub!(TODO("https://fxbug.dev/356912301"), "FUTEX_CLOCK_REALTIME timeout");
571        }
572        Ok(Some(deadline))
573    };
574    let read_deadline = |current_task: &CurrentTask| -> Result<Option<TargetTime>, Errno> {
575        let Some(timespec) = read_timespec(current_task)? else {
576            return Ok(None);
577        };
578        if is_realtime {
579            Ok(Some(TargetTime::RealTime(time_from_timespec::<UtcTimeline>(timespec)?)))
580        } else {
581            Ok(Some(TargetTime::Monotonic(time_from_timespec::<zx::MonotonicTimeline>(timespec)?)))
582        }
583    };
584
585    match cmd {
586        FUTEX_WAIT => {
587            let deadline = read_timeout(current_task)?.map(TargetTime::Monotonic);
588            let bitset = FUTEX_BITSET_MATCH_ANY;
589            do_futex_wait_with_restart::<Key>(current_task, addr, value, bitset, deadline)?;
590            Ok(0)
591        }
592        FUTEX_WAKE => {
593            futexes.wake::<Key>(current_task, addr, value as usize, FUTEX_BITSET_MATCH_ANY)
594        }
595        FUTEX_WAKE_OP => {
596            track_stub!(TODO("https://fxbug.dev/361181940"), "FUTEX_WAKE_OP");
597            error!(ENOSYS)
598        }
599        FUTEX_WAIT_BITSET => {
600            if value3 == 0 {
601                return error!(EINVAL);
602            }
603            let deadline = read_deadline(current_task)?;
604            do_futex_wait_with_restart::<Key>(current_task, addr, value, value3, deadline)?;
605            Ok(0)
606        }
607        FUTEX_WAKE_BITSET => {
608            if value3 == 0 {
609                return error!(EINVAL);
610            }
611            futexes.wake::<Key>(current_task, addr, value as usize, value3)
612        }
613        FUTEX_REQUEUE | FUTEX_CMP_REQUEUE => {
614            let wake_count = value as usize;
615            let requeue_count: usize = timeout_or_value2.into();
616            if wake_count > i32::MAX as usize || requeue_count > i32::MAX as usize {
617                return error!(EINVAL);
618            }
619            let expected_value = if cmd == FUTEX_CMP_REQUEUE { Some(value3) } else { None };
620            futexes.requeue::<Key>(
621                current_task,
622                addr,
623                wake_count,
624                requeue_count,
625                addr2,
626                expected_value,
627            )
628        }
629        FUTEX_WAIT_REQUEUE_PI => {
630            track_stub!(TODO("https://fxbug.dev/361181558"), "FUTEX_WAIT_REQUEUE_PI");
631            error!(ENOSYS)
632        }
633        FUTEX_CMP_REQUEUE_PI => {
634            track_stub!(TODO("https://fxbug.dev/361181773"), "FUTEX_CMP_REQUEUE_PI");
635            error!(ENOSYS)
636        }
637        FUTEX_LOCK_PI | FUTEX_LOCK_PI2 => {
638            let deadline = read_timeout(current_task)?.unwrap_or(zx::MonotonicInstant::INFINITE);
639            futexes.lock_pi::<Key>(current_task, addr, deadline)?;
640            Ok(0)
641        }
642        FUTEX_TRYLOCK_PI => {
643            track_stub!(TODO("https://fxbug.dev/361175318"), "FUTEX_TRYLOCK_PI");
644            error!(ENOSYS)
645        }
646        FUTEX_UNLOCK_PI => {
647            futexes.unlock_pi::<Key>(current_task, addr)?;
648            Ok(0)
649        }
650        _ => {
651            track_stub!(TODO("https://fxbug.dev/322875124"), "futex unknown command", cmd);
652            error!(ENOSYS)
653        }
654    }
655}
656
657fn do_futex_wait_with_restart<Key: FutexKey>(
658    current_task: &mut CurrentTask,
659    addr: UserAddress,
660    value: u32,
661    mask: u32,
662    deadline: Option<TargetTime>,
663) -> Result<(), Errno> {
664    let futexes = &current_task.kernel().shared_futexes;
665    let result = match deadline {
666        None => {
667            futexes.wait::<Key>(current_task, addr, value, mask, zx::MonotonicInstant::INFINITE)
668        }
669        Some(TargetTime::Monotonic(mono_deadline)) => {
670            futexes.wait::<Key>(current_task, addr, value, mask, mono_deadline)
671        }
672        Some(TargetTime::BootInstant(boot_deadline)) => {
673            let timer_slack = current_task.read().get_timerslack();
674            futexes.wait_boot::<Key>(current_task, addr, value, mask, boot_deadline, timer_slack)
675        }
676        Some(TargetTime::RealTime(utc_deadline)) => {
677            // Convert real time deadlines to boot time deadlines since waiting using a UTC deadline is unsupported.
678            let (boot_deadline, _) = estimate_boot_deadline_from_utc(utc_deadline);
679            let timer_slack = current_task.read().get_timerslack();
680            futexes.wait_boot::<Key>(current_task, addr, value, mask, boot_deadline, timer_slack)
681        }
682    };
683    match result {
684        Err(err) if err == EINTR => {
685            if let Some(deadline) = deadline {
686                current_task.set_syscall_restart_func(move |current_task| {
687                    do_futex_wait_with_restart::<Key>(
688                        current_task,
689                        addr,
690                        value,
691                        mask,
692                        Some(deadline),
693                    )
694                });
695                error!(ERESTART_RESTARTBLOCK)
696            } else {
697                error!(ERESTARTSYS)
698            }
699        }
700        result => result,
701    }
702}
703
704pub fn sys_get_robust_list(
705    current_task: &CurrentTask,
706    tid: tid_t,
707    user_head_ptr: UserRef<UserAddress>,
708    user_len_ptr: UserRef<usize>,
709) -> Result<(), Errno> {
710    if tid < 0 {
711        return error!(EINVAL);
712    }
713    if user_head_ptr.is_null() || user_len_ptr.is_null() {
714        return error!(EFAULT);
715    }
716    let task = if tid == 0 {
717        current_task.task.clone()
718    } else {
719        let task = current_task.get_task(tid)?;
720        current_task.check_ptrace_access_mode(PTRACE_MODE_READ_REALCREDS, &task)?;
721        task
722    };
723    current_task.write_object(user_head_ptr, &task.read().robust_list_head.addr())?;
724    current_task.write_object(user_len_ptr, &std::mem::size_of::<robust_list_head>())?;
725    Ok(())
726}
727
728pub fn sys_set_robust_list(
729    current_task: &CurrentTask,
730    user_head: UserRef<robust_list_head>,
731    len: usize,
732) -> Result<(), Errno> {
733    if len != std::mem::size_of::<robust_list_head>() {
734        return error!(EINVAL);
735    }
736    current_task.write().robust_list_head = user_head.into();
737    Ok(())
738}
739
740pub fn sys_mlock(
741    current_task: &CurrentTask,
742    addr: UserAddress,
743    length: usize,
744) -> Result<(), Errno> {
745    // If flags is 0, mlock2() behaves exactly the same as mlock().
746    sys_mlock2(current_task, addr, length, 0)
747}
748
749pub fn sys_mlock2(
750    current_task: &CurrentTask,
751    addr: UserAddress,
752    length: usize,
753    flags: u64,
754) -> Result<(), Errno> {
755    const KNOWN_FLAGS: u64 = MLOCK_ONFAULT as u64;
756    if (flags & !KNOWN_FLAGS) != 0 {
757        return error!(EINVAL);
758    }
759    let on_fault = flags & MLOCK_ONFAULT as u64 != 0;
760    current_task.mm()?.mlock(current_task, addr, length, on_fault)
761}
762
763pub fn sys_munlock(
764    current_task: &CurrentTask,
765    addr: UserAddress,
766    length: usize,
767) -> Result<(), Errno> {
768    current_task.mm()?.munlock(current_task, addr, length)
769}
770
771pub fn sys_mlockall(_current_task: &CurrentTask, _flags: u64) -> Result<(), Errno> {
772    track_stub!(TODO("https://fxbug.dev/297292097"), "mlockall()");
773    error!(ENOSYS)
774}
775
776pub fn sys_munlockall(_current_task: &CurrentTask, _flags: u64) -> Result<(), Errno> {
777    track_stub!(TODO("https://fxbug.dev/297292097"), "munlockall()");
778    error!(ENOSYS)
779}
780
781pub fn sys_mincore(
782    _current_task: &CurrentTask,
783    _addr: UserAddress,
784    _length: usize,
785    _out: UserRef<u8>,
786) -> Result<(), Errno> {
787    track_stub!(TODO("https://fxbug.dev/297372240"), "mincore()");
788    error!(ENOSYS)
789}
790
791// Syscalls for arch32 usage
792#[cfg(target_arch = "aarch64")]
793mod arch32 {
794    use crate::mm::PAGE_SIZE;
795    use crate::mm::memory_accessor::MemoryAccessorExt;
796    use crate::mm::syscalls::{UserAddress, sys_mmap};
797    use crate::task::{CurrentTask, RobustListHeadPtr};
798    use crate::vfs::FdNumber;
799    use starnix_uapi::auth::PTRACE_MODE_READ_REALCREDS;
800    use starnix_uapi::errors::Errno;
801    use starnix_uapi::user_address::UserRef;
802    use starnix_uapi::{error, uapi};
803
804    pub fn sys_arch32_set_robust_list(
805        current_task: &CurrentTask,
806        user_head: UserRef<uapi::arch32::robust_list_head>,
807        len: usize,
808    ) -> Result<(), Errno> {
809        if len != std::mem::size_of::<uapi::arch32::robust_list_head>() {
810            return error!(EINVAL);
811        }
812        current_task.write().robust_list_head = RobustListHeadPtr::from_32(user_head);
813        Ok(())
814    }
815
816    pub fn sys_arch32_get_robust_list(
817        current_task: &CurrentTask,
818        tid: starnix_uapi::tid_t,
819        user_head_ptr: UserRef<u32>,
820        user_len_ptr: UserRef<u32>,
821    ) -> Result<(), Errno> {
822        if tid < 0 {
823            return error!(EINVAL);
824        }
825        if user_head_ptr.is_null() || user_len_ptr.is_null() {
826            return error!(EFAULT);
827        }
828        let task = if tid == 0 {
829            current_task.task.clone()
830        } else {
831            let task = current_task.get_task(tid)?;
832            current_task.check_ptrace_access_mode(PTRACE_MODE_READ_REALCREDS, &task)?;
833            task
834        };
835
836        let addr = task.read().robust_list_head.addr().ptr() as u32;
837        current_task.write_object(user_head_ptr, &addr)?;
838        current_task.write_object(
839            user_len_ptr,
840            &(std::mem::size_of::<uapi::arch32::robust_list_head>() as u32),
841        )?;
842        Ok(())
843    }
844
845    pub fn sys_arch32_mmap2(
846        current_task: &mut CurrentTask,
847        addr: UserAddress,
848        length: usize,
849        prot: u32,
850        flags: u32,
851        fd: FdNumber,
852        offset: u64,
853    ) -> Result<UserAddress, Errno> {
854        sys_mmap(current_task, addr, length, prot, flags, fd, offset * *PAGE_SIZE)
855    }
856
857    pub fn sys_arch32_munmap(
858        current_task: &CurrentTask,
859        addr: UserAddress,
860        length: usize,
861    ) -> Result<(), Errno> {
862        if !addr.is_lower_32bit() || length >= (1 << 32) {
863            return error!(EINVAL);
864        }
865        current_task.mm()?.unmap(addr, length)?;
866        Ok(())
867    }
868
869    pub use super::{
870        sys_futex as sys_arch32_futex, sys_madvise as sys_arch32_madvise,
871        sys_membarrier as sys_arch32_membarrier, sys_mincore as sys_arch32_mincore,
872        sys_mlock as sys_arch32_mlock, sys_mlock2 as sys_arch32_mlock2,
873        sys_mlockall as sys_arch32_mlockall, sys_mremap as sys_arch32_mremap,
874        sys_msync as sys_arch32_msync, sys_munlock as sys_arch32_munlock,
875        sys_munlockall as sys_arch32_munlockall,
876        sys_process_mrelease as sys_arch32_process_mrelease,
877        sys_process_vm_readv as sys_arch32_process_vm_readv,
878    };
879}
880
881#[cfg(target_arch = "aarch64")]
882pub use arch32::*;
883
884#[cfg(test)]
885mod tests {
886    use super::*;
887    use crate::mm::memory::MemoryObject;
888    use crate::testing::*;
889    use starnix_uapi::errors::EEXIST;
890    use starnix_uapi::{MREMAP_FIXED, MREMAP_MAYMOVE, PROT_READ};
891
892    #[::fuchsia::test]
893    async fn test_mmap_with_colliding_hint() {
894        spawn_kernel_and_run(async |current_task| {
895            let page_size = *PAGE_SIZE;
896
897            let mapped_address = map_memory(&current_task, UserAddress::default(), page_size);
898            match do_mmap(
899                &current_task,
900                mapped_address,
901                page_size as usize,
902                PROT_READ,
903                MAP_PRIVATE | MAP_ANONYMOUS,
904                FdNumber::from_raw(-1),
905                0,
906            ) {
907                Ok(address) => {
908                    assert_ne!(address, mapped_address);
909                }
910                error => {
911                    panic!("mmap with colliding hint failed: {error:?}");
912                }
913            }
914        })
915        .await;
916    }
917
918    #[::fuchsia::test]
919    async fn test_mmap_with_fixed_collision() {
920        spawn_kernel_and_run(async |current_task| {
921            let page_size = *PAGE_SIZE;
922
923            let mapped_address = map_memory(&current_task, UserAddress::default(), page_size);
924            match do_mmap(
925                &current_task,
926                mapped_address,
927                page_size as usize,
928                PROT_READ,
929                MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
930                FdNumber::from_raw(-1),
931                0,
932            ) {
933                Ok(address) => {
934                    assert_eq!(address, mapped_address);
935                }
936                error => {
937                    panic!("mmap with fixed collision failed: {error:?}");
938                }
939            }
940        })
941        .await;
942    }
943
944    #[::fuchsia::test]
945    async fn test_mmap_with_fixed_noreplace_collision() {
946        spawn_kernel_and_run(async |current_task| {
947            let page_size = *PAGE_SIZE;
948
949            let mapped_address = map_memory(&current_task, UserAddress::default(), page_size);
950            match do_mmap(
951                &current_task,
952                mapped_address,
953                page_size as usize,
954                PROT_READ,
955                MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
956                FdNumber::from_raw(-1),
957                0,
958            ) {
959                Err(errno) => {
960                    assert_eq!(errno, EEXIST);
961                }
962                result => {
963                    panic!("mmap with fixed_noreplace collision failed: {result:?}");
964                }
965            }
966        })
967        .await;
968    }
969
970    /// It is ok to call munmap with an address that is a multiple of the page size, and
971    /// a non-zero length.
972    #[::fuchsia::test]
973    async fn test_munmap() {
974        spawn_kernel_and_run(async |current_task| {
975            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
976            assert_eq!(sys_munmap(&current_task, mapped_address, *PAGE_SIZE as usize), Ok(()));
977
978            // Verify that the memory is no longer readable.
979            assert_eq!(current_task.read_memory_to_array::<5>(mapped_address), error!(EFAULT));
980        })
981        .await;
982    }
983
984    /// It is ok to call munmap on an unmapped range.
985    #[::fuchsia::test]
986    async fn test_munmap_not_mapped() {
987        spawn_kernel_and_run(async |current_task| {
988            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
989            assert_eq!(sys_munmap(&current_task, mapped_address, *PAGE_SIZE as usize), Ok(()));
990            assert_eq!(sys_munmap(&current_task, mapped_address, *PAGE_SIZE as usize), Ok(()));
991        })
992        .await;
993    }
994
995    /// It is an error to call munmap with a length of 0.
996    #[::fuchsia::test]
997    async fn test_munmap_0_length() {
998        spawn_kernel_and_run(async |current_task| {
999            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
1000            assert_eq!(sys_munmap(&current_task, mapped_address, 0), error!(EINVAL));
1001        })
1002        .await;
1003    }
1004
1005    /// It is an error to call munmap with an address that is not a multiple of the page size.
1006    #[::fuchsia::test]
1007    async fn test_munmap_not_aligned() {
1008        spawn_kernel_and_run(async |current_task| {
1009            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
1010            assert_eq!(
1011                sys_munmap(&current_task, (mapped_address + 1u64).unwrap(), *PAGE_SIZE as usize),
1012                error!(EINVAL)
1013            );
1014
1015            // Verify that the memory is still readable.
1016            assert!(current_task.read_memory_to_array::<5>(mapped_address).is_ok());
1017        })
1018        .await;
1019    }
1020
1021    /// The entire page should be unmapped, not just the range [address, address + length).
1022    #[::fuchsia::test]
1023    async fn test_munmap_unmap_partial() {
1024        spawn_kernel_and_run(async |current_task| {
1025            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
1026            assert_eq!(
1027                sys_munmap(&current_task, mapped_address, (*PAGE_SIZE as usize) / 2),
1028                Ok(())
1029            );
1030
1031            // Verify that memory can't be read in either half of the page.
1032            assert_eq!(current_task.read_memory_to_array::<5>(mapped_address), error!(EFAULT));
1033            assert_eq!(
1034                current_task
1035                    .read_memory_to_array::<5>((mapped_address + (*PAGE_SIZE - 2)).unwrap()),
1036                error!(EFAULT)
1037            );
1038        })
1039        .await;
1040    }
1041
1042    /// All pages that intersect the munmap range should be unmapped.
1043    #[::fuchsia::test]
1044    async fn test_munmap_multiple_pages() {
1045        spawn_kernel_and_run(async |current_task| {
1046            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 2);
1047            assert_eq!(
1048                sys_munmap(&current_task, mapped_address, (*PAGE_SIZE as usize) + 1),
1049                Ok(())
1050            );
1051
1052            // Verify that neither page is readable.
1053            assert_eq!(current_task.read_memory_to_array::<5>(mapped_address), error!(EFAULT));
1054            assert_eq!(
1055                current_task
1056                    .read_memory_to_array::<5>((mapped_address + (*PAGE_SIZE + 1u64)).unwrap()),
1057                error!(EFAULT)
1058            );
1059        })
1060        .await;
1061    }
1062
1063    /// Only the pages that intersect the munmap range should be unmapped.
1064    #[::fuchsia::test]
1065    async fn test_munmap_one_of_many_pages() {
1066        spawn_kernel_and_run(async |current_task| {
1067            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 2);
1068            assert_eq!(
1069                sys_munmap(&current_task, mapped_address, (*PAGE_SIZE as usize) - 1),
1070                Ok(())
1071            );
1072
1073            // Verify that the second page is still readable.
1074            assert_eq!(current_task.read_memory_to_array::<5>(mapped_address), error!(EFAULT));
1075            assert!(
1076                current_task
1077                    .read_memory_to_array::<5>((mapped_address + (*PAGE_SIZE + 1u64)).unwrap())
1078                    .is_ok()
1079            );
1080        })
1081        .await;
1082    }
1083
1084    /// Unmap the middle page of a mapping.
1085    #[::fuchsia::test]
1086    async fn test_munmap_middle_page() {
1087        spawn_kernel_and_run(async |current_task| {
1088            let mapped_address = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 3);
1089            assert_eq!(
1090                sys_munmap(
1091                    &current_task,
1092                    (mapped_address + *PAGE_SIZE).unwrap(),
1093                    *PAGE_SIZE as usize
1094                ),
1095                Ok(())
1096            );
1097
1098            // Verify that the first and third pages are still readable.
1099            assert!(current_task.read_memory_to_vec(mapped_address, 5).is_ok());
1100            assert_eq!(
1101                current_task.read_memory_to_vec((mapped_address + *PAGE_SIZE).unwrap(), 5),
1102                error!(EFAULT)
1103            );
1104            assert!(
1105                current_task
1106                    .read_memory_to_vec((mapped_address + (*PAGE_SIZE * 2)).unwrap(), 5)
1107                    .is_ok()
1108            );
1109        })
1110        .await;
1111    }
1112
1113    /// Unmap a range of pages that includes disjoint mappings.
1114    #[::fuchsia::test]
1115    async fn test_munmap_many_mappings() {
1116        spawn_kernel_and_run(async |current_task| {
1117            let mapped_addresses: Vec<_> = std::iter::repeat_with(|| {
1118                map_memory(&current_task, UserAddress::default(), *PAGE_SIZE)
1119            })
1120            .take(3)
1121            .collect();
1122            let min_address = *mapped_addresses.iter().min().unwrap();
1123            let max_address = *mapped_addresses.iter().max().unwrap();
1124            let unmap_length = (max_address - min_address) + *PAGE_SIZE as usize;
1125
1126            assert_eq!(sys_munmap(&current_task, min_address, unmap_length), Ok(()));
1127
1128            // Verify that none of the mapped pages are readable.
1129            for mapped_address in mapped_addresses {
1130                assert_eq!(current_task.read_memory_to_vec(mapped_address, 5), error!(EFAULT));
1131            }
1132        })
1133        .await;
1134    }
1135
1136    #[::fuchsia::test]
1137    async fn test_msync_validates_address_range() {
1138        spawn_kernel_and_run(async |current_task| {
1139            // Map 3 pages and test that ranges covering these pages return no error.
1140            let addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 3);
1141            assert_eq!(sys_msync(&current_task, addr, *PAGE_SIZE as usize * 3, 0), Ok(()));
1142            assert_eq!(sys_msync(&current_task, addr, *PAGE_SIZE as usize * 2, 0), Ok(()));
1143            assert_eq!(
1144                sys_msync(&current_task, (addr + *PAGE_SIZE).unwrap(), *PAGE_SIZE as usize * 2, 0),
1145                Ok(())
1146            );
1147
1148            // Unmap the middle page and test that ranges covering that page return ENOMEM.
1149            sys_munmap(&current_task, (addr + *PAGE_SIZE).unwrap(), *PAGE_SIZE as usize)
1150                .expect("unmap middle");
1151            assert_eq!(sys_msync(&current_task, addr, *PAGE_SIZE as usize, 0), Ok(()));
1152            assert_eq!(
1153                sys_msync(&current_task, addr, *PAGE_SIZE as usize * 3, starnix_uapi::MS_SYNC),
1154                error!(ENOMEM)
1155            );
1156            assert_eq!(
1157                sys_msync(&current_task, addr, *PAGE_SIZE as usize * 2, starnix_uapi::MS_SYNC),
1158                error!(ENOMEM)
1159            );
1160            assert_eq!(
1161                sys_msync(
1162                    &current_task,
1163                    (addr + *PAGE_SIZE).unwrap(),
1164                    *PAGE_SIZE as usize * 2,
1165                    starnix_uapi::MS_SYNC
1166                ),
1167                error!(ENOMEM)
1168            );
1169            assert_eq!(
1170                sys_msync(
1171                    &current_task,
1172                    (addr + (*PAGE_SIZE * 2)).unwrap(),
1173                    *PAGE_SIZE as usize,
1174                    0
1175                ),
1176                Ok(())
1177            );
1178
1179            // Map the middle page back and test that ranges covering the three pages
1180            // (spanning multiple ranges) return no error.
1181            assert_eq!(
1182                map_memory(&current_task, (addr + *PAGE_SIZE).unwrap(), *PAGE_SIZE),
1183                (addr + *PAGE_SIZE).unwrap()
1184            );
1185            assert_eq!(sys_msync(&current_task, addr, *PAGE_SIZE as usize * 3, 0), Ok(()));
1186            assert_eq!(sys_msync(&current_task, addr, *PAGE_SIZE as usize * 2, 0), Ok(()));
1187            assert_eq!(
1188                sys_msync(&current_task, (addr + *PAGE_SIZE).unwrap(), *PAGE_SIZE as usize * 2, 0),
1189                Ok(())
1190            );
1191        })
1192        .await;
1193    }
1194
1195    /// Shrinks an entire range.
1196    #[::fuchsia::test]
1197    async fn test_mremap_shrink_whole_range_from_end() {
1198        spawn_kernel_and_run(async |current_task| {
1199            // Map 2 pages.
1200            let addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 2);
1201            fill_page(&current_task, addr, 'a');
1202            fill_page(&current_task, (addr + *PAGE_SIZE).unwrap(), 'b');
1203
1204            // Shrink the mapping from 2 to 1 pages.
1205            assert_eq!(
1206                remap_memory(
1207                    &current_task,
1208                    addr,
1209                    *PAGE_SIZE * 2,
1210                    *PAGE_SIZE,
1211                    0,
1212                    UserAddress::default()
1213                ),
1214                Ok(addr)
1215            );
1216
1217            check_page_eq(&current_task, addr, 'a');
1218            check_unmapped(&current_task, (addr + *PAGE_SIZE).unwrap());
1219        })
1220        .await;
1221    }
1222
1223    /// Shrinks part of a range, introducing a hole in the middle.
1224    #[::fuchsia::test]
1225    async fn test_mremap_shrink_partial_range() {
1226        spawn_kernel_and_run(async |current_task| {
1227            // Map 3 pages.
1228            let addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 3);
1229            fill_page(&current_task, addr, 'a');
1230            fill_page(&current_task, (addr + *PAGE_SIZE).unwrap(), 'b');
1231            fill_page(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1232
1233            // Shrink the first 2 pages down to 1, creating a hole.
1234            assert_eq!(
1235                remap_memory(
1236                    &current_task,
1237                    addr,
1238                    *PAGE_SIZE * 2,
1239                    *PAGE_SIZE,
1240                    0,
1241                    UserAddress::default()
1242                ),
1243                Ok(addr)
1244            );
1245
1246            check_page_eq(&current_task, addr, 'a');
1247            check_unmapped(&current_task, (addr + *PAGE_SIZE).unwrap());
1248            check_page_eq(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1249        })
1250        .await;
1251    }
1252
1253    /// Shrinking doesn't care if the range specified spans multiple mappings.
1254    #[::fuchsia::test]
1255    async fn test_mremap_shrink_across_ranges() {
1256        spawn_kernel_and_run(async |current_task| {
1257            // Map 3 pages, unmap the middle, then map the middle again. This will leave us with
1258            // 3 contiguous mappings.
1259            let addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 3);
1260            assert_eq!(
1261                sys_munmap(&current_task, (addr + *PAGE_SIZE).unwrap(), *PAGE_SIZE as usize),
1262                Ok(())
1263            );
1264            assert_eq!(
1265                map_memory(&current_task, (addr + *PAGE_SIZE).unwrap(), *PAGE_SIZE),
1266                (addr + *PAGE_SIZE).unwrap()
1267            );
1268
1269            fill_page(&current_task, addr, 'a');
1270            fill_page(&current_task, (addr + *PAGE_SIZE).unwrap(), 'b');
1271            fill_page(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1272
1273            // Remap over all three mappings, shrinking to 1 page.
1274            assert_eq!(
1275                remap_memory(
1276                    &current_task,
1277                    addr,
1278                    *PAGE_SIZE * 3,
1279                    *PAGE_SIZE,
1280                    0,
1281                    UserAddress::default()
1282                ),
1283                Ok(addr)
1284            );
1285
1286            check_page_eq(&current_task, addr, 'a');
1287            check_unmapped(&current_task, (addr + *PAGE_SIZE).unwrap());
1288            check_unmapped(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap());
1289        })
1290        .await;
1291    }
1292
1293    /// Grows a mapping in-place.
1294    #[::fuchsia::test]
1295    async fn test_mremap_grow_in_place() {
1296        spawn_kernel_and_run(async |current_task| {
1297            // Map 3 pages, unmap the middle, leaving a hole.
1298            let addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 3);
1299            fill_page(&current_task, addr, 'a');
1300            fill_page(&current_task, (addr + *PAGE_SIZE).unwrap(), 'b');
1301            fill_page(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1302            assert_eq!(
1303                sys_munmap(&current_task, (addr + *PAGE_SIZE).unwrap(), *PAGE_SIZE as usize),
1304                Ok(())
1305            );
1306
1307            // Grow the first page in-place into the middle.
1308            assert_eq!(
1309                remap_memory(
1310                    &current_task,
1311                    addr,
1312                    *PAGE_SIZE,
1313                    *PAGE_SIZE * 2,
1314                    0,
1315                    UserAddress::default()
1316                ),
1317                Ok(addr)
1318            );
1319
1320            check_page_eq(&current_task, addr, 'a');
1321
1322            // The middle page should be new, and not just pointing to the original middle page filled
1323            // with 'b'.
1324            check_page_ne(&current_task, (addr + *PAGE_SIZE).unwrap(), 'b');
1325
1326            check_page_eq(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1327        })
1328        .await;
1329    }
1330
1331    /// Tries to grow a set of pages that cannot fit, and forces a move.
1332    #[::fuchsia::test]
1333    async fn test_mremap_grow_maymove() {
1334        spawn_kernel_and_run(async |current_task| {
1335            // Map 3 pages.
1336            let addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 3);
1337            fill_page(&current_task, addr, 'a');
1338            fill_page(&current_task, (addr + *PAGE_SIZE).unwrap(), 'b');
1339            fill_page(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1340
1341            // Grow the first two pages by 1, forcing a move.
1342            let new_addr = remap_memory(
1343                &current_task,
1344                addr,
1345                *PAGE_SIZE * 2,
1346                *PAGE_SIZE * 3,
1347                MREMAP_MAYMOVE,
1348                UserAddress::default(),
1349            )
1350            .expect("failed to mremap");
1351
1352            assert_ne!(new_addr, addr, "mremap did not move the mapping");
1353
1354            // The first two pages should have been moved.
1355            check_unmapped(&current_task, addr);
1356            check_unmapped(&current_task, (addr + *PAGE_SIZE).unwrap());
1357
1358            // The third page should still be present.
1359            check_page_eq(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1360
1361            // The moved pages should have the same contents.
1362            check_page_eq(&current_task, new_addr, 'a');
1363            check_page_eq(&current_task, (new_addr + *PAGE_SIZE).unwrap(), 'b');
1364
1365            // The newly grown page should not be the same as the original third page.
1366            check_page_ne(&current_task, (new_addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1367        })
1368        .await;
1369    }
1370
1371    /// Shrinks a set of pages and move them to a fixed location.
1372    #[::fuchsia::test]
1373    async fn test_mremap_shrink_fixed() {
1374        spawn_kernel_and_run(async |current_task| {
1375            // Map 2 pages which will act as the destination.
1376            let dst_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 2);
1377            fill_page(&current_task, dst_addr, 'y');
1378            fill_page(&current_task, (dst_addr + *PAGE_SIZE).unwrap(), 'z');
1379
1380            // Map 3 pages.
1381            let addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 3);
1382            fill_page(&current_task, addr, 'a');
1383            fill_page(&current_task, (addr + *PAGE_SIZE).unwrap(), 'b');
1384            fill_page(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1385
1386            // Shrink the first two pages and move them to overwrite the mappings at `dst_addr`.
1387            let new_addr = remap_memory(
1388                &current_task,
1389                addr,
1390                *PAGE_SIZE * 2,
1391                *PAGE_SIZE,
1392                MREMAP_MAYMOVE | MREMAP_FIXED,
1393                dst_addr,
1394            )
1395            .expect("failed to mremap");
1396
1397            assert_eq!(new_addr, dst_addr, "mremap did not move the mapping");
1398
1399            // The first two pages should have been moved.
1400            check_unmapped(&current_task, addr);
1401            check_unmapped(&current_task, (addr + *PAGE_SIZE).unwrap());
1402
1403            // The third page should still be present.
1404            check_page_eq(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1405
1406            // The first moved page should have the same contents.
1407            check_page_eq(&current_task, new_addr, 'a');
1408
1409            // The second page should be part of the original dst mapping.
1410            check_page_eq(&current_task, (new_addr + *PAGE_SIZE).unwrap(), 'z');
1411        })
1412        .await;
1413    }
1414
1415    /// Clobbers the middle of an existing mapping with mremap to a fixed location.
1416    #[::fuchsia::test]
1417    async fn test_mremap_clobber_memory_mapping() {
1418        spawn_kernel_and_run(async |current_task| {
1419            let dst_memory = MemoryObject::from(zx::Vmo::create(2 * *PAGE_SIZE).unwrap());
1420            dst_memory.write(&['x' as u8].repeat(*PAGE_SIZE as usize), 0).unwrap();
1421            dst_memory.write(&['y' as u8].repeat(*PAGE_SIZE as usize), *PAGE_SIZE).unwrap();
1422
1423            let dst_addr = current_task
1424                .mm()
1425                .unwrap()
1426                .map_memory(
1427                    DesiredAddress::Any,
1428                    dst_memory.into(),
1429                    0,
1430                    2 * (*PAGE_SIZE as usize),
1431                    ProtectionFlags::READ,
1432                    MappingOptions::empty(),
1433                    MappingName::None,
1434                )
1435                .unwrap();
1436
1437            // Map 3 pages.
1438            let addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE * 3);
1439            fill_page(&current_task, addr, 'a');
1440            fill_page(&current_task, (addr + *PAGE_SIZE).unwrap(), 'b');
1441            fill_page(&current_task, (addr + (*PAGE_SIZE * 2)).unwrap(), 'c');
1442
1443            // Overwrite the second page of the mapping with the second page of the anonymous mapping.
1444            let remapped_addr = sys_mremap(
1445                &*current_task,
1446                (addr + *PAGE_SIZE).unwrap(),
1447                *PAGE_SIZE as usize,
1448                *PAGE_SIZE as usize,
1449                MREMAP_FIXED | MREMAP_MAYMOVE,
1450                (dst_addr + *PAGE_SIZE).unwrap(),
1451            )
1452            .unwrap();
1453
1454            assert_eq!(remapped_addr, (dst_addr + *PAGE_SIZE).unwrap());
1455
1456            check_page_eq(&current_task, addr, 'a');
1457            check_unmapped(&current_task, (addr + *PAGE_SIZE).unwrap());
1458            check_page_eq(&current_task, (addr + (2 * *PAGE_SIZE)).unwrap(), 'c');
1459
1460            check_page_eq(&current_task, dst_addr, 'x');
1461            check_page_eq(&current_task, (dst_addr + *PAGE_SIZE).unwrap(), 'b');
1462        })
1463        .await;
1464    }
1465
1466    #[cfg(target_arch = "x86_64")]
1467    #[::fuchsia::test]
1468    async fn test_map_32_bit() {
1469        use starnix_uapi::PROT_WRITE;
1470
1471        spawn_kernel_and_run(async |current_task| {
1472            let page_size = *PAGE_SIZE;
1473
1474            for _i in 0..256 {
1475                match do_mmap(
1476                    &current_task,
1477                    UserAddress::from(0),
1478                    page_size as usize,
1479                    PROT_READ | PROT_WRITE,
1480                    MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT,
1481                    FdNumber::from_raw(-1),
1482                    0,
1483                ) {
1484                    Ok(address) => {
1485                        let memory_end = address.ptr() + page_size as usize;
1486                        assert!(memory_end <= 0x80000000);
1487                    }
1488                    error => {
1489                        panic!("mmap with MAP_32BIT failed: {error:?}");
1490                    }
1491                }
1492            }
1493        })
1494        .await;
1495    }
1496}