1 #include <darwintest.h>
3 #include <sys/sysctl.h>
10 #include "excserver.h"
11 #include "exc_helpers.h"
13 extern int pid_hibernate(int pid
);
15 static vm_address_t page_size
;
18 T_META_REQUIRES_SYSCTL_EQ("hw.optional.wkdm_popcount", 1)
24 T_ASSERT_POSIX_SUCCESS(pid_hibernate(-2), NULL
);
25 T_ASSERT_POSIX_SUCCESS(pid_hibernate(-2), NULL
);
29 dirty_page(const vm_address_t address
)
31 assert((address
& (page_size
- 1)) == 0UL);
32 uint32_t *const page_as_u32
= (uint32_t *)address
;
33 for (uint32_t i
= 0; i
< page_size
/ sizeof(uint32_t); i
+= 2) {
34 page_as_u32
[i
+ 0] = i
% 4;
35 page_as_u32
[i
+ 1] = 0xcdcdcdcd;
40 try_to_corrupt_page(vm_address_t page_va
)
43 size_t size
= sizeof(val
);
44 int result
= sysctlbyname("vm.compressor_inject_error", &val
, &size
,
45 &page_va
, sizeof(page_va
));
50 create_corrupted_region(const vm_address_t buffer_length
)
52 void *const bufferp
= malloc(buffer_length
);
53 T_ASSERT_NOTNULL(bufferp
, "allocated test buffer");
54 const vm_address_t buffer
= (vm_address_t
)bufferp
;
56 T_LOG("buffer address: %lx\n", (unsigned long)buffer
);
58 for (size_t buffer_offset
= 0; buffer_offset
< buffer_length
;
59 buffer_offset
+= page_size
) {
60 dirty_page(buffer
+ buffer_offset
);
66 for (size_t buffer_offset
= 0; buffer_offset
< buffer_length
;
67 buffer_offset
+= page_size
) {
68 if (try_to_corrupt_page(buffer
+ buffer_offset
)) {
73 T_LOG("corrupted %u/%lu pages. accessing...\n", corrupt
,
74 (unsigned long)(buffer_length
/ page_size
));
76 T_SKIP("no pages corrupted");
83 try_write(volatile uint32_t *word __unused
)
90 : "+r"(val
) : "m"(*word
));
91 // The exception handler skips over the instruction that zeroes val when a
92 // decompression failure is detected.
100 run_test(vm_address_t buffer_start
, vm_address_t buffer_length
)
103 for (size_t buffer_offset
= 0; buffer_offset
< buffer_length
;
104 buffer_offset
+= page_size
) {
105 // Access pages until the fault is detected.
106 if (!try_write((volatile uint32_t *)(buffer_start
+
108 T_LOG("test_thread breaking");
117 T_LOG("test thread completing");
122 kern_memory_failure_handler(
123 exception_type_t exception
,
124 mach_exception_data_t code
)
126 T_EXPECT_EQ(exception
, EXC_BAD_ACCESS
,
127 "Verified bad address exception");
128 T_EXPECT_EQ((int)code
[0], KERN_MEMORY_FAILURE
, "caught KERN_MEMORY_FAILURE");
129 T_PASS("received KERN_MEMORY_FAILURE from test thread");
130 // Skip the next instruction as well so that the faulting code can detect
136 run_test_expect_fault()
138 mach_port_t exc_port
= create_exception_port(EXC_MASK_BAD_ACCESS
);
139 vm_address_t buffer_length
= 10 * 1024ULL * 1024ULL;
140 vm_address_t buffer_start
= create_corrupted_region(buffer_length
);
142 run_exception_handler(exc_port
, kern_memory_failure_handler
);
143 run_test(buffer_start
, buffer_length
);
144 free((void *)buffer_start
);
149 T_DECL(decompression_failure
,
150 "Confirm that exception is raised on decompression failure",
151 // Disable software checks in development builds, as these would result in
153 T_META_BOOTARGS_SET("vm_compressor_validation=0"))
155 if (pid_hibernate(-2) != 0) {
156 T_SKIP("compressor not active");
160 size_t size
= sizeof(value
);
161 if (sysctlbyname("vm.compressor_inject_error", &value
, &size
, NULL
, 0)
163 T_SKIP("vm.compressor_inject_error not present");
166 T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.pagesize", &value
, &size
, NULL
, 0),
168 T_ASSERT_EQ_ULONG(size
, sizeof(value
), NULL
);
169 page_size
= (vm_address_t
)value
;
171 run_test_expect_fault();