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