1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2018, 2019 Andrew Turner 5 * 6 * This software was developed by SRI International and the University of 7 * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 8 * ("CTSRD"), as part of the DARPA CRASH research programme. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #include <sys/param.h> 34 #include <sys/ioctl.h> 35 #include <sys/kcov.h> 36 #include <sys/mman.h> 37 38 #include <machine/atomic.h> 39 40 #include <fcntl.h> 41 #include <pthread.h> 42 #include <semaphore.h> 43 44 #include <atf-c.h> 45 46 static const char *modes[] = { 47 "PC tracing", 48 "comparison tracing", 49 }; 50 51 static size_t page_size; 52 53 static void 54 init_page_size(void) 55 { 56 page_size = getpagesize(); 57 } 58 59 static int 60 open_kcov(void) 61 { 62 int fd; 63 64 fd = open("/dev/kcov", O_RDWR); 65 if (fd == -1) 66 atf_tc_skip("Failed to open /dev/kcov"); 67 68 return (fd); 69 } 70 71 ATF_TC(kcov_bufsize); 72 ATF_TC_HEAD(kcov_bufsize, tc) 73 { 74 init_page_size(); 75 } 76 77 ATF_TC_BODY(kcov_bufsize, tc) 78 { 79 int fd; 80 81 fd = open_kcov(); 82 83 ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 0) == -1); 84 ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 1) == -1); 85 ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == 0); 86 ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == -1); 87 88 close(fd); 89 } 90 91 ATF_TC(kcov_mmap); 92 ATF_TC_HEAD(kcov_mmap, tc) 93 { 94 init_page_size(); 95 } 96 97 ATF_TC_BODY(kcov_mmap, tc) 98 { 99 void *data1, *data2; 100 int fd; 101 102 fd = open_kcov(); 103 104 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, 105 fd, 0) == MAP_FAILED); 106 107 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, 108 2 * page_size / KCOV_ENTRY_SIZE) == 0); 109 110 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, 111 fd, 0) == MAP_FAILED); 112 ATF_CHECK(mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED, 113 fd, 0) == MAP_FAILED); 114 ATF_REQUIRE((data1 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 115 MAP_SHARED, fd, 0)) != MAP_FAILED); 116 ATF_REQUIRE((data2 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 117 MAP_SHARED, fd, 0)) != MAP_FAILED); 118 119 *(uint64_t *)data1 = 0x123456789abcdeful; 120 ATF_REQUIRE(*(uint64_t *)data2 == 0x123456789abcdefull); 121 *(uint64_t *)data2 = 0xfedcba9876543210ul; 122 ATF_REQUIRE(*(uint64_t *)data1 == 0xfedcba9876543210ull); 123 124 munmap(data1, 2 * page_size); 125 munmap(data2, 2 * page_size); 126 127 close(fd); 128 } 129 130 /* This shouldn't panic */ 131 ATF_TC(kcov_mmap_no_munmap); 132 ATF_TC_HEAD(kcov_mmap_no_munmap, tc) 133 { 134 init_page_size(); 135 } 136 137 ATF_TC_BODY(kcov_mmap_no_munmap, tc) 138 { 139 int fd; 140 141 fd = open_kcov(); 142 143 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0); 144 145 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, 146 fd, 0) != MAP_FAILED); 147 148 close(fd); 149 } 150 151 ATF_TC(kcov_mmap_no_munmap_no_close); 152 ATF_TC_HEAD(kcov_mmap_no_munmap_no_close, tc) 153 { 154 init_page_size(); 155 } 156 157 ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc) 158 { 159 int fd; 160 161 fd = open_kcov(); 162 163 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0); 164 165 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, 166 fd, 0) != MAP_FAILED); 167 } 168 169 static sem_t sem1, sem2; 170 171 static void * 172 kcov_mmap_enable_thread(void *data) 173 { 174 int fd; 175 176 fd = open_kcov(); 177 *(int *)data = fd; 178 179 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0); 180 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, 181 fd, 0) != MAP_FAILED); 182 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0); 183 184 sem_post(&sem1); 185 sem_wait(&sem2); 186 187 return (NULL); 188 } 189 190 ATF_TC(kcov_mmap_enable_thread_close); 191 ATF_TC_HEAD(kcov_mmap_enable_thread_close, tc) 192 { 193 init_page_size(); 194 } 195 196 ATF_TC_BODY(kcov_mmap_enable_thread_close, tc) 197 { 198 pthread_t thread; 199 int fd; 200 201 sem_init(&sem1, 0, 0); 202 sem_init(&sem2, 0, 0); 203 pthread_create(&thread, NULL, 204 kcov_mmap_enable_thread, &fd); 205 sem_wait(&sem1); 206 close(fd); 207 sem_post(&sem2); 208 pthread_join(thread, NULL); 209 } 210 211 ATF_TC(kcov_enable); 212 ATF_TC_HEAD(kcov_enable, tc) 213 { 214 init_page_size(); 215 } 216 217 ATF_TC_BODY(kcov_enable, tc) 218 { 219 int fd; 220 221 fd = open_kcov(); 222 223 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1); 224 225 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0); 226 227 /* We need to enable before disable */ 228 ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1); 229 230 /* Check enabling works only with a valid trace method */ 231 ATF_CHECK(ioctl(fd, KIOENABLE, -1) == -1); 232 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0); 233 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1); 234 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == -1); 235 236 /* Disable should only be called once */ 237 ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0); 238 ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1); 239 240 /* Re-enabling should also work */ 241 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == 0); 242 ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0); 243 244 close(fd); 245 } 246 247 ATF_TC(kcov_enable_no_disable); 248 ATF_TC_HEAD(kcov_enable_no_disable, tc) 249 { 250 init_page_size(); 251 } 252 253 ATF_TC_BODY(kcov_enable_no_disable, tc) 254 { 255 int fd; 256 257 fd = open_kcov(); 258 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0); 259 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0); 260 close(fd); 261 } 262 263 ATF_TC(kcov_enable_no_disable_no_close); 264 ATF_TC_HEAD(kcov_enable_no_disable_no_close, tc) 265 { 266 init_page_size(); 267 } 268 269 ATF_TC_BODY(kcov_enable_no_disable_no_close, tc) 270 { 271 int fd; 272 273 fd = open_kcov(); 274 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0); 275 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0); 276 } 277 278 static void * 279 common_head(int *fdp) 280 { 281 void *data; 282 int fd; 283 284 fd = open_kcov(); 285 286 ATF_REQUIRE_MSG(ioctl(fd, KIOSETBUFSIZE, 287 page_size / KCOV_ENTRY_SIZE) == 0, 288 "Unable to set the kcov buffer size"); 289 290 data = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 291 ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer"); 292 293 *fdp = fd; 294 return (data); 295 } 296 297 static void 298 common_tail(int fd, void *data) 299 { 300 301 ATF_REQUIRE_MSG(munmap(data, page_size) == 0, 302 "Unable to unmap the kcov buffer"); 303 304 close(fd); 305 } 306 307 static void 308 basic_test(u_int mode) 309 { 310 uint64_t *buf; 311 int fd; 312 313 buf = common_head(&fd); 314 ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0, 315 "Unable to enable kcov %s", 316 mode < nitems(modes) ? modes[mode] : "unknown mode"); 317 318 atomic_store_64(&buf[0], 0); 319 320 sleep(0); 321 ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) != 0, "No records found"); 322 323 ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0, 324 "Unable to disable kcov"); 325 326 common_tail(fd, buf); 327 } 328 329 ATF_TC(kcov_basic_pc); 330 ATF_TC_HEAD(kcov_basic_pc, tc) 331 { 332 init_page_size(); 333 } 334 335 ATF_TC_BODY(kcov_basic_pc, tc) 336 { 337 basic_test(KCOV_MODE_TRACE_PC); 338 } 339 340 ATF_TC(kcov_basic_cmp); 341 ATF_TC_HEAD(kcov_basic_cmp, tc) 342 { 343 init_page_size(); 344 } 345 346 ATF_TC_BODY(kcov_basic_cmp, tc) 347 { 348 basic_test(KCOV_MODE_TRACE_CMP); 349 } 350 351 static void * 352 thread_test_helper(void *ptr) 353 { 354 uint64_t *buf = ptr; 355 356 atomic_store_64(&buf[0], 0); 357 sleep(0); 358 ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) == 0, 359 "Records changed in blocked thread"); 360 361 return (NULL); 362 } 363 364 static void 365 thread_test(u_int mode) 366 { 367 pthread_t thread; 368 uint64_t *buf; 369 int fd; 370 371 buf = common_head(&fd); 372 373 ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0, 374 "Unable to enable kcov %s", 375 mode < nitems(modes) ? modes[mode] : "unknown mode"); 376 377 pthread_create(&thread, NULL, thread_test_helper, buf); 378 pthread_join(thread, NULL); 379 380 ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0, 381 "Unable to disable kcov"); 382 383 common_tail(fd, buf); 384 } 385 386 ATF_TC(kcov_thread_pc); 387 ATF_TC_HEAD(kcov_thread_pc, tc) 388 { 389 init_page_size(); 390 } 391 392 ATF_TC_BODY(kcov_thread_pc, tc) 393 { 394 thread_test(KCOV_MODE_TRACE_PC); 395 } 396 397 ATF_TC(kcov_thread_cmp); 398 ATF_TC_HEAD(kcov_thread_cmp, tc) 399 { 400 init_page_size(); 401 } 402 403 ATF_TC_BODY(kcov_thread_cmp, tc) 404 { 405 thread_test(KCOV_MODE_TRACE_CMP); 406 } 407 408 struct multi_thread_data { 409 uint64_t *buf; 410 int fd; 411 u_int mode; 412 int thread; 413 }; 414 415 static void * 416 multi_thread_test_helper(void *ptr) 417 { 418 struct multi_thread_data *data = ptr; 419 420 ATF_REQUIRE_MSG(ioctl(data->fd, KIOENABLE, data->mode) == 0, 421 "Unable to enable kcov %s in thread %d", 422 data->mode < nitems(modes) ? modes[data->mode] : "unknown mode", 423 data->thread); 424 425 atomic_store_64(&data->buf[0], 0); 426 sleep(0); 427 ATF_REQUIRE_MSG(atomic_load_64(&data->buf[0]) != 0, 428 "No records found in thread %d", data->thread); 429 430 return (NULL); 431 } 432 433 ATF_TC(kcov_enable_multi_thread); 434 ATF_TC_HEAD(kcov_enable_multi_thread, t) 435 { 436 init_page_size(); 437 } 438 439 ATF_TC_BODY(kcov_enable_multi_thread, t) 440 { 441 struct multi_thread_data data; 442 pthread_t thread; 443 444 data.buf = common_head(&data.fd); 445 446 /* Run the thread to completion */ 447 data.thread = 1; 448 data.mode = KCOV_MODE_TRACE_PC; 449 pthread_create(&thread, NULL, multi_thread_test_helper, &data); 450 pthread_join(thread, NULL); 451 452 /* Run it again to check enable works on the same fd */ 453 data.thread = 2; 454 data.mode = KCOV_MODE_TRACE_CMP; 455 pthread_create(&thread, NULL, multi_thread_test_helper, &data); 456 pthread_join(thread, NULL); 457 458 common_tail(data.fd, data.buf); 459 } 460 461 ATF_TP_ADD_TCS(tp) 462 { 463 464 ATF_TP_ADD_TC(tp, kcov_bufsize); 465 ATF_TP_ADD_TC(tp, kcov_mmap); 466 ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap); 467 ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close); 468 ATF_TP_ADD_TC(tp, kcov_enable); 469 ATF_TP_ADD_TC(tp, kcov_enable_no_disable); 470 ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close); 471 ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close); 472 ATF_TP_ADD_TC(tp, kcov_basic_pc); 473 ATF_TP_ADD_TC(tp, kcov_basic_cmp); 474 ATF_TP_ADD_TC(tp, kcov_thread_pc); 475 ATF_TP_ADD_TC(tp, kcov_thread_cmp); 476 ATF_TP_ADD_TC(tp, kcov_enable_multi_thread); 477 return (atf_no_error()); 478 } 479