blob: 4179ebedfea68594d3a16a546e62a63bbac44a5c [file] [log] [blame]
Matt Johnston060c71e2025-01-08 14:35:13 +08001#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
18static_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
27static const size_t FUZZCTRL_SIZE = 0x400;
28
29static const uint8_t RX_CHANCE = 90;
30static const uint8_t TX_BUSY_CHANCE = 3;
31
32static const uint8_t OWN_I2C_ADDR = 0x20;
33static const uint8_t OWN_EID = 123;
34
35/* time step in milliseconds */
36static const uint32_t MAX_TIME_STEP = 15000;
37
38struct fuzz_buf {
39 size_t len;
40 size_t pos;
41 const uint8_t *data;
42};
43
44struct 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
56static 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
65static 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 */
77static 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 */
91static 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
104static 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
117static 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
129static 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
151static 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
197static 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
214int 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
256int 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 */
279static 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
293int 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