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