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", T_META_TAG_VM_PREFERRED)
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