cybersecurity · intermediate · ~15 min · safe pentest lab

A libFuzzer-shape target that never crashes

A real fuzz-target signature with the bounds-safety discipline a fuzzer will exercise.

Challenge

Your job

Implement:

#include <stdint.h>
#include <stddef.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

Parse the input as a stream of records:

[len:u8][type:u8][payload: len-1 bytes]

Count records whose type == 0x01. Return the count. Always return 0 on degenerate input. Must never crash on any input.

Rules

  • size == 0 or data == NULL → return 0.
  • A record with len == 0 → stop the walk, return what's been counted.
  • A record whose len walks past size → stop the walk, return what's been counted (do not read past data[size - 1]).
  • No heap allocation; no globals; no early return on len < 2 other than the rules above.

Hints

  1. (concept) Same TLV walk as parse-bt-advertisement, but the function signature is fixed and the failure mode is "stop counting" not "return -1".
  2. (common bug) Forgetting that len itself is one byte; the next record is at off + len + 1.

Why this matters

The fuzz-target signature is a portable contract. Match it once and every popular fuzzer can drive your parser.

Input format

A byte buffer + its length.

Output format

Non-negative count of type-0x01 records.

Constraints

No crashes on any input. No allocation. No reads past data[size - 1].

Starter code

#include <stdint.h>
#include <stddef.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    /* TODO */
    (void)data; (void)size;
    return 0;
}

Common mistakes

Returning -1 when input is bad (the fuzz target should always succeed). Reading data[off+1] before bounds-checking. Allowing len==0 to loop forever.

Edge cases to handle

size 0. data NULL with size 0. Single byte. All-junk input.

Complexity

O(size).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.