1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2
3 /* Fuzzing should always have assertions */
4 #ifdef NDEBUG
5 #undef NDEBUG
6 #endif
7
8 #include <libpldm/base.h>
9 #include <libpldm/firmware_fd.h>
10 #include <libpldm/firmware_update.h>
11 #include <libpldm/sizes.h>
12
13 #include <cstdarg>
14 #include <cstdint>
15 #include <cstring>
16 #include <memory>
17 #include <vector>
18
19 #include "array.h"
20 #include "msgbuf.h"
21
22 /* Avoid out-of-memory, and
23 * avoid wasting time on inputs larger than MCTP message limits */
24 static const uint32_t MAX_PART = 200;
25
26 /* Maximum "send" buffer. Should be larger than any expected sent message */
27 static const uint32_t MAX_SEND = 1024;
28
29 /* Arbitrary EID */
30 static const uint8_t FIXED_ADDR = 20;
31
32 static const uint8_t PROGRESS_PERCENT = 5;
33
34 static const ssize_t FUZZCTRL_SIZE = 0x400;
35
36 static bool printf_enabled;
37 // NOLINTNEXTLINE(cert-dcl50-cpp)
debug_printf(const char * fmt,...)38 static void debug_printf(const char* fmt, ...)
39 {
40 if (printf_enabled)
41 {
42 va_list ap;
43 va_start(ap, fmt);
44 vprintf(fmt, ap);
45 va_end(ap);
46 }
47 }
48
49 struct fuzz_ops_ctx
50 {
51 struct pldm_msgbuf* fuzz_ctrl;
52
53 /* Details of in-progress update, for consistency checking */
54 bool current_update;
55 struct pldm_firmware_update_component update_comp;
56 uint32_t offset;
57 bool transferred;
58 bool verified;
59 bool applied;
60
61 uint64_t now;
62 };
63
64 /* Returns true with roughly `percent` chance */
fuzz_chance(struct fuzz_ops_ctx * ctx,uint8_t percent)65 static bool fuzz_chance(struct fuzz_ops_ctx* ctx, uint8_t percent)
66 {
67 uint8_t v;
68 assert(percent <= 100);
69 int rc = pldm_msgbuf_extract_uint8(ctx->fuzz_ctrl, v);
70 if (rc != 0)
71 {
72 return false;
73 }
74 uint8_t cutoff = (uint32_t)percent * UINT8_MAX / 100;
75 return v <= cutoff;
76 }
77
78 // openbmc 49871
79 static const uint8_t openbmc_iana[] = {
80 0xcf,
81 0xc2,
82 0x00,
83 0x00,
84 };
85
86 /* An arbitrary but valid set of descriptors.
87 * Short to be readily discoverable by fuzzing */
88 static const pldm_descriptor FIXED_DESCRIPTORS[] = {
89 {
90 .descriptor_type = PLDM_FWUP_IANA_ENTERPRISE_ID,
91 .descriptor_length = 4,
92 .descriptor_data = openbmc_iana,
93 },
94 };
95
cb_device_identifiers(void * ctx LIBPLDM_CC_UNUSED,uint8_t * descriptors_count,const struct pldm_descriptor ** descriptors)96 static int cb_device_identifiers(void* ctx LIBPLDM_CC_UNUSED,
97 uint8_t* descriptors_count,
98 const struct pldm_descriptor** descriptors)
99 {
100 debug_printf("cb_device_identifiers\n");
101 *descriptors_count = 1;
102 *descriptors = FIXED_DESCRIPTORS;
103 return 0;
104 }
105
106 static const struct pldm_firmware_component_standalone comp = {
107 .comp_classification = PLDM_COMP_UNKNOWN,
108 .comp_identifier = 0,
109 .comp_classification_index = 0,
110 .active_ver =
111 {
112 .comparison_stamp = 1,
113 .str =
114 {
115 .str_type = PLDM_STR_TYPE_UTF_8,
116 .str_len = 3,
117 .str_data = "zzz",
118 },
119 .date = {0},
120 },
121 .pending_ver =
122 {
123 .comparison_stamp = 1,
124 .str =
125 {
126 .str_type = PLDM_STR_TYPE_UNKNOWN,
127 .str_len = 4,
128 .str_data = "fnnn",
129 },
130 .date = {0},
131 },
132 .comp_activation_methods = {0},
133 .capabilities_during_update = {0},
134 };
135
136 static const struct pldm_firmware_component_standalone* comp_list[1] = {
137 &comp,
138 };
139
cb_components(void * ctx,uint16_t * ret_entry_count,const struct pldm_firmware_component_standalone *** ret_entries)140 static int cb_components(
141 void* ctx, uint16_t* ret_entry_count,
142 const struct pldm_firmware_component_standalone*** ret_entries)
143 {
144 debug_printf("cb_components\n");
145 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
146
147 *ret_entry_count = ARRAY_SIZE(comp_list);
148 *ret_entries = comp_list;
149 if (fuzz_chance(fuzz_ctx, 4))
150 {
151 return -EINVAL;
152 }
153 return 0;
154 }
155
cb_imageset_versions(void * ctx,struct pldm_firmware_string * active,struct pldm_firmware_string * pending)156 static int cb_imageset_versions(void* ctx, struct pldm_firmware_string* active,
157 struct pldm_firmware_string* pending)
158 {
159 debug_printf("cb_imageset_versions\n");
160 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
161
162 active->str_type = PLDM_STR_TYPE_ASCII;
163 active->str_len = 4;
164 memcpy(active->str_data, "1234", 4);
165 pending->str_type = PLDM_STR_TYPE_ASCII;
166 pending->str_len = 4;
167 memcpy(pending->str_data, "1235", 4);
168 if (fuzz_chance(fuzz_ctx, 4))
169 {
170 return -EINVAL;
171 }
172 return 0;
173 }
174
175 static enum pldm_component_response_codes
cb_update_component(void * ctx,bool update,const struct pldm_firmware_update_component * comp)176 cb_update_component(void* ctx, bool update,
177 const struct pldm_firmware_update_component* comp)
178 {
179 debug_printf("cb_update_component update=%d\n", update);
180 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
181
182 if (fuzz_chance(fuzz_ctx, 4))
183 {
184 return PLDM_CRC_COMP_PREREQUISITES_NOT_MET;
185 }
186 if (update)
187 {
188 /* Set up a new update */
189 assert(!fuzz_ctx->current_update);
190 debug_printf("cb_update_component set current_update=true\n");
191 fuzz_ctx->current_update = true;
192 fuzz_ctx->transferred = false;
193 fuzz_ctx->verified = false;
194 fuzz_ctx->applied = false;
195 fuzz_ctx->offset = 0;
196 memcpy(&fuzz_ctx->update_comp, comp, sizeof(*comp));
197 }
198 return PLDM_CRC_COMP_CAN_BE_UPDATED;
199 }
200
cb_transfer_size(void * ctx,uint32_t ua_max_transfer_size)201 static uint32_t cb_transfer_size(void* ctx, uint32_t ua_max_transfer_size)
202 {
203 debug_printf("cb_transfer_size ua_size=%zu\n",
204 (ssize_t)ua_max_transfer_size);
205 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
206
207 if (fuzz_chance(fuzz_ctx, 50))
208 {
209 // Sometimes adjust it
210 return MAX_PART - 20;
211 }
212 return ua_max_transfer_size;
213 }
214
215 static uint8_t
cb_firmware_data(void * ctx,uint32_t offset,const uint8_t * data LIBPLDM_CC_UNUSED,uint32_t len,const struct pldm_firmware_update_component * comp)216 cb_firmware_data(void* ctx, uint32_t offset,
217 const uint8_t* data LIBPLDM_CC_UNUSED, uint32_t len,
218 const struct pldm_firmware_update_component* comp)
219 {
220 debug_printf("cb_firmware_data offset=%zu len %zu\n", (size_t)offset,
221 (size_t)len);
222 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
223
224 assert(fuzz_ctx->current_update);
225 assert(!fuzz_ctx->transferred);
226 assert(!fuzz_ctx->verified);
227 assert(!fuzz_ctx->applied);
228 assert(offset == fuzz_ctx->offset);
229 fuzz_ctx->offset += len;
230 assert(fuzz_ctx->offset <= fuzz_ctx->update_comp.comp_image_size);
231 assert(memcmp(comp, &fuzz_ctx->update_comp, sizeof(*comp)) == 0);
232
233 if (fuzz_ctx->offset == fuzz_ctx->update_comp.comp_image_size)
234 {
235 fuzz_ctx->transferred = true;
236 }
237
238 if (fuzz_chance(fuzz_ctx, 2))
239 {
240 return PLDM_FWUP_TRANSFER_ERROR_IMAGE_CORRUPT;
241 }
242 return PLDM_FWUP_TRANSFER_SUCCESS;
243 }
244
cb_verify(void * ctx,const struct pldm_firmware_update_component * comp,bool * ret_pending,uint8_t * ret_percent_complete LIBPLDM_CC_UNUSED)245 static uint8_t cb_verify(void* ctx,
246 const struct pldm_firmware_update_component* comp,
247 bool* ret_pending,
248 uint8_t* ret_percent_complete LIBPLDM_CC_UNUSED)
249 {
250 debug_printf("cb_verify\n");
251 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
252
253 assert(fuzz_ctx->current_update);
254 assert(fuzz_ctx->transferred);
255 assert(!fuzz_ctx->verified);
256 assert(!fuzz_ctx->applied);
257 assert(memcmp(comp, &fuzz_ctx->update_comp, sizeof(*comp)) == 0);
258
259 if (fuzz_chance(fuzz_ctx, 5))
260 {
261 debug_printf("cb_verify set failure\n");
262 return PLDM_FWUP_VERIFY_ERROR_VERSION_MISMATCH;
263 }
264
265 if (fuzz_chance(fuzz_ctx, 50))
266 {
267 debug_printf("cb_verify set ret_pending=true\n");
268 *ret_pending = true;
269 }
270 else
271 {
272 fuzz_ctx->verified = true;
273 }
274
275 return PLDM_SUCCESS;
276 }
277
cb_apply(void * ctx,const struct pldm_firmware_update_component * comp,bool * ret_pending,uint8_t * ret_percent_complete LIBPLDM_CC_UNUSED)278 static uint8_t cb_apply(void* ctx,
279 const struct pldm_firmware_update_component* comp,
280 bool* ret_pending,
281 uint8_t* ret_percent_complete LIBPLDM_CC_UNUSED)
282 {
283 debug_printf("cb_apply\n");
284 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
285
286 assert(fuzz_ctx->current_update);
287 assert(fuzz_ctx->transferred);
288 assert(fuzz_ctx->verified);
289 assert(!fuzz_ctx->applied);
290 assert(memcmp(comp, &fuzz_ctx->update_comp, sizeof(*comp)) == 0);
291
292 if (fuzz_chance(fuzz_ctx, 5))
293 {
294 debug_printf("cb_apply set failure\n");
295 return PLDM_FWUP_APPLY_FAILURE_MEMORY_ISSUE;
296 }
297
298 if (fuzz_chance(fuzz_ctx, 50))
299 {
300 debug_printf("cb_apply set ret_pending=true\n");
301 *ret_pending = true;
302 }
303 else
304 {
305 debug_printf("cb_apply set current_update=false\n");
306 fuzz_ctx->current_update = false;
307 fuzz_ctx->applied = true;
308 }
309
310 return PLDM_SUCCESS;
311 }
312
cb_activate(void * ctx,bool self_contained LIBPLDM_CC_UNUSED,uint16_t * ret_estimated_time LIBPLDM_CC_UNUSED)313 static uint8_t cb_activate(void* ctx, bool self_contained LIBPLDM_CC_UNUSED,
314 uint16_t* ret_estimated_time LIBPLDM_CC_UNUSED)
315 {
316 debug_printf("cb_activate\n");
317 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
318
319 assert(!fuzz_ctx->current_update);
320 if (fuzz_chance(fuzz_ctx, 5))
321 {
322 return PLDM_ERROR;
323 }
324 return PLDM_SUCCESS;
325 }
326
cb_cancel_update_component(void * ctx,const struct pldm_firmware_update_component * comp)327 static void cb_cancel_update_component(
328 void* ctx, const struct pldm_firmware_update_component* comp)
329 {
330 debug_printf("cb_cancel_update_component\n");
331 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
332
333 assert(fuzz_ctx->current_update);
334 assert(fuzz_ctx->offset <= fuzz_ctx->update_comp.comp_image_size);
335 assert(memcmp(comp, &fuzz_ctx->update_comp, sizeof(*comp)) == 0);
336 fuzz_ctx->current_update = false;
337 }
338
cb_now(void * ctx)339 static uint64_t cb_now(void* ctx)
340 {
341 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx);
342
343 // Arbitrary 3s increment. FD code has a 1s retry timeout.
344 fuzz_ctx->now += 3000;
345 return fuzz_ctx->now;
346 }
347
348 static const struct pldm_fd_ops fuzz_ops = {
349 .device_identifiers = cb_device_identifiers,
350 .components = cb_components,
351 .imageset_versions = cb_imageset_versions,
352 .update_component = cb_update_component,
353 .transfer_size = cb_transfer_size,
354 .firmware_data = cb_firmware_data,
355 .verify = cb_verify,
356 .apply = cb_apply,
357 .activate = cb_activate,
358 .cancel_update_component = cb_cancel_update_component,
359 .now = cb_now,
360 };
361
LLVMFuzzerInitialize(int * argc LIBPLDM_CC_UNUSED,char *** argv LIBPLDM_CC_UNUSED)362 extern "C" int LLVMFuzzerInitialize(int* argc LIBPLDM_CC_UNUSED,
363 char*** argv LIBPLDM_CC_UNUSED)
364 {
365 printf_enabled = getenv("TRACEFWFD");
366 return 0;
367 }
368
LLVMFuzzerTestOneInput(uint8_t * input,size_t len)369 extern "C" int LLVMFuzzerTestOneInput(uint8_t* input, size_t len)
370 {
371 PLDM_MSGBUF_DEFINE_P(fuzzproto);
372 PLDM_MSGBUF_DEFINE_P(fuzzctrl);
373 int rc;
374
375 /* Split input into two parts. First FUZZCTRL_SIZE (0x400 bytes currently)
376 * is used for fuzzing control (random choices etc).
377 * The remainder is a PLDM packet stream, of length:data */
378 if (len < FUZZCTRL_SIZE)
379 {
380 return 0;
381 }
382 size_t proto_size = len - FUZZCTRL_SIZE;
383 rc = pldm_msgbuf_init_errno(fuzzctrl, 0, input, FUZZCTRL_SIZE);
384 assert(rc == 0);
385 rc =
386 pldm_msgbuf_init_errno(fuzzproto, 0, &input[FUZZCTRL_SIZE], proto_size);
387 assert(rc == 0);
388
389 auto ops_ctx = std::make_unique<fuzz_ops_ctx>();
390 memset(ops_ctx.get(), 0x0, sizeof(fuzz_ops_ctx));
391 /* callbacks may consume bytes from the fuzz control */
392 ops_ctx->fuzz_ctrl = fuzzctrl;
393
394 struct pldm_fd* fd = pldm_fd_new(&fuzz_ops, ops_ctx.get(), NULL);
395 assert(fd);
396
397 while (true)
398 {
399 /* Arbitrary length send buffer */
400 uint32_t send_len;
401 rc = pldm_msgbuf_extract_uint32(fuzzctrl, send_len);
402 if (rc)
403 {
404 break;
405 }
406 send_len %= (MAX_SEND + 1);
407 std::vector<uint8_t> send_buf(send_len);
408
409 size_t len = send_buf.size();
410 /* Either perform pldm_fd_handle_msg() or pldm_fd_progress() */
411 if (fuzz_chance(ops_ctx.get(), PROGRESS_PERCENT))
412 {
413 uint8_t address = FIXED_ADDR;
414 pldm_fd_progress(fd, send_buf.data(), &len, &address);
415 }
416 else
417 {
418 uint32_t part_len;
419 rc = pldm_msgbuf_extract_uint32(fuzzproto, part_len);
420 if (rc)
421 {
422 break;
423 }
424 part_len = std::min(part_len, MAX_PART);
425 /* Fresh allocation so that ASAN will notice overflow reads */
426 std::vector<uint8_t> part_buf(part_len);
427 rc = pldm_msgbuf_extract_array_uint8(
428 fuzzproto, part_len, part_buf.data(), part_buf.size());
429 if (rc != 0)
430 {
431 break;
432 }
433 pldm_fd_handle_msg(fd, FIXED_ADDR, part_buf.data(), part_buf.size(),
434 send_buf.data(), &len);
435 }
436 assert(len <= send_buf.size());
437 }
438 rc = pldm_msgbuf_discard(fuzzproto, rc);
439 rc = pldm_msgbuf_discard(fuzzctrl, rc);
440 (void)rc;
441
442 free(fd);
443 return 0;
444 }
445
446 #ifdef HFND_FUZZING_ENTRY_FUNCTION
447 #define USING_HONGGFUZZ 1
448 #else
449 #define USING_HONGGFUZZ 0
450 #endif
451
452 #ifdef __AFL_FUZZ_TESTCASE_LEN
453 #define USING_AFL 1
454 #else
455 #define USING_AFL 0
456 #endif
457
458 #if USING_AFL
459 __AFL_FUZZ_INIT();
460 #endif
461
462 #if !USING_AFL && !USING_HONGGFUZZ
463 /* Let it build without AFL taking stdin instead */
run_standalone()464 static void run_standalone()
465 {
466 while (true)
467 {
468 unsigned char buf[1024000];
469 ssize_t len = read(STDIN_FILENO, buf, sizeof(buf));
470 if (len <= 0)
471 {
472 break;
473 }
474 LLVMFuzzerTestOneInput(buf, len);
475 }
476 }
477 #endif
478
479 #if !USING_HONGGFUZZ
main(int argc,char ** argv)480 int main(int argc, char** argv)
481 {
482 LLVMFuzzerInitialize(&argc, &argv);
483
484 #if USING_AFL
485 __AFL_INIT();
486 uint8_t* buf = __AFL_FUZZ_TESTCASE_BUF;
487
488 while (__AFL_LOOP(100000))
489 {
490 size_t len = __AFL_FUZZ_TESTCASE_LEN;
491 LLVMFuzzerTestOneInput(buf, len);
492 }
493 #else
494 run_standalone();
495 #endif
496
497 return 0;
498 }
499 #endif // !USING_HONGGFUZZ
500