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 #include <zephyr/toolchain.h>
8 #include <irq_ctrl.h>
9 #if defined(CONFIG_BOARD_NATIVE_SIM)
10 #include <nsi_cpu_if.h>
11 #include <nsi_main_semipublic.h>
12 #elif defined(CONFIG_BOARD_NATIVE_POSIX)
13 /* Note: native_posix will be deprecated soon */
14 extern void posix_init(int argc, char *argv[]);
15 extern void posix_exec_for(uint64_t us);
16 #define nsi_init posix_init
17 #define nsi_exec_for posix_exec_for
18 #else
19 #error "Platform not supported"
20 #endif
21 
22 /* Fuzz testing is coverage-based, so we want to hide a failure case
23  * (a write through a null pointer in this case) down inside a call
24  * tree in such a way that it would be very unlikely to be found by
25  * randomly-selected input.  But the fuzzer can still find it in
26  * linear(-ish) time by discovering each new function along the way
27  * and then probing that new space.  The 1 in 2^56 case here would
28  * require months-to-years of work for a large datacenter, but the
29  * fuzzer gets it in 20 seconds or so. This requires that the code for
30  * each case be distinguishable/instrumentable though, which is why we
31  * generate the recursive handler functions this way and disable
32  * inlining to prevent optimization.
33  */
34 int *global_null_ptr;
35 static const uint8_t key[] = { 0x9e, 0x21, 0x0c, 0x18, 0x9d, 0xd1, 0x7d };
36 bool found[ARRAY_SIZE(key)];
37 
38 #define LASTKEY (ARRAY_SIZE(key) - 1)
39 
40 #define GEN_CHECK(cur, nxt)                                                                        \
41 	void check##nxt(const uint8_t *data, size_t sz);                                           \
42 	void __noinline check##cur(const uint8_t *data, size_t sz)                  \
43 	{                                                                                          \
44 		if (cur < sz && data[cur] == key[cur]) {                                           \
45 			if (!found[cur]) {                                                         \
46 				printk("#\n# Found key %d\n#\n", cur);                             \
47 				found[cur] = true;                                                 \
48 			}                                                                          \
49 			if (cur == LASTKEY) {                                                      \
50 				*global_null_ptr = 0; /* boom! */                                  \
51 			} else {                                                                   \
52 				check##nxt(data, sz);                                              \
53 			}                                                                          \
54 		}                                                                                  \
55 	}
56 
57 GEN_CHECK(0, 1)
58 GEN_CHECK(1, 2)
59 GEN_CHECK(2, 3)
60 GEN_CHECK(3, 4)
61 GEN_CHECK(4, 5)
62 GEN_CHECK(5, 6)
63 GEN_CHECK(6, 0)
64 
65 /* Fuzz input received from LLVM via "interrupt" */
66 static const uint8_t *fuzz_buf;
67 static size_t fuzz_sz;
68 
69 K_SEM_DEFINE(fuzz_sem, 0, K_SEM_MAX_LIMIT);
70 
fuzz_isr(const void * arg)71 static void fuzz_isr(const void *arg)
72 {
73 	/* We could call check0() to execute the fuzz case here, but
74 	 * pass it through to the main thread instead to get more OS
75 	 * coverage.
76 	 */
77 	k_sem_give(&fuzz_sem);
78 }
79 
main(void)80 int main(void)
81 {
82 	printk("Hello World! %s\n", CONFIG_BOARD);
83 
84 	IRQ_CONNECT(CONFIG_ARCH_POSIX_FUZZ_IRQ, 0, fuzz_isr, NULL, 0);
85 	irq_enable(CONFIG_ARCH_POSIX_FUZZ_IRQ);
86 
87 	while (true) {
88 		k_sem_take(&fuzz_sem, K_FOREVER);
89 
90 		/* Execute the fuzz case we got from LLVM and passed
91 		 * through an interrupt to this thread.
92 		 */
93 		check0(fuzz_buf, fuzz_sz);
94 	}
95 	return 0;
96 }
97 
98 /**
99  * Entry point for fuzzing. Works by placing the data
100  * into two known symbols, triggering an app-visible interrupt, and
101  * then letting the simulator run for a fixed amount of time (intended to be
102  * "long enough" to handle the event and reach a quiescent state
103  * again)
104  */
105 #if defined(CONFIG_BOARD_NATIVE_SIM)
106 NATIVE_SIMULATOR_IF /* We expose this function to the final runner link stage*/
107 #endif
LLVMFuzzerTestOneInput(const uint8_t * data,size_t sz)108 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz)
109 {
110 	static bool runner_initialized;
111 
112 	if (!runner_initialized) {
113 		nsi_init(0, NULL);
114 		runner_initialized = true;
115 	}
116 
117 	/* Provide the fuzz data to the embedded OS as an interrupt, with
118 	 * "DMA-like" data placed into native_fuzz_buf/sz
119 	 */
120 	fuzz_buf = (void *)data;
121 	fuzz_sz = sz;
122 
123 	hw_irq_ctrl_set_irq(CONFIG_ARCH_POSIX_FUZZ_IRQ);
124 
125 	/* Give the OS time to process whatever happened in that
126 	 * interrupt and reach an idle state.
127 	 */
128 	nsi_exec_for(k_ticks_to_us_ceil64(CONFIG_ARCH_POSIX_FUZZ_TICKS));
129 
130 	return 0;
131 }
132