xref: /xnu-10002.81.5/tests/vm/vm_test_cow_before_zf_read.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
1 #include <darwintest.h>
2 
3 #include <stdlib.h>
4 
5 #include <mach/mach_init.h>
6 #include <mach/mach_vm.h>
7 #include <mach/vm_map.h>
8 
9 T_GLOBAL_META(
10 	T_META_NAMESPACE("xnu.vm"),
11 	T_META_RADAR_COMPONENT_NAME("xnu"),
12 	T_META_RADAR_COMPONENT_VERSION("VM"));
13 
14 void
test_cow_before_zf_read(boolean_t write_first,boolean_t share_it)15 test_cow_before_zf_read(
16 	boolean_t write_first,
17 	boolean_t share_it)
18 {
19 	kern_return_t kr;
20 	mach_vm_size_t vm_size = 1024 * 1024;
21 
22 	uint64_t first_layer_addr = 0;
23 	kr = mach_vm_allocate(mach_task_self(),
24 	    &first_layer_addr,
25 	    vm_size,
26 	    VM_FLAGS_ANYWHERE);
27 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_vm_allocate()");
28 
29 	uint64_t cow_addr = 0;
30 	if (share_it) {
31 		// sharing this allocation will make it COPY_DELAY
32 		// rather than COPY_SYMMETRIC.
33 		memory_object_size_t me_size = vm_size;
34 		mach_port_t first_layer_entry_port = 0;
35 		kr = mach_make_memory_entry_64(mach_task_self(),
36 		    &me_size,
37 		    first_layer_addr,
38 		    MAP_MEM_VM_SHARE | VM_PROT_READ | VM_PROT_WRITE,
39 		    &first_layer_entry_port,
40 		    MACH_PORT_NULL);
41 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_make_memory_entry_64(VM_SHARE)");
42 
43 		// remap the COW way
44 		vm_prot_t cur_prot, max_prot;
45 		kr = mach_vm_remap(mach_task_self(),
46 		    &cow_addr,
47 		    vm_size,
48 		    0,                               /* mask */
49 		    VM_FLAGS_ANYWHERE,
50 		    mach_task_self(),
51 		    first_layer_addr,
52 		    TRUE,                               /* copy */
53 		    &cur_prot,
54 		    &max_prot,
55 		    VM_INHERIT_DEFAULT);
56 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_vm_remap(copy=TRUE)");
57 	} else {
58 		// touch 2nd page to force object creation, as COPY_SYMMETRIC
59 		*(int*)(first_layer_addr + 0x4000) = 0xabcd;
60 		// create a COW mapping
61 		vm_offset_t cow_offset = 0;
62 		mach_msg_type_number_t cow_size;
63 		kr = mach_vm_read(mach_task_self(),
64 		    first_layer_addr,
65 		    vm_size,
66 		    &cow_offset,
67 		    &cow_size);
68 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_vm_read()");
69 		cow_addr = (uint64_t)(uintptr_t)cow_offset;
70 	}
71 
72 	if (write_first) {
73 		// writing first would avoid the bug ...
74 		*(int*)first_layer_addr = 0;
75 	}
76 
77 	// trigger a zero-fill read fault on original mapping
78 	T_LOG("*(int*)first_layer_addr = 0x%x\n", *(int*)first_layer_addr);
79 	// then trigger a write fault
80 	T_LOG("write 0xbaad to *(int*)first_layer_addr");
81 	*(int*)first_layer_addr = 0xbaad;
82 	T_LOG("*(int*)cow_addr = 0x%x\n", *(int*)cow_addr);
83 
84 	if (*(int*)cow_addr != 0) {
85 		T_FAIL("COW failed for write_first=%d share_it=%d",
86 		    write_first, share_it);
87 	} else {
88 		T_PASS("COW worked for write_first=%d share_it=%d",
89 		    write_first, share_it);
90 	}
91 	return;
92 }
93 
94 T_DECL(vm_test_cow_before_zf_read, "Test COW before a zero-fill read fault")
95 {
96 	test_cow_before_zf_read(FALSE, FALSE);
97 	test_cow_before_zf_read(FALSE, TRUE);
98 	test_cow_before_zf_read(TRUE, FALSE);
99 	test_cow_before_zf_read(TRUE, TRUE);
100 }
101