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