1 /* Copyright (c) 2022 Google, LLC.
2  * SPDX-License-Identifier: Apache-2.0
3  */
4 #include <zephyr/kernel.h>
5 #include <string.h>
6 #include <zephyr/irq.h>
7 
8 /* Fuzz testing is coverage-based, so we want to hide a failure case
9  * (a write through a null pointer in this case) down inside a call
10  * tree in such a way that it would be very unlikely to be found by
11  * randomly-selected input.  But the fuzzer can still find it in
12  * linear(-ish) time by discovering each new function along the way
13  * and then probing that new space.  The 1 in 2^56 case here would
14  * require months-to-years of work for a large datacenter, but the
15  * fuzzer gets it in 20 seconds or so. This requires that the code for
16  * each case be distinguishable/instrumentable though, which is why we
17  * generate the recursive handler functions this way and disable
18  * inlining to prevent optimization.
19  */
20 int *global_null_ptr;
21 static const uint8_t key[] = { 0x9e, 0x21, 0x0c, 0x18, 0x9d, 0xd1, 0x7d };
22 bool found[ARRAY_SIZE(key)];
23 
24 #define LASTKEY (ARRAY_SIZE(key) - 1)
25 
26 #define GEN_CHECK(cur, nxt)                                                                        \
27 	void check##nxt(const uint8_t *data, size_t sz);                                           \
28 	void __attribute__((noinline)) check##cur(const uint8_t *data, size_t sz)                  \
29 	{                                                                                          \
30 		if (cur < sz && data[cur] == key[cur]) {                                           \
31 			if (!found[cur]) {                                                         \
32 				printk("#\n# Found key %d\n#\n", cur);                             \
33 				found[cur] = true;                                                 \
34 			}                                                                          \
35 			if (cur == LASTKEY) {                                                      \
36 				*global_null_ptr = 0; /* boom! */                                  \
37 			} else {                                                                   \
38 				check##nxt(data, sz);                                              \
39 			}                                                                          \
40 		}                                                                                  \
41 	}
42 
43 GEN_CHECK(0, 1)
44 GEN_CHECK(1, 2)
45 GEN_CHECK(2, 3)
46 GEN_CHECK(3, 4)
47 GEN_CHECK(4, 5)
48 GEN_CHECK(5, 6)
49 GEN_CHECK(6, 0)
50 
51 /* Fuzz input received from LLVM via "interrupt" */
52 extern const uint8_t *posix_fuzz_buf;
53 extern size_t posix_fuzz_sz;
54 
55 K_SEM_DEFINE(fuzz_sem, 0, K_SEM_MAX_LIMIT);
56 
fuzz_isr(const void * arg)57 static void fuzz_isr(const void *arg)
58 {
59 	/* We could call check0() to execute the fuzz case here, but
60 	 * pass it through to the main thread instead to get more OS
61 	 * coverage.
62 	 */
63 	k_sem_give(&fuzz_sem);
64 }
65 
main(void)66 int main(void)
67 {
68 	printk("Hello World! %s\n", CONFIG_BOARD);
69 
70 	IRQ_CONNECT(CONFIG_ARCH_POSIX_FUZZ_IRQ, 0, fuzz_isr, NULL, 0);
71 	irq_enable(CONFIG_ARCH_POSIX_FUZZ_IRQ);
72 
73 	while (true) {
74 		k_sem_take(&fuzz_sem, K_FOREVER);
75 
76 		/* Execute the fuzz case we got from LLVM and passed
77 		 * through an interrupt to this thread.
78 		 */
79 		check0(posix_fuzz_buf, posix_fuzz_sz);
80 	}
81 	return 0;
82 }
83