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

Write a libFuzzer-shape target that walks a length-prefixed record stream and survives any input a fuzzer can throw at it.

Task

Implement the standard fuzz-target signature:

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

Parse data as a stream of records, each laid out as:

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

Count the records whose type == 0x01 and return that count.

Input

  • data, size: the fuzzer-supplied bytes and their length. The grader passes hand-crafted edge inputs.

Output

Returns a non-negative int: the number of type == 0x01 records seen before the walk stops. The function must never crash.

Example

{0x01,0x02, 0x01,0x01}              ->   1   (two len=1 records, one is type 1)
{0x01,0x01, 0x02,0x01,0xAA, 0x01,0x01} ->   3
{0x01,0x01, 0x10,0x01}             ->   1   (second len=16 walks past end: stop)
{0x01,0x01, 0x00,0xFF, 0x01,0x01}  ->   1   (len==0 stops the walk)
NULL/size 0                        ->   0

Edge cases

  • size == 0 or data == NULL: return 0.
  • A record with len == 0: stop the walk, return the count so far.
  • A record whose len would read past size: stop the walk (never read past data[size - 1]).
  • Note len is one byte; the next record starts at off + len + 1.

Rules

  • Never crash on any input; no heap allocation; no globals.

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 data and its length size.

Output format

A non-negative int: the count of type-0x01 records before the walk stops.

Constraints

No crashes on any input; no allocation; never read 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.