xref: /aosp_15_r20/external/crosvm/swap/tests/page_handler.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Integration tests for [PageHandler]. these are more than unit tests since [PageHandler] rely on
6 //! the userfaultfd(2) kernel feature.
7 
8 #![cfg(all(unix, feature = "enable"))]
9 
10 mod common;
11 
12 use std::array;
13 use std::ops::Range;
14 use std::thread;
15 use std::time;
16 
17 use base::pagesize;
18 use base::test_utils::call_test_with_sudo;
19 use base::MappedRegion;
20 use base::MemoryMappingBuilder;
21 use base::SharedMemory;
22 use common::*;
23 use swap::page_handler::Error;
24 use swap::page_handler::PageHandler;
25 use swap::userfaultfd::register_regions;
26 use swap::userfaultfd::unregister_regions;
27 use swap::worker::Worker;
28 
29 const HUGEPAGE_SIZE: usize = 2 * 1024 * 1024; // 2MB
30 
31 #[test]
create_success()32 fn create_success() {
33     call_test_with_sudo("create_success_impl")
34 }
35 
36 #[ignore = "Only to be called by create_success"]
37 #[test]
create_success_impl()38 fn create_success_impl() {
39     let worker = Worker::new(2, 2);
40     let file = tempfile::tempfile().unwrap();
41     let staging_shmem = SharedMemory::new("test staging memory", 6 * pagesize() as u64).unwrap();
42     let shm = create_shared_memory("shm", 6 * pagesize());
43     let base_addr = shm.base_addr();
44 
45     let result = PageHandler::create(
46         &file,
47         &staging_shmem,
48         &[
49             base_addr..(base_addr + 3 * pagesize()),
50             (base_addr + 3 * pagesize())..(base_addr + 6 * pagesize()),
51         ],
52         worker.channel.clone(),
53     );
54 
55     assert!(result.is_ok());
56     worker.close();
57 }
58 
59 #[test]
create_partially_overlap()60 fn create_partially_overlap() {
61     call_test_with_sudo("create_partially_overlap_impl")
62 }
63 
64 #[ignore = "Only to be called by create_partially_overlap"]
65 #[test]
create_partially_overlap_impl()66 fn create_partially_overlap_impl() {
67     let worker = Worker::new(2, 2);
68     let file = tempfile::tempfile().unwrap();
69     let staging_shmem = SharedMemory::new("test staging memory", 3 * pagesize() as u64).unwrap();
70     let shm = create_shared_memory("shm", 3 * pagesize());
71     let base_addr = shm.base_addr();
72 
73     for range in [
74         // the same address range
75         base_addr..(base_addr + 3 * pagesize()),
76         // left of the existing region overlaps
77         (base_addr - pagesize())..(base_addr + pagesize()),
78         // new region is inside
79         (base_addr + pagesize())..(base_addr + 2 * pagesize()),
80         // right of the existing region overlaps
81         (base_addr + 2 * pagesize())..(base_addr + 4 * pagesize()),
82         // new region covers whole the existing region
83         (base_addr - pagesize())..(base_addr + 4 * pagesize()),
84     ] {
85         let result = PageHandler::create(
86             &file,
87             &staging_shmem,
88             &[base_addr..(base_addr + 3 * pagesize()), range],
89             worker.channel.clone(),
90         );
91         assert_eq!(result.is_err(), true);
92         match result {
93             Err(Error::RegionOverlap(_, _)) => {}
94             _ => {
95                 unreachable!("not overlap")
96             }
97         }
98     }
99     worker.close();
100 }
101 
102 #[test]
create_invalid_range()103 fn create_invalid_range() {
104     call_test_with_sudo("create_invalid_range_impl")
105 }
106 
107 #[ignore = "Only to be called by create_invalid_range"]
108 #[test]
create_invalid_range_impl()109 fn create_invalid_range_impl() {
110     let worker = Worker::new(2, 2);
111     let file = tempfile::tempfile().unwrap();
112     let staging_shmem = SharedMemory::new("test staging memory", 6 * pagesize() as u64).unwrap();
113     let shm = create_shared_memory("shm", 6 * pagesize());
114     let base_addr = shm.base_addr();
115     let region = base_addr..(base_addr - pagesize());
116 
117     let result = PageHandler::create(&file, &staging_shmem, &[region], worker.channel.clone());
118 
119     assert!(result.is_err());
120     worker.close();
121 }
122 
wait_thread_with_timeout<T>(join_handle: thread::JoinHandle<T>, timeout_millis: u64) -> T123 fn wait_thread_with_timeout<T>(join_handle: thread::JoinHandle<T>, timeout_millis: u64) -> T {
124     for _ in 0..timeout_millis {
125         if join_handle.is_finished() {
126             return join_handle.join().unwrap();
127         }
128         thread::sleep(time::Duration::from_millis(1));
129     }
130     panic!("thread join timeout");
131 }
132 
133 #[test]
handle_page_fault_zero_success()134 fn handle_page_fault_zero_success() {
135     call_test_with_sudo("handle_page_fault_zero_success_impl")
136 }
137 
138 #[ignore = "Only to be called by handle_page_fault_zero_success"]
139 #[test]
handle_page_fault_zero_success_impl()140 fn handle_page_fault_zero_success_impl() {
141     let worker = Worker::new(2, 2);
142     let file = tempfile::tempfile().unwrap();
143     let staging_shmem = SharedMemory::new("test staging memory", 3 * pagesize() as u64).unwrap();
144     let uffd = create_uffd_for_test();
145     let shm = create_shared_memory("shm", 3 * pagesize());
146     let base_addr = shm.base_addr();
147     let region = base_addr..(base_addr + 3 * pagesize());
148     let regions = [region];
149     let page_handler =
150         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
151     // TODO(b/315998194): Add safety comment
152     #[allow(clippy::undocumented_unsafe_blocks)]
153     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
154 
155     page_handler.handle_page_fault(&uffd, base_addr).unwrap();
156     page_handler
157         .handle_page_fault(&uffd, base_addr + pagesize() + 1)
158         .unwrap();
159     page_handler
160         .handle_page_fault(&uffd, base_addr + 3 * pagesize() - 1)
161         .unwrap();
162 
163     // read values on another thread to avoid blocking forever
164     let join_handle = thread::spawn(move || {
165         let mut result = Vec::new();
166         for i in 0..(3 * pagesize()) {
167             let ptr = shm.mmap.as_ptr() as usize + i;
168             // SAFETY: trivially safe
169             unsafe {
170                 result.push(*(ptr as *mut u8));
171             }
172         }
173         result
174     });
175 
176     let result = wait_thread_with_timeout(join_handle, 100);
177 
178     assert_eq!(result, vec![0; 3 * pagesize()]);
179     worker.close();
180 }
181 
182 #[test]
handle_page_fault_invalid_address()183 fn handle_page_fault_invalid_address() {
184     call_test_with_sudo("handle_page_fault_invalid_address_impl")
185 }
186 
187 #[ignore = "Only to be called by handle_page_fault_invalid_address"]
188 #[test]
handle_page_fault_invalid_address_impl()189 fn handle_page_fault_invalid_address_impl() {
190     let worker = Worker::new(2, 2);
191     let file = tempfile::tempfile().unwrap();
192     let staging_shmem = SharedMemory::new("test staging memory", 3 * pagesize() as u64).unwrap();
193     let uffd = create_uffd_for_test();
194     let shm = create_shared_memory("shm", 3 * pagesize());
195     let base_addr = shm.base_addr();
196     let region = base_addr..(base_addr + 3 * pagesize());
197     let regions = [region];
198     let page_handler =
199         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
200     // TODO(b/315998194): Add safety comment
201     #[allow(clippy::undocumented_unsafe_blocks)]
202     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
203 
204     assert_eq!(
205         page_handler
206             .handle_page_fault(&uffd, base_addr - 1)
207             .is_err(),
208         true
209     );
210     assert_eq!(
211         page_handler
212             .handle_page_fault(&uffd, base_addr + 3 * pagesize())
213             .is_err(),
214         true
215     );
216     worker.close();
217 }
218 
219 #[test]
handle_page_fault_duplicated_page_fault()220 fn handle_page_fault_duplicated_page_fault() {
221     call_test_with_sudo("handle_page_fault_duplicated_page_fault_impl")
222 }
223 
224 #[ignore = "Only to be called by handle_page_fault_duplicated_page_fault"]
225 #[test]
handle_page_fault_duplicated_page_fault_impl()226 fn handle_page_fault_duplicated_page_fault_impl() {
227     let worker = Worker::new(2, 2);
228     let file = tempfile::tempfile().unwrap();
229     let staging_shmem = SharedMemory::new("test staging memory", 3 * pagesize() as u64).unwrap();
230     let uffd = create_uffd_for_test();
231     let shm = create_shared_memory("shm", 3 * pagesize());
232     let base_addr = shm.base_addr();
233     let region = base_addr..(base_addr + 3 * pagesize());
234     let regions = [region];
235     let page_handler =
236         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
237     // TODO(b/315998194): Add safety comment
238     #[allow(clippy::undocumented_unsafe_blocks)]
239     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
240 
241     assert_eq!(
242         page_handler.handle_page_fault(&uffd, base_addr).is_ok(),
243         true
244     );
245     assert_eq!(
246         page_handler.handle_page_fault(&uffd, base_addr + 1).is_ok(),
247         true
248     );
249     worker.close();
250 }
251 
252 #[test]
handle_page_remove_success()253 fn handle_page_remove_success() {
254     call_test_with_sudo("handle_page_remove_success_impl")
255 }
256 
257 #[ignore = "Only to be called by handle_page_remove_success"]
258 #[test]
handle_page_remove_success_impl()259 fn handle_page_remove_success_impl() {
260     let worker = Worker::new(2, 2);
261     let file = tempfile::tempfile().unwrap();
262     let staging_shmem = SharedMemory::new("test staging memory", 3 * pagesize() as u64).unwrap();
263     let uffd = create_uffd_for_test();
264     let shm = create_shared_memory("shm", 3 * pagesize());
265     let base_addr = shm.base_addr();
266     let region = base_addr..(base_addr + 3 * pagesize());
267     let regions = [region];
268     let page_handler =
269         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
270     // TODO(b/315998194): Add safety comment
271     #[allow(clippy::undocumented_unsafe_blocks)]
272     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
273 
274     // fill the first page with zero
275     page_handler.handle_page_fault(&uffd, base_addr).unwrap();
276     // write value on another thread to avoid blocking forever
277     let join_handle = thread::spawn(move || {
278         let ptr = base_addr as *mut u8;
279         // SAFETY: trivially safe
280         unsafe {
281             *ptr = 1;
282         }
283     });
284     wait_thread_with_timeout(join_handle, 100);
285     let second_page_addr = base_addr + pagesize();
286     page_handler
287         .handle_page_remove(base_addr, second_page_addr)
288         .unwrap();
289     // TODO(b/315998194): Add safety comment
290     #[allow(clippy::undocumented_unsafe_blocks)]
291     unsafe {
292         libc::madvise(
293             base_addr as *mut libc::c_void,
294             pagesize(),
295             libc::MADV_REMOVE,
296         );
297     }
298     // fill the first page with zero again
299     page_handler.handle_page_fault(&uffd, base_addr).unwrap();
300     // read value on another thread to avoid blocking forever
301     let join_handle = thread::spawn(move || {
302         let ptr = base_addr as *mut u8;
303         // SAFETY: trivially safe
304         unsafe { *ptr }
305     });
306 
307     assert_eq!(wait_thread_with_timeout(join_handle, 100), 0);
308     worker.close();
309 }
310 
311 #[test]
handle_page_remove_invalid_address()312 fn handle_page_remove_invalid_address() {
313     call_test_with_sudo("handle_page_remove_invalid_address_impl")
314 }
315 
316 #[ignore = "Only to be called by handle_page_remove_invalid_address"]
317 #[test]
handle_page_remove_invalid_address_impl()318 fn handle_page_remove_invalid_address_impl() {
319     let worker = Worker::new(2, 2);
320     let file = tempfile::tempfile().unwrap();
321     let staging_shmem = SharedMemory::new("test staging memory", 3 * pagesize() as u64).unwrap();
322     let uffd = create_uffd_for_test();
323     let shm = create_shared_memory("shm", 3 * pagesize());
324     let base_addr = shm.base_addr();
325     let region = base_addr..(base_addr + 3 * pagesize());
326     let regions = [region];
327     let page_handler =
328         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
329     // TODO(b/315998194): Add safety comment
330     #[allow(clippy::undocumented_unsafe_blocks)]
331     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
332 
333     page_handler.handle_page_fault(&uffd, base_addr).unwrap();
334     page_handler
335         .handle_page_fault(&uffd, base_addr + pagesize())
336         .unwrap();
337     page_handler
338         .handle_page_fault(&uffd, base_addr + 2 * pagesize())
339         .unwrap();
340     assert_eq!(
341         page_handler
342             .handle_page_remove(base_addr - 1, base_addr + 3 * pagesize())
343             .is_err(),
344         true
345     );
346     assert_eq!(
347         page_handler
348             .handle_page_remove(base_addr, base_addr + 3 * pagesize() + 1)
349             .is_err(),
350         true
351     );
352     // remove for whole region should succeed.
353     assert_eq!(
354         page_handler
355             .handle_page_remove(base_addr, base_addr + 3 * pagesize())
356             .is_ok(),
357         true
358     );
359     worker.close();
360 }
361 
362 #[test]
move_to_staging_data_written_before_enabling()363 fn move_to_staging_data_written_before_enabling() {
364     call_test_with_sudo("move_to_staging_data_written_before_enabling_impl")
365 }
366 
367 #[ignore = "Only to be called by move_to_staging_data_written_before_enabling"]
368 #[test]
move_to_staging_data_written_before_enabling_impl()369 fn move_to_staging_data_written_before_enabling_impl() {
370     let worker = Worker::new(2, 2);
371     let uffd = create_uffd_for_test();
372     let file = tempfile::tempfile().unwrap();
373     let staging_shmem = SharedMemory::new("test staging memory", 6 * pagesize() as u64).unwrap();
374     let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
375     let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
376         .from_shared_memory(&shm)
377         .build()
378         .unwrap();
379     let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
380         .from_shared_memory(&shm)
381         .offset(3 * pagesize() as u64)
382         .build()
383         .unwrap();
384     let base_addr1 = mmap1.as_ptr() as usize;
385     let base_addr2 = mmap2.as_ptr() as usize;
386 
387     let regions = [
388         base_addr1..(base_addr1 + 3 * pagesize()),
389         base_addr2..(base_addr2 + 3 * pagesize()),
390     ];
391     let page_handler =
392         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
393     // write data before registering to userfaultfd
394     // TODO(b/315998194): Add safety comment
395     #[allow(clippy::undocumented_unsafe_blocks)]
396     unsafe {
397         for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
398             *(i as *mut u8) = 1;
399         }
400         for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
401             *(i as *mut u8) = 2;
402         }
403         for i in base_addr2 + 2 * pagesize()..base_addr2 + 3 * pagesize() {
404             *(i as *mut u8) = 3;
405         }
406     }
407     // TODO(b/315998194): Add safety comment
408     #[allow(clippy::undocumented_unsafe_blocks)]
409     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
410 
411     // TODO(b/315998194): Add safety comment
412     #[allow(clippy::undocumented_unsafe_blocks)]
413     unsafe {
414         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
415         page_handler
416             .move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
417             .unwrap();
418     }
419     worker.channel.wait_complete();
420     // page faults on all pages.
421     for i in 0..3 {
422         page_handler
423             .handle_page_fault(&uffd, base_addr1 + i * pagesize())
424             .unwrap();
425         page_handler
426             .handle_page_fault(&uffd, base_addr2 + i * pagesize())
427             .unwrap();
428     }
429 
430     // read values on another thread to avoid blocking forever
431     let join_handle = thread::spawn(move || {
432         let mut result = Vec::new();
433         for i in 0..3 {
434             for j in 0..pagesize() {
435                 let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
436                 // SAFETY: trivially safe
437                 unsafe {
438                     result.push(*ptr);
439                 }
440             }
441         }
442         for i in 0..3 {
443             for j in 0..pagesize() {
444                 let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
445                 // SAFETY: trivially safe
446                 unsafe {
447                     result.push(*ptr);
448                 }
449             }
450         }
451         result
452     });
453     let result = wait_thread_with_timeout(join_handle, 100);
454     let values: Vec<u8> = vec![0, 1, 0, 0, 2, 3];
455     for (i, v) in values.iter().enumerate() {
456         for j in 0..pagesize() {
457             assert_eq!(&result[i * pagesize() + j], v);
458         }
459     }
460     worker.close();
461 }
462 
page_idx_range(start_addr: usize, end_addr: usize) -> Range<usize>463 fn page_idx_range(start_addr: usize, end_addr: usize) -> Range<usize> {
464     (start_addr / pagesize())..(end_addr / pagesize())
465 }
466 
page_idx_to_addr(page_idx: usize) -> usize467 fn page_idx_to_addr(page_idx: usize) -> usize {
468     page_idx * pagesize()
469 }
470 
471 #[test]
move_to_staging_hugepage_chunks()472 fn move_to_staging_hugepage_chunks() {
473     call_test_with_sudo("move_to_staging_hugepage_chunks_impl")
474 }
475 
476 #[ignore = "Only to be called by move_to_staging_hugepage_chunks"]
477 #[test]
move_to_staging_hugepage_chunks_impl()478 fn move_to_staging_hugepage_chunks_impl() {
479     let worker = Worker::new(2, 2);
480     let uffd = create_uffd_for_test();
481     let file = tempfile::tempfile().unwrap();
482     let staging_shmem =
483         SharedMemory::new("test staging memory", 10 * HUGEPAGE_SIZE as u64).unwrap();
484     let shm = SharedMemory::new("shm", 10 * HUGEPAGE_SIZE as u64).unwrap();
485     let mmap1 = MemoryMappingBuilder::new(5 * HUGEPAGE_SIZE)
486         .from_shared_memory(&shm)
487         .build()
488         .unwrap();
489     let mmap2 = MemoryMappingBuilder::new(5 * HUGEPAGE_SIZE)
490         .from_shared_memory(&shm)
491         .offset(5 * HUGEPAGE_SIZE as u64)
492         .build()
493         .unwrap();
494     let base_addr1 = mmap1.as_ptr() as usize;
495     let base_addr2 = mmap2.as_ptr() as usize;
496 
497     let regions = [
498         base_addr1..(base_addr1 + 5 * HUGEPAGE_SIZE),
499         base_addr2..(base_addr2 + 5 * HUGEPAGE_SIZE),
500     ];
501     let page_handler =
502         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
503     // write data before registering to userfaultfd
504     // TODO(b/315998194): Add safety comment
505     #[allow(clippy::undocumented_unsafe_blocks)]
506     unsafe {
507         for i in page_idx_range(base_addr1 + pagesize(), base_addr1 + 3 * pagesize()) {
508             *(page_idx_to_addr(i) as *mut u8) = 1;
509         }
510         for i in page_idx_range(
511             base_addr1 + HUGEPAGE_SIZE - pagesize(),
512             base_addr1 + HUGEPAGE_SIZE + pagesize(),
513         ) {
514             *(page_idx_to_addr(i) as *mut u8) = 2;
515         }
516         for i in page_idx_range(
517             base_addr1 + 2 * HUGEPAGE_SIZE + pagesize(),
518             base_addr1 + 3 * HUGEPAGE_SIZE + pagesize(),
519         ) {
520             *(page_idx_to_addr(i) as *mut u8) = 3;
521         }
522         for i in page_idx_range(base_addr2 + HUGEPAGE_SIZE, base_addr2 + 2 * HUGEPAGE_SIZE) {
523             *(page_idx_to_addr(i) as *mut u8) = 4;
524         }
525         for i in page_idx_range(
526             base_addr2 + 2 * HUGEPAGE_SIZE + pagesize(),
527             base_addr2 + 5 * HUGEPAGE_SIZE - pagesize(),
528         ) {
529             *(page_idx_to_addr(i) as *mut u8) = 5;
530         }
531     }
532     // TODO(b/315998194): Add safety comment
533     #[allow(clippy::undocumented_unsafe_blocks)]
534     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
535 
536     // TODO(b/315998194): Add safety comment
537     #[allow(clippy::undocumented_unsafe_blocks)]
538     unsafe {
539         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
540         page_handler
541             .move_to_staging(base_addr2, &shm, 5 * HUGEPAGE_SIZE as u64)
542             .unwrap();
543     }
544     worker.channel.wait_complete();
545     // page faults on all pages.
546     for i in 0..5 * HUGEPAGE_SIZE / pagesize() {
547         page_handler
548             .handle_page_fault(&uffd, base_addr1 + i * pagesize())
549             .unwrap();
550         page_handler
551             .handle_page_fault(&uffd, base_addr2 + i * pagesize())
552             .unwrap();
553     }
554 
555     // read values on another thread to avoid blocking forever
556     let join_handle = thread::spawn(move || {
557         let mut result = Vec::new();
558         for i in page_idx_range(base_addr1, base_addr1 + 5 * HUGEPAGE_SIZE) {
559             let ptr = (page_idx_to_addr(i)) as *mut u8;
560             // SAFETY: trivially safe
561             unsafe {
562                 result.push(*ptr);
563             }
564         }
565         for i in page_idx_range(base_addr2, base_addr2 + 5 * HUGEPAGE_SIZE) {
566             let ptr = (page_idx_to_addr(i)) as *mut u8;
567             // SAFETY: trivially safe
568             unsafe {
569                 result.push(*ptr);
570             }
571         }
572         result
573     });
574     let result = wait_thread_with_timeout(join_handle, 100);
575     assert_eq!(result[0], 0);
576     assert_eq!(result[1], 1);
577     assert_eq!(result[2], 1);
578     for i in page_idx_range(3 * pagesize(), HUGEPAGE_SIZE - pagesize()) {
579         assert_eq!(result[i], 0);
580     }
581     for i in page_idx_range(HUGEPAGE_SIZE - pagesize(), HUGEPAGE_SIZE + pagesize()) {
582         assert_eq!(result[i], 2);
583     }
584     for i in page_idx_range(HUGEPAGE_SIZE + pagesize(), 2 * HUGEPAGE_SIZE + pagesize()) {
585         assert_eq!(result[i], 0);
586     }
587     for i in page_idx_range(
588         2 * HUGEPAGE_SIZE + pagesize(),
589         3 * HUGEPAGE_SIZE + pagesize(),
590     ) {
591         assert_eq!(result[i], 3);
592     }
593     for i in page_idx_range(3 * HUGEPAGE_SIZE + pagesize(), 6 * HUGEPAGE_SIZE) {
594         assert_eq!(result[i], 0);
595     }
596     for i in page_idx_range(6 * HUGEPAGE_SIZE, 7 * HUGEPAGE_SIZE) {
597         assert_eq!(result[i], 4);
598     }
599     for i in page_idx_range(7 * HUGEPAGE_SIZE, 7 * HUGEPAGE_SIZE + pagesize()) {
600         assert_eq!(result[i], 0);
601     }
602     for i in page_idx_range(
603         7 * HUGEPAGE_SIZE + pagesize(),
604         10 * HUGEPAGE_SIZE - pagesize(),
605     ) {
606         assert_eq!(result[i], 5);
607     }
608     for i in page_idx_range(10 * HUGEPAGE_SIZE - pagesize(), 10 * HUGEPAGE_SIZE) {
609         assert_eq!(result[i], 0);
610     }
611     worker.close();
612 }
613 
614 #[test]
move_to_staging_invalid_base_addr()615 fn move_to_staging_invalid_base_addr() {
616     call_test_with_sudo("move_to_staging_invalid_base_addr_impl")
617 }
618 
619 #[ignore = "Only to be called by move_to_staging_invalid_base_addr"]
620 #[test]
move_to_staging_invalid_base_addr_impl()621 fn move_to_staging_invalid_base_addr_impl() {
622     let worker = Worker::new(2, 2);
623     let uffd = create_uffd_for_test();
624     let file = tempfile::tempfile().unwrap();
625     let staging_shmem = SharedMemory::new("test staging memory", 3 * pagesize() as u64).unwrap();
626     let shm = create_shared_memory("shm1", 3 * pagesize());
627     let base_addr = shm.base_addr();
628     let region = base_addr..(base_addr + 3 * pagesize());
629     let regions = [region];
630     let page_handler =
631         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
632     // TODO(b/315998194): Add safety comment
633     #[allow(clippy::undocumented_unsafe_blocks)]
634     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
635 
636     // the base_addr is within the region
637     assert_eq!(
638         {
639             // TODO(b/315998194): Add safety comment
640             #[allow(clippy::undocumented_unsafe_blocks)]
641             unsafe {
642                 page_handler
643                     .move_to_staging(base_addr + pagesize(), &shm.shm, 0)
644                     .is_err()
645             }
646         },
647         true
648     );
649     // the base_addr is outside of the region
650     assert_eq!(
651         {
652             // TODO(b/315998194): Add safety comment
653             #[allow(clippy::undocumented_unsafe_blocks)]
654             unsafe {
655                 page_handler
656                     .move_to_staging(base_addr - pagesize(), &shm.shm, 0)
657                     .is_err()
658             }
659         },
660         true
661     );
662     worker.close();
663 }
664 
swap_out_all(page_handler: &PageHandler)665 fn swap_out_all(page_handler: &PageHandler) {
666     while page_handler.swap_out(1024 * 1024).unwrap() != 0 {}
667 }
668 
669 #[test]
swap_out_success()670 fn swap_out_success() {
671     call_test_with_sudo("swap_out_success_impl")
672 }
673 
674 #[ignore = "Only to be called by swap_out_success"]
675 #[test]
swap_out_success_impl()676 fn swap_out_success_impl() {
677     let worker = Worker::new(2, 2);
678     let uffd = create_uffd_for_test();
679     let file = tempfile::tempfile().unwrap();
680     let staging_shmem = SharedMemory::new("test staging memory", 6 * pagesize() as u64).unwrap();
681     let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
682     let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
683         .from_shared_memory(&shm)
684         .build()
685         .unwrap();
686     let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
687         .from_shared_memory(&shm)
688         .offset(3 * pagesize() as u64)
689         .build()
690         .unwrap();
691     let base_addr1 = mmap1.as_ptr() as usize;
692     let base_addr2 = mmap2.as_ptr() as usize;
693     let regions = [
694         base_addr1..(base_addr1 + 3 * pagesize()),
695         base_addr2..(base_addr2 + 3 * pagesize()),
696     ];
697     let page_handler =
698         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
699     // write data before registering to userfaultfd
700     // TODO(b/315998194): Add safety comment
701     #[allow(clippy::undocumented_unsafe_blocks)]
702     unsafe {
703         for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
704             *(i as *mut u8) = 1;
705         }
706         for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
707             *(i as *mut u8) = 2;
708         }
709     }
710     // TODO(b/315998194): Add safety comment
711     #[allow(clippy::undocumented_unsafe_blocks)]
712     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
713 
714     // TODO(b/315998194): Add safety comment
715     #[allow(clippy::undocumented_unsafe_blocks)]
716     unsafe {
717         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
718         page_handler
719             .move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
720             .unwrap();
721     }
722     worker.channel.wait_complete();
723     swap_out_all(&page_handler);
724     // page faults on all pages. page 0 and page 2 will be swapped in from the file. page 1 will
725     // be filled with zero.
726     for i in 0..3 {
727         page_handler
728             .handle_page_fault(&uffd, base_addr1 + i * pagesize())
729             .unwrap();
730         page_handler
731             .handle_page_fault(&uffd, base_addr2 + i * pagesize())
732             .unwrap();
733     }
734 
735     // read values on another thread to avoid blocking forever
736     let join_handle = thread::spawn(move || {
737         let mut result = Vec::new();
738         for i in 0..3 {
739             for j in 0..pagesize() {
740                 let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
741                 // SAFETY: trivially safe
742                 unsafe {
743                     result.push(*ptr);
744                 }
745             }
746         }
747         for i in 0..3 {
748             for j in 0..pagesize() {
749                 let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
750                 // SAFETY: trivially safe
751                 unsafe {
752                     result.push(*ptr);
753                 }
754             }
755         }
756         result
757     });
758     let result = wait_thread_with_timeout(join_handle, 100);
759     let values: Vec<u8> = vec![0, 1, 0, 0, 2, 0];
760     for (i, v) in values.iter().enumerate() {
761         for j in 0..pagesize() {
762             assert_eq!(&result[i * pagesize() + j], v);
763         }
764     }
765     worker.close();
766 }
767 
768 #[test]
swap_out_handled_page()769 fn swap_out_handled_page() {
770     call_test_with_sudo("swap_out_handled_page_impl")
771 }
772 
773 #[ignore = "Only to be called by swap_out_handled_page"]
774 #[test]
swap_out_handled_page_impl()775 fn swap_out_handled_page_impl() {
776     let worker = Worker::new(2, 2);
777     let uffd = create_uffd_for_test();
778     let file = tempfile::tempfile().unwrap();
779     let staging_shmem = SharedMemory::new("test staging memory", 6 * pagesize() as u64).unwrap();
780     let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
781     let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
782         .from_shared_memory(&shm)
783         .build()
784         .unwrap();
785     let base_addr1 = mmap1.as_ptr() as usize;
786 
787     let region = base_addr1..(base_addr1 + 3 * pagesize());
788     let regions = [region];
789     let page_handler =
790         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
791     // write data before registering to userfaultfd
792     // TODO(b/315998194): Add safety comment
793     #[allow(clippy::undocumented_unsafe_blocks)]
794     unsafe {
795         for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
796             *(i as *mut u8) = 1;
797         }
798     }
799     // TODO(b/315998194): Add safety comment
800     #[allow(clippy::undocumented_unsafe_blocks)]
801     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
802 
803     // TODO(b/315998194): Add safety comment
804     #[allow(clippy::undocumented_unsafe_blocks)]
805     unsafe {
806         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
807     }
808     worker.channel.wait_complete();
809     // page in before swap_out()
810     page_handler
811         .handle_page_fault(&uffd, base_addr1 + pagesize())
812         .unwrap();
813     swap_out_all(&page_handler);
814 
815     // read values on another thread to avoid blocking forever
816     let join_handle = thread::spawn(move || {
817         let mut result = Vec::new();
818         for i in 0..pagesize() {
819             let ptr = (base_addr1 + pagesize() + i) as *mut u8;
820             // SAFETY: trivially safe
821             unsafe {
822                 result.push(*ptr);
823             }
824         }
825         result
826     });
827     // reading the page is not blocked.s
828     let result = wait_thread_with_timeout(join_handle, 100);
829     for v in result {
830         assert_eq!(v, 1);
831     }
832     worker.close();
833 }
834 
835 #[test]
swap_out_twice()836 fn swap_out_twice() {
837     call_test_with_sudo("swap_out_twice_impl")
838 }
839 
840 #[ignore = "Only to be called by swap_out_twice"]
841 #[test]
swap_out_twice_impl()842 fn swap_out_twice_impl() {
843     let worker = Worker::new(2, 2);
844     let uffd = create_uffd_for_test();
845     let file = tempfile::tempfile().unwrap();
846     let staging_shmem = SharedMemory::new("test staging memory", 6 * pagesize() as u64).unwrap();
847     let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
848     let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
849         .from_shared_memory(&shm)
850         .build()
851         .unwrap();
852     let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
853         .from_shared_memory(&shm)
854         .offset(3 * pagesize() as u64)
855         .build()
856         .unwrap();
857     let base_addr1 = mmap1.as_ptr() as usize;
858     let base_addr2 = mmap2.as_ptr() as usize;
859     let regions = [
860         base_addr1..(base_addr1 + 3 * pagesize()),
861         base_addr2..(base_addr2 + 3 * pagesize()),
862     ];
863     let page_handler =
864         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
865     // TODO(b/315998194): Add safety comment
866     #[allow(clippy::undocumented_unsafe_blocks)]
867     unsafe {
868         for i in 0..pagesize() {
869             *((base_addr1 + i) as *mut u8) = 1;
870             *((base_addr1 + 2 * pagesize() + i) as *mut u8) = 2;
871             *((base_addr2 + i) as *mut u8) = 3;
872             *((base_addr2 + 2 * pagesize() + i) as *mut u8) = 4;
873         }
874     }
875     // TODO(b/315998194): Add safety comment
876     #[allow(clippy::undocumented_unsafe_blocks)]
877     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
878 
879     // TODO(b/315998194): Add safety comment
880     #[allow(clippy::undocumented_unsafe_blocks)]
881     unsafe {
882         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
883         page_handler
884             .move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
885             .unwrap();
886     }
887     worker.channel.wait_complete();
888     swap_out_all(&page_handler);
889     // page faults on all pages in mmap1.
890     for i in 0..3 {
891         page_handler
892             .handle_page_fault(&uffd, (base_addr1) + i * pagesize())
893             .unwrap();
894     }
895     // write values on another thread to avoid blocking forever
896     let join_handle = thread::spawn(move || {
897         for i in 0..pagesize() {
898             let ptr = (base_addr1 + pagesize() + i) as *mut u8;
899             // SAFETY: trivially safe
900             unsafe {
901                 *ptr = 5;
902             }
903         }
904         for i in 0..pagesize() {
905             let ptr = (base_addr1 + 2 * pagesize() + i) as *mut u8;
906             // SAFETY: trivially safe
907             unsafe {
908                 *ptr = 6;
909             }
910         }
911     });
912     wait_thread_with_timeout(join_handle, 100);
913     // TODO(b/315998194): Add safety comment
914     #[allow(clippy::undocumented_unsafe_blocks)]
915     unsafe {
916         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
917         page_handler
918             .move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
919             .unwrap();
920     }
921     worker.channel.wait_complete();
922     swap_out_all(&page_handler);
923 
924     // page faults on all pages.
925     for i in 0..3 {
926         page_handler
927             .handle_page_fault(&uffd, base_addr1 + i * pagesize())
928             .unwrap();
929         page_handler
930             .handle_page_fault(&uffd, base_addr2 + i * pagesize())
931             .unwrap();
932     }
933     // read values on another thread to avoid blocking forever
934     let join_handle = thread::spawn(move || {
935         let mut result = Vec::new();
936         for i in 0..3 {
937             for j in 0..pagesize() {
938                 let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
939                 // SAFETY: trivially safe
940                 unsafe {
941                     result.push(*ptr);
942                 }
943             }
944         }
945         for i in 0..3 {
946             for j in 0..pagesize() {
947                 let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
948                 // SAFETY: trivially safe
949                 unsafe {
950                     result.push(*ptr);
951                 }
952             }
953         }
954         result
955     });
956     let result = wait_thread_with_timeout(join_handle, 100);
957     let values: Vec<u8> = vec![1, 5, 6, 3, 0, 4];
958     for (i, v) in values.iter().enumerate() {
959         for j in 0..pagesize() {
960             assert_eq!(&result[i * pagesize() + j], v);
961         }
962     }
963     worker.close();
964 }
965 
966 #[test]
swap_in_success()967 fn swap_in_success() {
968     call_test_with_sudo("swap_in_success_impl")
969 }
970 
971 #[ignore = "Only to be called by swap_in_success"]
972 #[test]
swap_in_success_impl()973 fn swap_in_success_impl() {
974     let worker = Worker::new(2, 2);
975     let uffd = create_uffd_for_test();
976     let file = tempfile::tempfile().unwrap();
977     let staging_shmem = SharedMemory::new("test staging memory", 6 * pagesize() as u64).unwrap();
978     let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
979     let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
980         .from_shared_memory(&shm)
981         .build()
982         .unwrap();
983     let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
984         .from_shared_memory(&shm)
985         .offset(3 * pagesize() as u64)
986         .build()
987         .unwrap();
988     let base_addr1 = mmap1.as_ptr() as usize;
989     let base_addr2 = mmap2.as_ptr() as usize;
990     let regions = [
991         base_addr1..(base_addr1 + 3 * pagesize()),
992         base_addr2..(base_addr2 + 3 * pagesize()),
993     ];
994     let page_handler =
995         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
996     // TODO(b/315998194): Add safety comment
997     #[allow(clippy::undocumented_unsafe_blocks)]
998     unsafe {
999         for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
1000             *(i as *mut u8) = 1;
1001         }
1002         for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
1003             *(i as *mut u8) = 2;
1004         }
1005         for i in base_addr2 + 2 * pagesize()..base_addr2 + 3 * pagesize() {
1006             *(i as *mut u8) = 3;
1007         }
1008     }
1009     // TODO(b/315998194): Add safety comment
1010     #[allow(clippy::undocumented_unsafe_blocks)]
1011     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
1012 
1013     // TODO(b/315998194): Add safety comment
1014     #[allow(clippy::undocumented_unsafe_blocks)]
1015     unsafe {
1016         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
1017         page_handler
1018             .move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
1019             .unwrap();
1020     }
1021     worker.channel.wait_complete();
1022     swap_out_all(&page_handler);
1023     page_handler
1024         .handle_page_fault(&uffd, base_addr1 + pagesize())
1025         .unwrap();
1026     page_handler
1027         .handle_page_fault(&uffd, base_addr2 + pagesize())
1028         .unwrap();
1029     // TODO(b/315998194): Add safety comment
1030     #[allow(clippy::undocumented_unsafe_blocks)]
1031     unsafe {
1032         for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
1033             *(i as *mut u8) = 4;
1034         }
1035     }
1036     // move to staging memory.
1037     // TODO(b/315998194): Add safety comment
1038     #[allow(clippy::undocumented_unsafe_blocks)]
1039     unsafe {
1040         page_handler
1041             .move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
1042             .unwrap();
1043     }
1044     worker.channel.wait_complete();
1045     let mut swap_in_ctx = page_handler.start_swap_in();
1046     while swap_in_ctx.swap_in(&uffd, 1024 * 1024).unwrap() != 0 {}
1047     unregister_regions(&regions, array::from_ref(&uffd)).unwrap();
1048 
1049     // read values on another thread to avoid blocking forever
1050     let join_handle = thread::spawn(move || {
1051         let mut result = Vec::new();
1052         for i in 0..3 {
1053             for j in 0..pagesize() {
1054                 let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
1055                 // SAFETY: trivially safe
1056                 unsafe {
1057                     result.push(*ptr);
1058                 }
1059             }
1060         }
1061         for i in 0..3 {
1062             for j in 0..pagesize() {
1063                 let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
1064                 // SAFETY: trivially safe
1065                 unsafe {
1066                     result.push(*ptr);
1067                 }
1068             }
1069         }
1070         result
1071     });
1072     let result = wait_thread_with_timeout(join_handle, 100);
1073     let values: Vec<u8> = vec![0, 1, 0, 0, 4, 3];
1074     for (i, v) in values.iter().enumerate() {
1075         for j in 0..pagesize() {
1076             assert_eq!(&result[i * pagesize() + j], v);
1077         }
1078     }
1079     worker.close();
1080 }
1081 
1082 #[test]
trim_success()1083 fn trim_success() {
1084     call_test_with_sudo("trim_success_impl")
1085 }
1086 
1087 #[ignore = "Only to be called by trim_success"]
1088 #[test]
trim_success_impl()1089 fn trim_success_impl() {
1090     let worker = Worker::new(2, 2);
1091     let uffd = create_uffd_for_test();
1092     let file = tempfile::tempfile().unwrap();
1093     let staging_shmem = SharedMemory::new("test staging memory", 6 * pagesize() as u64).unwrap();
1094     let shm = SharedMemory::new("shm", 6 * pagesize() as u64).unwrap();
1095     let mmap1 = MemoryMappingBuilder::new(3 * pagesize())
1096         .from_shared_memory(&shm)
1097         .build()
1098         .unwrap();
1099     let mmap2 = MemoryMappingBuilder::new(3 * pagesize())
1100         .from_shared_memory(&shm)
1101         .offset(3 * pagesize() as u64)
1102         .build()
1103         .unwrap();
1104     let base_addr1 = mmap1.as_ptr() as usize;
1105     let base_addr2 = mmap2.as_ptr() as usize;
1106     let regions = [
1107         base_addr1..(base_addr1 + 3 * pagesize()),
1108         base_addr2..(base_addr2 + 3 * pagesize()),
1109     ];
1110     let page_handler =
1111         PageHandler::create(&file, &staging_shmem, &regions, worker.channel.clone()).unwrap();
1112     // TODO(b/315998194): Add safety comment
1113     #[allow(clippy::undocumented_unsafe_blocks)]
1114     unsafe {
1115         for i in base_addr1..base_addr1 + pagesize() {
1116             *(i as *mut u8) = 0;
1117         }
1118         for i in base_addr1 + pagesize()..base_addr1 + 2 * pagesize() {
1119             *(i as *mut u8) = 1;
1120         }
1121         for i in base_addr2..base_addr2 + pagesize() {
1122             *(i as *mut u8) = 0;
1123         }
1124         for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
1125             *(i as *mut u8) = 2;
1126         }
1127         for i in base_addr2 + 2 * pagesize()..base_addr2 + 3 * pagesize() {
1128             *(i as *mut u8) = 3;
1129         }
1130     }
1131     // TODO(b/315998194): Add safety comment
1132     #[allow(clippy::undocumented_unsafe_blocks)]
1133     unsafe { register_regions(&regions, array::from_ref(&uffd)) }.unwrap();
1134 
1135     // TODO(b/315998194): Add safety comment
1136     #[allow(clippy::undocumented_unsafe_blocks)]
1137     unsafe {
1138         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
1139         page_handler
1140             .move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
1141             .unwrap();
1142     }
1143     worker.channel.wait_complete();
1144 
1145     let mut trim_ctx = page_handler.start_trim();
1146 
1147     assert_eq!(trim_ctx.trim_pages(6 * pagesize()).unwrap().unwrap(), 1);
1148     assert_eq!(trim_ctx.trimmed_clean_pages(), 0);
1149     assert_eq!(trim_ctx.trimmed_zero_pages(), 1);
1150     // 1 zero page
1151     assert_eq!(trim_ctx.trim_pages(6 * pagesize()).unwrap().unwrap(), 1);
1152     assert_eq!(trim_ctx.trimmed_clean_pages(), 0);
1153     assert_eq!(trim_ctx.trimmed_zero_pages(), 2);
1154 
1155     swap_out_all(&page_handler);
1156     for i in 0..3 {
1157         page_handler
1158             .handle_page_fault(&uffd, base_addr1 + i * pagesize())
1159             .unwrap();
1160         page_handler
1161             .handle_page_fault(&uffd, base_addr2 + i * pagesize())
1162             .unwrap();
1163     }
1164     // TODO(b/315998194): Add safety comment
1165     #[allow(clippy::undocumented_unsafe_blocks)]
1166     unsafe {
1167         for i in base_addr2 + pagesize()..base_addr2 + 2 * pagesize() {
1168             *(i as *mut u8) = 4;
1169         }
1170     }
1171 
1172     // move to staging memory.
1173     // TODO(b/315998194): Add safety comment
1174     #[allow(clippy::undocumented_unsafe_blocks)]
1175     unsafe {
1176         page_handler.move_to_staging(base_addr1, &shm, 0).unwrap();
1177         page_handler
1178             .move_to_staging(base_addr2, &shm, 3 * pagesize() as u64)
1179             .unwrap();
1180     }
1181     worker.channel.wait_complete();
1182 
1183     let mut trim_ctx = page_handler.start_trim();
1184     // 2 zero pages and 1 clean page
1185     assert_eq!(trim_ctx.trim_pages(6 * pagesize()).unwrap().unwrap(), 3);
1186     assert_eq!(trim_ctx.trimmed_clean_pages(), 1);
1187     assert_eq!(trim_ctx.trimmed_zero_pages(), 2);
1188     // 1 zero page and 1 clean pages
1189     assert_eq!(trim_ctx.trim_pages(6 * pagesize()).unwrap().unwrap(), 2);
1190     assert_eq!(trim_ctx.trimmed_clean_pages(), 2);
1191     assert_eq!(trim_ctx.trimmed_zero_pages(), 3);
1192     assert!(trim_ctx.trim_pages(pagesize()).unwrap().is_none());
1193 
1194     let mut swap_in_ctx = page_handler.start_swap_in();
1195     while swap_in_ctx.swap_in(&uffd, 1024 * 1024).unwrap() != 0 {}
1196     unregister_regions(&regions, array::from_ref(&uffd)).unwrap();
1197 
1198     // read values on another thread to avoid blocking forever
1199     let join_handle = thread::spawn(move || {
1200         let mut result = Vec::new();
1201         for i in 0..3 {
1202             for j in 0..pagesize() {
1203                 let ptr = (base_addr1 + i * pagesize() + j) as *mut u8;
1204                 // SAFETY: trivially safe
1205                 unsafe {
1206                     result.push(*ptr);
1207                 }
1208             }
1209         }
1210         for i in 0..3 {
1211             for j in 0..pagesize() {
1212                 let ptr = (base_addr2 + i * pagesize() + j) as *mut u8;
1213                 // SAFETY: trivially safe
1214                 unsafe {
1215                     result.push(*ptr);
1216                 }
1217             }
1218         }
1219         result
1220     });
1221     let result = wait_thread_with_timeout(join_handle, 100);
1222     let values: Vec<u8> = vec![0, 1, 0, 0, 4, 3];
1223     for (i, v) in values.iter().enumerate() {
1224         for j in 0..pagesize() {
1225             assert_eq!(&result[i * pagesize() + j], v);
1226         }
1227     }
1228     worker.close();
1229 }
1230