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