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