Modules

Fuzz

moduleextensionexperimentalSource

Available on macOS, Linux, Windows, Wasm·since 0.26.0

#import "Extensions/Fuzz";

Builds random arguments from a type's layout and runs fuzz cases over them. gen makes a value from a seeded Rng, favouring the edge cases that tend to break code (zero, minimum and maximum integers, NaN, empty and malformed strings). run_cases drives a table of Cases from the command line, and run_engine hands them to libFuzzer.

Example↗
r: Rng;
rng_seed(*r, 7);
first := copy_string(gen(*r, string));
case_end(*r);

rng_seed(*r, 7);
again := gen(*r, string);
assert(first == again);
case_end(*r);

Procedures#

RNG#

rng_seedexperimentalsince 0.26.0Source
(r: *Rng, seed: u64)

Starts the sequence for seed. The same seed always gives the same values.

nextexperimentalsince 0.26.0Source
(r: *Rng) -> u64

The next 64 random bits.

takeexperimentalsince 0.26.0Source
(r: *Rng, bytes: s64) -> u64

The next bytes (1 to 8) bytes of the input, or the low bytes of the next seeded value.

belowexperimentalsince 0.26.0Source
(r: *Rng, n: s64) -> s64

Uniform in [0, n); 0 when n <= 0. Reads only as many input bytes as n needs.

chanceexperimentalsince 0.26.0Source
(r: *Rng, one_in: s64) -> bool

True about one time in one_in.

case_allocexperimentalsince 0.26.0Source
(r: *Rng, bytes: s64) -> *u8

Memory that lives until case_end; zeroed, so strings are NUL terminated.

case_endexperimentalsince 0.26.0Source
(r: *Rng)

Frees what the case allocated and resets temporary storage. Call it after every case.

Values#

can_fillexperimentalsince 0.26.0Source
(info: *Type_Info) -> bool

Whether fill can produce this type. Callbacks, Any, Type and resizable arrays cannot be built.

has_pointerexperimentalsince 0.26.0Source
(info: *Type_Info) -> bool

Whether a value of this type contains a pointer, which targets must opt into.

genexperimentalsince 0.26.0Source
(r: *Rng, $T: Type) -> T

A random T: integers, floats, bools, strings, enums, fixed and slice arrays, structs of those, and pointers when r.pointers is set. Memory for strings and views comes from the Rng and lasts until case_end.

r: Rng;
rng_seed(*r, 3);
r.max_len = 8;
text := gen(*r, string);
assert(text.count <= 4 * 8 + 4);
number := gen(*r, s32);
case_end(*r);
↗
fillexperimentalsince 0.26.0Source
(r: *Rng, info: *Type_Info, dst: *u8)

Writes a random value of the type info describes at dst; gen is the typed wrapper.

store_bitsexperimentalsince 0.26.0Source
(dst: *u8, size: s64, bits: u64)

Writes the low size bytes of bits at dst, little endian.

integer_bitsexperimentalsince 0.26.0Source
(r: *Rng, size: s64, signed: bool) -> u64

Boundary values come up often because bugs cluster at them.

float_bitsexperimentalsince 0.26.0Source
(r: *Rng, size: s64) -> u64

The bit pattern of a random float, favouring zero, NaN, infinities and the extremes.

fill_enumexperimentalsince 0.26.0Source
(r: *Rng, info: *Type_Info_Enum, dst: *u8)

An enum value: usually a member, sometimes one no member names.

fill_pointerexperimentalsince 0.26.0Source
(r: *Rng, info: *Type_Info_Pointer, dst: *u8)

Null, or a pointer to a freshly generated pointee when the Rng allows pointers.

fill_arrayexperimentalsince 0.26.0Source
(r: *Rng, info: *Type_Info_Array, dst: *u8)

A fixed array's elements, or a view over generated elements.

gen_lengthexperimentalsince 0.26.0Source
(r: *Rng) -> s64

Mostly short, sometimes empty, sometimes as long as allowed.

gen_stringexperimentalsince 0.26.0Source
(r: *Rng) -> string

Strings mix well-formed text with the malformed byte runs decoders must reject.

put_code_pointexperimentalsince 0.26.0Source
(dst: *u8, code: u32) -> s64

Writes code at dst as UTF-8 and returns the byte count.

put_malformedexperimentalsince 0.26.0Source
(r: *Rng, dst: *u8) -> s64

Writes one malformed sequence of at most four bytes and returns its length.

special_byteexperimentalsince 0.26.0Source
(r: *Rng) -> u8

A byte that tends to matter to parsers: whitespace, quotes, separators and punctuation.

alphabet_byteexperimentalsince 0.26.0Source
(r: *Rng) -> u8

A random byte from a mix of letters, digits and number-ish characters.

Runner#

run_casesexperimentalsince 0.31.0Source
(cases: [] Case) -> s32

Arguments (after --): --seed N, --count N (seeds per case), --only NAME, --max-len N, --list. Prints CASE <name> <seed> <max_len> before every call so the last line names the culprit when the process dies, and DONE <calls> at the end. Returns the process exit code.

run_engineexperimentalsince 0.31.0Source
(cases: [] Case) -> s32

Coverage-guided mode. The program is built with -sanitize fuzzer (SanitizerCoverage plus the libFuzzer engine without its main); run_engine hands the engine control, and it calls engine_input with each input. Command line: libFuzzer's own flags and corpus directories, plus --only NAME to fuzz one case and --trace to print each input's case name (to learn which case a stored crash belongs to). The first input byte picks the case.

Types#

Rngstructexperimentalsince 0.26.0Source
:: struct

A deterministic random source, plus the allocations made while filling values. Seed it with rng_seed and release a case's memory with case_end.

state: u64
from_input: bool
With from_input, every choice is read from data instead of the seeded sequence, as a coverage-guided engine (libFuzzer) supplies it. Reads past the end give zero bytes, which select the first alternative (empty, zero, the first member).
data: *u8
size: s64
position: s64
max_len: s64
Upper bound for generated lengths; the minimizer lowers it to shrink a failing case.
pointers: bool
Pointers are filled with null or a fresh allocation only when the target allows it.
nullable: bool
With pointers, a pointer may also be null; for procedures that document accepting null.
live: [..] *void
r: Rng;
rng_seed(*r, 1);
roll := below(*r, 6);
assert(roll >= 0 && roll < 6);
↗
CasestructforeignexperimentalSource
:: struct
name: string
run: (r: *Rng)
pointers: bool
nullable: bool