1 /* 2 * Copyright (c) 2025 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 #include <darwintest.h> 30 #include <fcntl.h> 31 #include <sys/types.h> 32 #include <sys/sysctl.h> 33 #include <IOKit/IOKitLib.h> 34 #include "test_utils.h" 35 36 // Include the KSANCOV tool header 37 #include "../san/tools/ksancov.h" 38 39 T_GLOBAL_META( 40 T_META_RADAR_COMPONENT_NAME("xnu"), 41 T_META_RADAR_COMPONENT_VERSION("kasan"), 42 T_META_OWNER("a_fioraldi") 43 ); 44 45 T_DECL( 46 kcov_ksancov_mode_trace, 47 "Ensure that KSANCOV can trace program counters when the kernel is compiled with instrumentation (KSANCOV=1)", 48 // Only relevant on KASAN kernels with KCOV available 49 T_META_REQUIRES_SYSCTL_EQ("kern.kcov.available", 1), 50 // Only relevant on KASAN kernels compiled with SanitizerCoverage instrumentation 51 T_META_REQUIRES_SYSCTL_EQ("kern.kcov.sancov_compiled", 1) 52 ) { 53 int fd = ksancov_open(); 54 T_ASSERT_POSIX_SUCCESS(fd, "ksancov_open"); 55 56 size_t max_entries = 64UL * 1024; 57 int ret = ksancov_mode_trace(fd, max_entries); 58 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_mode_trace"); 59 60 uintptr_t addr; 61 size_t sz; 62 ret = ksancov_map(fd, &addr, &sz); 63 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_map"); 64 65 ksancov_trace_t *trace = (ksancov_trace_t *)addr; 66 67 ret = ksancov_thread_self(fd); 68 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_thread_self"); 69 70 ksancov_reset(trace); 71 ksancov_start(trace); 72 73 // trace the getppid syscall 74 getppid(); 75 76 ksancov_stop(trace); 77 78 // ksancov_trace_* contain useful assertions 79 size_t head = ksancov_trace_head(trace); 80 T_EXPECT_GT(head, (size_t)0, "Expected to have at least 1 program counter in the coverage trace"); 81 if (head > 0) { 82 ksancov_trace_entry(trace, 0); 83 } 84 85 ret = close(fd); 86 T_ASSERT_POSIX_SUCCESS(ret, "close"); 87 } 88 89 T_DECL( 90 kcov_ksancov_mode_trace_kext, 91 "Ensure that KSANCOV can trace program counters of a kext bundle", 92 // Only relevant on KASAN kernels with KCOV available 93 T_META_REQUIRES_SYSCTL_EQ("kern.kcov.available", 1), 94 // Can enable coverage collection of KEXTs only when on demand KSANCOV is enabled 95 T_META_REQUIRES_SYSCTL_EQ("kern.kcov.od.support_enabled", 1), 96 // Enable this test only on iPhone rdar://155524478 97 T_META_ENABLED(TARGET_OS_IOS && !TARGET_OS_MACCATALYST && !TARGET_OS_SIMULATOR) 98 ) { 99 io_service_t io_surface = IO_OBJECT_NULL; 100 io_surface = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot")); 101 T_ASSERT_NE(io_surface, IO_OBJECT_NULL, "Expected to find the IOSurface service"); 102 103 int fd = ksancov_open(); 104 T_ASSERT_POSIX_SUCCESS(fd, "ksancov_open"); 105 106 size_t max_entries = 64UL * 1024; 107 int ret = ksancov_mode_trace(fd, max_entries); 108 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_mode_trace"); 109 110 uintptr_t addr; 111 size_t sz; 112 ret = ksancov_map(fd, &addr, &sz); 113 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_map"); 114 115 ksancov_trace_t *trace = (ksancov_trace_t *)addr; 116 117 ret = ksancov_thread_self(fd); 118 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_thread_self"); 119 120 ret = ksancov_on_demand_set_gate(fd, "com.apple.iokit.IOSurface", true); 121 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_on_demand_set_gate"); 122 ksancov_reset(trace); 123 ksancov_start(trace); 124 125 // trace the IOSurface IOServiceOpen/Close 126 io_connect_t connect = IO_OBJECT_NULL; 127 IOReturn ioret; 128 ioret = IOServiceOpen(io_surface, mach_task_self(), 0, &connect); 129 T_EXPECT_EQ(ioret, kIOReturnSuccess, "Unable to open the IOSurface userclient"); 130 if (ioret == kIOReturnSuccess) { 131 IOServiceClose(connect); 132 } 133 134 ksancov_stop(trace); 135 ret = ksancov_on_demand_set_gate(fd, "com.apple.iokit.IOSurface", false); 136 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_on_demand_set_gate"); 137 138 // ksancov_trace_* contain useful assertions 139 size_t head = ksancov_trace_head(trace); 140 T_EXPECT_GT(head, (size_t)0, "Expected to have at least 1 program counter in the coverage trace"); 141 if (head > 0) { 142 ksancov_trace_entry(trace, 0); 143 } 144 145 ret = close(fd); 146 T_ASSERT_POSIX_SUCCESS(ret, "close"); 147 } 148 149 T_DECL( 150 kcov_ksancov_mode_trace_cmp_kext, 151 "Ensure that KSANCOV can trace program counters and comparisons of a kext bundle", 152 // Only relevant on KASAN kernels with KCOV available 153 T_META_REQUIRES_SYSCTL_EQ("kern.kcov.available", 1), 154 // Can enable coverage collection of KEXTs only when on demand KSANCOV is enabled 155 T_META_REQUIRES_SYSCTL_EQ("kern.kcov.od.support_enabled", 1), 156 // Enable this test only on iPhone rdar://155524478 157 T_META_ENABLED(TARGET_OS_IOS && !TARGET_OS_MACCATALYST && !TARGET_OS_SIMULATOR) 158 ) { 159 io_service_t io_surface = IO_OBJECT_NULL; 160 io_surface = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot")); 161 T_ASSERT_NE(io_surface, IO_OBJECT_NULL, "Expected to find the IOSurface service"); 162 163 int fd = ksancov_open(); 164 T_ASSERT_POSIX_SUCCESS(fd, "ksancov_open"); 165 166 size_t max_entries = 64UL * 1024; 167 int ret = ksancov_mode_trace(fd, max_entries); 168 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_mode_trace"); 169 170 uintptr_t addr; 171 size_t sz; 172 ret = ksancov_map(fd, &addr, &sz); 173 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_map"); 174 175 ksancov_trace_t *trace = (ksancov_trace_t *)addr; 176 177 ret = ksancov_cmps_mode_trace(fd, max_entries, true); 178 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_cmps_mode_trace"); 179 180 ret = ksancov_cmps_map(fd, &addr, &sz); 181 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_cmps_map"); 182 183 ksancov_trace_t *cmps_trace = (ksancov_trace_t *)addr; 184 185 ret = ksancov_thread_self(fd); 186 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_thread_self"); 187 188 ret = ksancov_on_demand_set_gate(fd, "com.apple.iokit.IOSurface", true); 189 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_on_demand_set_gate"); 190 ksancov_reset(trace); 191 ksancov_reset(cmps_trace); 192 ksancov_start(trace); 193 ksancov_start(cmps_trace); 194 195 // trace the IOSurface IOServiceOpen/Close 196 io_connect_t connect = IO_OBJECT_NULL; 197 IOReturn ioret; 198 ioret = IOServiceOpen(io_surface, mach_task_self(), 0, &connect); 199 T_EXPECT_EQ(ioret, kIOReturnSuccess, "Unable to open the IOSurface userclient"); 200 if (ioret == kIOReturnSuccess) { 201 IOServiceClose(connect); 202 } 203 204 ksancov_stop(trace); 205 ksancov_stop(cmps_trace); 206 ret = ksancov_on_demand_set_gate(fd, "com.apple.iokit.IOSurface", false); 207 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_on_demand_set_gate"); 208 209 // ksancov_trace_* contain useful assertions 210 size_t head = ksancov_trace_head(trace); 211 T_EXPECT_GT(head, (size_t)0, "Expected to have at least 1 program counter in the coverage trace"); 212 if (head > 0) { 213 ksancov_trace_entry(trace, 0); 214 } 215 216 head = ksancov_trace_head(cmps_trace); 217 T_EXPECT_GT(head, (size_t)0, "Expected to have at least 1 comparison in the comparisons trace"); 218 219 size_t i; 220 for (i = 0; i < head;) { 221 ksancov_cmps_trace_ent_t *entry = ksancov_cmps_trace_entry(cmps_trace, i); 222 if (KCOV_CMP_IS_FUNC(entry->type)) { 223 size_t space = ksancov_cmps_trace_func_space(entry->len1_func, entry->len2_func); 224 i += space / sizeof(ksancov_cmps_trace_ent_t); 225 } else { 226 ++i; 227 } 228 } 229 T_EXPECT_EQ(i, head, "Cursor went past the end of the trace"); 230 231 ret = close(fd); 232 T_ASSERT_POSIX_SUCCESS(ret, "close"); 233 } 234 235 T_DECL( 236 kcov_ksancov_testcases, 237 "Test the testcases buffer feature in KSANCOV", 238 // Only relevant on KASAN kernels with KCOV available 239 T_META_REQUIRES_SYSCTL_EQ("kern.kcov.available", 1) 240 ) { 241 int fd = ksancov_open(); 242 if (fd < 0) { 243 T_SKIP("Ksancov not available, skipping..."); 244 return; 245 } 246 247 size_t max_entries = 32; 248 int ret = ksancov_mode_trace(fd, max_entries); 249 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_mode_trace"); 250 251 size_t num_testcases = 2; 252 ret = ksancov_testcases(fd, num_testcases); 253 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_testcases"); 254 255 uintptr_t addr; 256 size_t sz; 257 ret = ksancov_testcases_map(fd, &addr, &sz); 258 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_testcases_map"); 259 260 T_EXPECT_EQ(sz, sizeof(ksancov_serialized_testcases_t) + sizeof(ksancov_serialized_testcase_t) * num_testcases, "Testcases mapped size mismatch"); 261 ksancov_serialized_testcases_t *testcases = (ksancov_serialized_testcases_t *)addr; 262 263 ret = ksancov_thread_self(fd); 264 T_ASSERT_POSIX_SUCCESS(ret, "ksancov_thread_self"); 265 266 T_ASSERT_EQ(sizeof(testcases->list[testcases->head].buffer), (size_t)KSANCOV_SERIALIZED_TESTCASE_BYTES, "Buffer size not KSANCOV_SERIALIZED_TESTCASE_BYTES"); 267 testcases->list[testcases->head].buffer[0] = 'f'; 268 testcases->list[testcases->head].buffer[1] = 'o'; 269 testcases->list[testcases->head].buffer[2] = 'o'; 270 testcases->list[testcases->head].buffer[3] = 0; 271 testcases->list[testcases->head].size = 4; 272 testcases->head = (testcases->head + 1) % num_testcases; 273 274 ret = ksancov_testcases_log(fd); 275 // The serial log can fail if serial=0x1 or debug=0x2 are not set in the boot args 276 277 ret = close(fd); 278 T_ASSERT_POSIX_SUCCESS(ret, "close"); 279 } 280