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, ®ions, worker.channel.clone()).unwrap();
151 // TODO(b/315998194): Add safety comment
152 #[allow(clippy::undocumented_unsafe_blocks)]
153 unsafe { register_regions(®ions, 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, ®ions, worker.channel.clone()).unwrap();
200 // TODO(b/315998194): Add safety comment
201 #[allow(clippy::undocumented_unsafe_blocks)]
202 unsafe { register_regions(®ions, 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, ®ions, worker.channel.clone()).unwrap();
237 // TODO(b/315998194): Add safety comment
238 #[allow(clippy::undocumented_unsafe_blocks)]
239 unsafe { register_regions(®ions, 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, ®ions, worker.channel.clone()).unwrap();
270 // TODO(b/315998194): Add safety comment
271 #[allow(clippy::undocumented_unsafe_blocks)]
272 unsafe { register_regions(®ions, 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, ®ions, worker.channel.clone()).unwrap();
329 // TODO(b/315998194): Add safety comment
330 #[allow(clippy::undocumented_unsafe_blocks)]
331 unsafe { register_regions(®ions, 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, ®ions, 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(®ions, 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, ®ions, 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(®ions, 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, ®ions, worker.channel.clone()).unwrap();
632 // TODO(b/315998194): Add safety comment
633 #[allow(clippy::undocumented_unsafe_blocks)]
634 unsafe { register_regions(®ions, 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, ®ions, 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(®ions, 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, ®ions, 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(®ions, 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, ®ions, 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(®ions, 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, ®ions, 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(®ions, 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(®ions, 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, ®ions, 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(®ions, 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(®ions, 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