xref: /openbmc/libmctp/tests/fuzz/i2c-fuzz.c (revision 060c71ed3b529c8e0a7de41e45b3b20b70933daf)
1 #include <stdbool.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <assert.h>
8 #include <errno.h>
9 #include <endian.h>
10 
11 #include "compiler.h"
12 #include "libmctp.h"
13 #include "libmctp-i2c.h"
14 #include "libmctp-sizes.h"
15 #include "libmctp-alloc.h"
16 
17 #if NDEBUG
18 static_assert(0, "fuzzing shouldn't build with NDEBUG");
19 #endif
20 
21 /* Limits memory used in tx path */
22 #define MAX_SEND 600
23 
24 /* Avoids wasting time traversing unreachable sizes */
25 #define MAX_RECEIVE 30
26 
27 static const size_t FUZZCTRL_SIZE = 0x400;
28 
29 static const uint8_t RX_CHANCE = 90;
30 static const uint8_t TX_BUSY_CHANCE = 3;
31 
32 static const uint8_t OWN_I2C_ADDR = 0x20;
33 static const uint8_t OWN_EID = 123;
34 
35 /* time step in milliseconds */
36 static const uint32_t MAX_TIME_STEP = 15000;
37 
38 struct fuzz_buf {
39 	size_t len;
40 	size_t pos;
41 	const uint8_t *data;
42 };
43 
44 struct fuzz_ctx {
45 	struct fuzz_buf *ctrl;
46 	struct fuzz_buf *input;
47 
48 	struct mctp_binding_i2c *i2c;
49 	struct mctp *mctp;
50 
51 	uint64_t now;
52 
53 	bool done;
54 };
55 
fuzz_buf_new(const void * data,size_t len)56 static struct fuzz_buf *fuzz_buf_new(const void *data, size_t len)
57 {
58 	struct fuzz_buf *buf = malloc(sizeof(struct fuzz_buf));
59 	buf->pos = 0;
60 	buf->len = len;
61 	buf->data = data;
62 	return buf;
63 }
64 
fuzz_buf_extract(struct fuzz_buf * buf,size_t len)65 static const void *fuzz_buf_extract(struct fuzz_buf *buf, size_t len)
66 {
67 	if (buf->pos + len > buf->len) {
68 		return NULL;
69 	}
70 
71 	const void *ret = &buf->data[buf->pos];
72 	buf->pos += len;
73 	return ret;
74 }
75 
76 /* Returns true on success */
fuzz_buf_extract_u32(struct fuzz_buf * buf,uint32_t * ret)77 static bool fuzz_buf_extract_u32(struct fuzz_buf *buf, uint32_t *ret)
78 {
79 	const void *r = fuzz_buf_extract(buf, sizeof(uint32_t));
80 	if (!r) {
81 		return false;
82 	}
83 
84 	uint32_t v;
85 	memcpy(&v, r, sizeof(v));
86 	*ret = be32toh(v);
87 	return true;
88 }
89 
90 /* Returns true with roughly `percent` chance */
fuzz_chance(struct fuzz_ctx * ctx,uint8_t percent)91 static bool fuzz_chance(struct fuzz_ctx *ctx, uint8_t percent)
92 {
93 	assert(percent <= 100);
94 
95 	const uint8_t *v = fuzz_buf_extract(ctx->ctrl, sizeof(uint8_t));
96 	if (!v) {
97 		return false;
98 	}
99 
100 	uint8_t cutoff = (uint32_t)percent * UINT8_MAX / 100;
101 	return *v <= cutoff;
102 }
103 
fuzz_i2c_tx(const void * buf,size_t len,void * c)104 static int fuzz_i2c_tx(const void *buf, size_t len, void *c)
105 {
106 	struct fuzz_ctx *ctx = c;
107 	(void)buf;
108 	(void)len;
109 
110 	if (fuzz_chance(ctx, TX_BUSY_CHANCE)) {
111 		return -EBUSY;
112 	}
113 
114 	return 0;
115 }
116 
fuzz_i2c_rxmsg(uint8_t src_eid,bool tag_owner,uint8_t msg_tag,void * c,void * msg,size_t len)117 static void fuzz_i2c_rxmsg(uint8_t src_eid, bool tag_owner, uint8_t msg_tag,
118 			   void *c, void *msg, size_t len)
119 {
120 	struct fuzz_ctx *ctx = c;
121 	(void)ctx;
122 	(void)src_eid;
123 	(void)tag_owner;
124 	(void)msg_tag;
125 	(void)msg;
126 	(void)len;
127 }
128 
do_rx(struct fuzz_ctx * ctx)129 static void do_rx(struct fuzz_ctx *ctx)
130 {
131 	uint32_t len;
132 	if (!fuzz_buf_extract_u32(ctx->ctrl, &len)) {
133 		ctx->done = true;
134 		return;
135 	}
136 
137 	if (len > MAX_RECEIVE) {
138 		ctx->done = true;
139 		return;
140 	}
141 
142 	const uint8_t *data = fuzz_buf_extract(ctx->input, len);
143 	if (!data) {
144 		ctx->done = true;
145 		return;
146 	}
147 
148 	mctp_i2c_rx(ctx->i2c, data, len);
149 }
150 
do_tx(struct fuzz_ctx * ctx)151 static void do_tx(struct fuzz_ctx *ctx)
152 {
153 	int rc;
154 
155 	const uint8_t *e = fuzz_buf_extract(ctx->ctrl, sizeof(uint8_t));
156 	if (!e) {
157 		ctx->done = true;
158 		return;
159 	}
160 	mctp_eid_t eid = *e;
161 
162 	bool tag_owner = fuzz_chance(ctx, 50);
163 	/* `t` generates the dest eid in owner case, or tag in non-owner case */
164 	const uint8_t *t = fuzz_buf_extract(ctx->ctrl, sizeof(uint8_t));
165 	if (!t) {
166 		ctx->done = true;
167 		return;
168 	}
169 
170 	uint32_t len;
171 	if (!fuzz_buf_extract_u32(ctx->ctrl, &len)) {
172 		ctx->done = true;
173 		return;
174 	}
175 	len = len % (MAX_SEND + 1);
176 
177 	uint8_t *fake_send_data = __mctp_msg_alloc(len, ctx->mctp);
178 
179 	mctp_i2c_tx_poll(ctx->i2c);
180 
181 	if (tag_owner) {
182 		/* Random destination from a small set, reuse `t` */
183 		mctp_eid_t dest = 10 + (*t % 5);
184 		uint8_t tag;
185 		rc = mctp_message_tx_request(ctx->mctp, dest, fake_send_data,
186 					     len, &tag);
187 		if (rc == 0) {
188 			assert((tag & MCTP_HDR_TAG_MASK) == tag);
189 		}
190 	} else {
191 		uint8_t tag = *t % 8;
192 		mctp_message_tx_alloced(ctx->mctp, eid, tag_owner, tag,
193 					fake_send_data, len);
194 	}
195 }
196 
fuzz_now(void * c)197 static uint64_t fuzz_now(void *c)
198 {
199 	struct fuzz_ctx *ctx = c;
200 
201 	uint32_t step = 10;
202 	uint32_t s;
203 	if (fuzz_buf_extract_u32(ctx->ctrl, &s)) {
204 		step = s % (MAX_TIME_STEP + 1);
205 	}
206 
207 	uint64_t prev = ctx->now;
208 	ctx->now += step;
209 	/* Notice if overflow occurs */
210 	assert(ctx->now >= prev);
211 	return ctx->now;
212 }
213 
LLVMFuzzerTestOneInput(uint8_t * input,size_t len)214 int LLVMFuzzerTestOneInput(uint8_t *input, size_t len)
215 {
216 	/* Split input into two parts. First FUZZCTRL_SIZE (0x400 bytes currently)
217      * is used for fuzzing control (random choices etc).
218      * The remainder is a PLDM packet stream, of length:data */
219 	if (len < FUZZCTRL_SIZE) {
220 		return 0;
221 	}
222 
223 	struct fuzz_ctx _ctx = {
224 		.ctrl = fuzz_buf_new(input, FUZZCTRL_SIZE),
225 		.input = fuzz_buf_new(&input[FUZZCTRL_SIZE],
226 				      len - FUZZCTRL_SIZE),
227 		.now = 0,
228 		.done = false,
229 	};
230 	struct fuzz_ctx *ctx = &_ctx;
231 
232 	/* Instantiate the MCTP stack */
233 	ctx->i2c = malloc(MCTP_SIZEOF_BINDING_I2C);
234 	mctp_i2c_setup(ctx->i2c, OWN_I2C_ADDR, fuzz_i2c_tx, ctx);
235 	ctx->mctp = mctp_init();
236 	mctp_register_bus(ctx->mctp, mctp_binding_i2c_core(ctx->i2c), OWN_EID);
237 	mctp_set_rx_all(ctx->mctp, fuzz_i2c_rxmsg, ctx);
238 	mctp_set_now_op(ctx->mctp, fuzz_now, ctx);
239 
240 	while (!ctx->done) {
241 		if (fuzz_chance(ctx, RX_CHANCE)) {
242 			do_rx(ctx);
243 		} else {
244 			do_tx(ctx);
245 		}
246 	}
247 
248 	mctp_destroy(ctx->mctp);
249 	free(ctx->i2c);
250 	free(ctx->ctrl);
251 	free(ctx->input);
252 
253 	return 0;
254 }
255 
LLVMFuzzerInitialize(int * argc __unused,char *** argv __unused)256 int LLVMFuzzerInitialize(int *argc __unused, char ***argv __unused)
257 {
258 	return 0;
259 }
260 
261 #ifdef HFND_FUZZING_ENTRY_FUNCTION
262 #define USING_HONGGFUZZ 1
263 #else
264 #define USING_HONGGFUZZ 0
265 #endif
266 
267 #ifdef __AFL_FUZZ_TESTCASE_LEN
268 #define USING_AFL 1
269 #else
270 #define USING_AFL 0
271 #endif
272 
273 #if USING_AFL
274 __AFL_FUZZ_INIT();
275 #endif
276 
277 #if !USING_AFL && !USING_HONGGFUZZ
278 /* Let it build without AFL taking stdin instead */
run_standalone()279 static void run_standalone()
280 {
281 	while (true) {
282 		unsigned char buf[1024000];
283 		ssize_t len = read(STDIN_FILENO, buf, sizeof(buf));
284 		if (len <= 0) {
285 			break;
286 		}
287 		LLVMFuzzerTestOneInput(buf, len);
288 	}
289 }
290 #endif
291 
292 #if !USING_HONGGFUZZ
main(int argc,char ** argv)293 int main(int argc, char **argv)
294 {
295 	LLVMFuzzerInitialize(&argc, &argv);
296 
297 #if USING_AFL
298 	__AFL_INIT();
299 	uint8_t *buf = __AFL_FUZZ_TESTCASE_BUF;
300 
301 	while (__AFL_LOOP(100000)) {
302 		size_t len = __AFL_FUZZ_TESTCASE_LEN;
303 		LLVMFuzzerTestOneInput(buf, len);
304 	}
305 #else
306 	run_standalone();
307 #endif
308 
309 	return 0;
310 }
311 #endif // !USING_HONGGFUZZ
312