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