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/param.h>
33 #include <sys/ioctl.h>
34 #include <sys/kcov.h>
35 #include <sys/mman.h>
36
37 #include <machine/atomic.h>
38
39 #include <fcntl.h>
40 #include <pthread.h>
41 #include <semaphore.h>
42
43 #include <atf-c.h>
44
45 static const char *modes[] = {
46 "PC tracing",
47 "comparison tracing",
48 };
49
50 static size_t page_size;
51
52 static void
init_page_size(void)53 init_page_size(void)
54 {
55 page_size = getpagesize();
56 }
57
58 static int
open_kcov(void)59 open_kcov(void)
60 {
61 int fd;
62
63 fd = open("/dev/kcov", O_RDWR);
64 if (fd == -1)
65 atf_tc_skip("Failed to open /dev/kcov");
66
67 return (fd);
68 }
69
70 ATF_TC(kcov_bufsize);
ATF_TC_HEAD(kcov_bufsize,tc)71 ATF_TC_HEAD(kcov_bufsize, tc)
72 {
73 init_page_size();
74 }
75
ATF_TC_BODY(kcov_bufsize,tc)76 ATF_TC_BODY(kcov_bufsize, tc)
77 {
78 int fd;
79
80 fd = open_kcov();
81
82 ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 0) == -1);
83 ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 1) == -1);
84 ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == 0);
85 ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == -1);
86
87 close(fd);
88 }
89
90 ATF_TC(kcov_mmap);
ATF_TC_HEAD(kcov_mmap,tc)91 ATF_TC_HEAD(kcov_mmap, tc)
92 {
93 init_page_size();
94 }
95
ATF_TC_BODY(kcov_mmap,tc)96 ATF_TC_BODY(kcov_mmap, tc)
97 {
98 void *data1, *data2;
99 int fd;
100
101 fd = open_kcov();
102
103 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
104 fd, 0) == MAP_FAILED);
105
106 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE,
107 2 * page_size / KCOV_ENTRY_SIZE) == 0);
108
109 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
110 fd, 0) == MAP_FAILED);
111 ATF_CHECK(mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
112 fd, 0) == MAP_FAILED);
113 ATF_REQUIRE((data1 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
114 MAP_SHARED, fd, 0)) != MAP_FAILED);
115 ATF_REQUIRE((data2 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
116 MAP_SHARED, fd, 0)) != MAP_FAILED);
117
118 *(uint64_t *)data1 = 0x123456789abcdeful;
119 ATF_REQUIRE(*(uint64_t *)data2 == 0x123456789abcdefull);
120 *(uint64_t *)data2 = 0xfedcba9876543210ul;
121 ATF_REQUIRE(*(uint64_t *)data1 == 0xfedcba9876543210ull);
122
123 munmap(data1, 2 * page_size);
124 munmap(data2, 2 * page_size);
125
126 close(fd);
127 }
128
129 /* This shouldn't panic */
130 ATF_TC(kcov_mmap_no_munmap);
ATF_TC_HEAD(kcov_mmap_no_munmap,tc)131 ATF_TC_HEAD(kcov_mmap_no_munmap, tc)
132 {
133 init_page_size();
134 }
135
ATF_TC_BODY(kcov_mmap_no_munmap,tc)136 ATF_TC_BODY(kcov_mmap_no_munmap, tc)
137 {
138 int fd;
139
140 fd = open_kcov();
141
142 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
143
144 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
145 fd, 0) != MAP_FAILED);
146
147 close(fd);
148 }
149
150 ATF_TC(kcov_mmap_no_munmap_no_close);
ATF_TC_HEAD(kcov_mmap_no_munmap_no_close,tc)151 ATF_TC_HEAD(kcov_mmap_no_munmap_no_close, tc)
152 {
153 init_page_size();
154 }
155
ATF_TC_BODY(kcov_mmap_no_munmap_no_close,tc)156 ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc)
157 {
158 int fd;
159
160 fd = open_kcov();
161
162 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
163
164 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
165 fd, 0) != MAP_FAILED);
166 }
167
168 static sem_t sem1, sem2;
169
170 static void *
kcov_mmap_enable_thread(void * data)171 kcov_mmap_enable_thread(void *data)
172 {
173 int fd;
174
175 fd = open_kcov();
176 *(int *)data = fd;
177
178 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
179 ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
180 fd, 0) != MAP_FAILED);
181 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
182
183 sem_post(&sem1);
184 sem_wait(&sem2);
185
186 return (NULL);
187 }
188
189 ATF_TC(kcov_mmap_enable_thread_close);
ATF_TC_HEAD(kcov_mmap_enable_thread_close,tc)190 ATF_TC_HEAD(kcov_mmap_enable_thread_close, tc)
191 {
192 init_page_size();
193 }
194
ATF_TC_BODY(kcov_mmap_enable_thread_close,tc)195 ATF_TC_BODY(kcov_mmap_enable_thread_close, tc)
196 {
197 pthread_t thread;
198 int fd;
199
200 sem_init(&sem1, 0, 0);
201 sem_init(&sem2, 0, 0);
202 pthread_create(&thread, NULL,
203 kcov_mmap_enable_thread, &fd);
204 sem_wait(&sem1);
205 close(fd);
206 sem_post(&sem2);
207 pthread_join(thread, NULL);
208 }
209
210 ATF_TC(kcov_enable);
ATF_TC_HEAD(kcov_enable,tc)211 ATF_TC_HEAD(kcov_enable, tc)
212 {
213 init_page_size();
214 }
215
ATF_TC_BODY(kcov_enable,tc)216 ATF_TC_BODY(kcov_enable, tc)
217 {
218 int fd;
219
220 fd = open_kcov();
221
222 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
223
224 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
225
226 /* We need to enable before disable */
227 ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
228
229 /* Check enabling works only with a valid trace method */
230 ATF_CHECK(ioctl(fd, KIOENABLE, -1) == -1);
231 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
232 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
233 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == -1);
234
235 /* Disable should only be called once */
236 ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
237 ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
238
239 /* Re-enabling should also work */
240 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == 0);
241 ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
242
243 close(fd);
244 }
245
246 ATF_TC(kcov_enable_no_disable);
ATF_TC_HEAD(kcov_enable_no_disable,tc)247 ATF_TC_HEAD(kcov_enable_no_disable, tc)
248 {
249 init_page_size();
250 }
251
ATF_TC_BODY(kcov_enable_no_disable,tc)252 ATF_TC_BODY(kcov_enable_no_disable, tc)
253 {
254 int fd;
255
256 fd = open_kcov();
257 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
258 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
259 close(fd);
260 }
261
262 ATF_TC(kcov_enable_no_disable_no_close);
ATF_TC_HEAD(kcov_enable_no_disable_no_close,tc)263 ATF_TC_HEAD(kcov_enable_no_disable_no_close, tc)
264 {
265 init_page_size();
266 }
267
ATF_TC_BODY(kcov_enable_no_disable_no_close,tc)268 ATF_TC_BODY(kcov_enable_no_disable_no_close, tc)
269 {
270 int fd;
271
272 fd = open_kcov();
273 ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
274 ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
275 }
276
277 static void *
common_head(int * fdp)278 common_head(int *fdp)
279 {
280 void *data;
281 int fd;
282
283 fd = open_kcov();
284
285 ATF_REQUIRE_MSG(ioctl(fd, KIOSETBUFSIZE,
286 page_size / KCOV_ENTRY_SIZE) == 0,
287 "Unable to set the kcov buffer size");
288
289 data = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
290 ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer");
291
292 *fdp = fd;
293 return (data);
294 }
295
296 static void
common_tail(int fd,void * data)297 common_tail(int fd, void *data)
298 {
299
300 ATF_REQUIRE_MSG(munmap(data, page_size) == 0,
301 "Unable to unmap the kcov buffer");
302
303 close(fd);
304 }
305
306 static void
basic_test(u_int mode)307 basic_test(u_int mode)
308 {
309 uint64_t *buf;
310 int fd;
311
312 buf = common_head(&fd);
313 ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
314 "Unable to enable kcov %s",
315 mode < nitems(modes) ? modes[mode] : "unknown mode");
316
317 atomic_store_64(&buf[0], 0);
318
319 sleep(0);
320 ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) != 0, "No records found");
321
322 ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
323 "Unable to disable kcov");
324
325 common_tail(fd, buf);
326 }
327
328 ATF_TC(kcov_basic_pc);
ATF_TC_HEAD(kcov_basic_pc,tc)329 ATF_TC_HEAD(kcov_basic_pc, tc)
330 {
331 init_page_size();
332 }
333
ATF_TC_BODY(kcov_basic_pc,tc)334 ATF_TC_BODY(kcov_basic_pc, tc)
335 {
336 basic_test(KCOV_MODE_TRACE_PC);
337 }
338
339 ATF_TC(kcov_basic_cmp);
ATF_TC_HEAD(kcov_basic_cmp,tc)340 ATF_TC_HEAD(kcov_basic_cmp, tc)
341 {
342 init_page_size();
343 }
344
ATF_TC_BODY(kcov_basic_cmp,tc)345 ATF_TC_BODY(kcov_basic_cmp, tc)
346 {
347 basic_test(KCOV_MODE_TRACE_CMP);
348 }
349
350 static void *
thread_test_helper(void * ptr)351 thread_test_helper(void *ptr)
352 {
353 uint64_t *buf = ptr;
354
355 atomic_store_64(&buf[0], 0);
356 sleep(0);
357 ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) == 0,
358 "Records changed in blocked thread");
359
360 return (NULL);
361 }
362
363 static void
thread_test(u_int mode)364 thread_test(u_int mode)
365 {
366 pthread_t thread;
367 uint64_t *buf;
368 int fd;
369
370 buf = common_head(&fd);
371
372 ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
373 "Unable to enable kcov %s",
374 mode < nitems(modes) ? modes[mode] : "unknown mode");
375
376 pthread_create(&thread, NULL, thread_test_helper, buf);
377 pthread_join(thread, NULL);
378
379 ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
380 "Unable to disable kcov");
381
382 common_tail(fd, buf);
383 }
384
385 ATF_TC(kcov_thread_pc);
ATF_TC_HEAD(kcov_thread_pc,tc)386 ATF_TC_HEAD(kcov_thread_pc, tc)
387 {
388 init_page_size();
389 }
390
ATF_TC_BODY(kcov_thread_pc,tc)391 ATF_TC_BODY(kcov_thread_pc, tc)
392 {
393 thread_test(KCOV_MODE_TRACE_PC);
394 }
395
396 ATF_TC(kcov_thread_cmp);
ATF_TC_HEAD(kcov_thread_cmp,tc)397 ATF_TC_HEAD(kcov_thread_cmp, tc)
398 {
399 init_page_size();
400 }
401
ATF_TC_BODY(kcov_thread_cmp,tc)402 ATF_TC_BODY(kcov_thread_cmp, tc)
403 {
404 thread_test(KCOV_MODE_TRACE_CMP);
405 }
406
407 struct multi_thread_data {
408 uint64_t *buf;
409 int fd;
410 u_int mode;
411 int thread;
412 };
413
414 static void *
multi_thread_test_helper(void * ptr)415 multi_thread_test_helper(void *ptr)
416 {
417 struct multi_thread_data *data = ptr;
418
419 ATF_REQUIRE_MSG(ioctl(data->fd, KIOENABLE, data->mode) == 0,
420 "Unable to enable kcov %s in thread %d",
421 data->mode < nitems(modes) ? modes[data->mode] : "unknown mode",
422 data->thread);
423
424 atomic_store_64(&data->buf[0], 0);
425 sleep(0);
426 ATF_REQUIRE_MSG(atomic_load_64(&data->buf[0]) != 0,
427 "No records found in thread %d", data->thread);
428
429 return (NULL);
430 }
431
432 ATF_TC(kcov_enable_multi_thread);
ATF_TC_HEAD(kcov_enable_multi_thread,t)433 ATF_TC_HEAD(kcov_enable_multi_thread, t)
434 {
435 init_page_size();
436 }
437
ATF_TC_BODY(kcov_enable_multi_thread,t)438 ATF_TC_BODY(kcov_enable_multi_thread, t)
439 {
440 struct multi_thread_data data;
441 pthread_t thread;
442
443 data.buf = common_head(&data.fd);
444
445 /* Run the thread to completion */
446 data.thread = 1;
447 data.mode = KCOV_MODE_TRACE_PC;
448 pthread_create(&thread, NULL, multi_thread_test_helper, &data);
449 pthread_join(thread, NULL);
450
451 /* Run it again to check enable works on the same fd */
452 data.thread = 2;
453 data.mode = KCOV_MODE_TRACE_CMP;
454 pthread_create(&thread, NULL, multi_thread_test_helper, &data);
455 pthread_join(thread, NULL);
456
457 common_tail(data.fd, data.buf);
458 }
459
ATF_TP_ADD_TCS(tp)460 ATF_TP_ADD_TCS(tp)
461 {
462
463 ATF_TP_ADD_TC(tp, kcov_bufsize);
464 ATF_TP_ADD_TC(tp, kcov_mmap);
465 ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap);
466 ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close);
467 ATF_TP_ADD_TC(tp, kcov_enable);
468 ATF_TP_ADD_TC(tp, kcov_enable_no_disable);
469 ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close);
470 ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close);
471 ATF_TP_ADD_TC(tp, kcov_basic_pc);
472 ATF_TP_ADD_TC(tp, kcov_basic_cmp);
473 ATF_TP_ADD_TC(tp, kcov_thread_pc);
474 ATF_TP_ADD_TC(tp, kcov_thread_cmp);
475 ATF_TP_ADD_TC(tp, kcov_enable_multi_thread);
476 return (atf_no_error());
477 }
478