Skip to content

fuzz: Add structured graph fuzz harness and fix folding overflows it found#149

Draft
weltling wants to merge 4 commits into
dstogov:masterfrom
weltling:fuzz-graph-prototype
Draft

fuzz: Add structured graph fuzz harness and fix folding overflows it found#149
weltling wants to merge 4 commits into
dstogov:masterfrom
weltling:fuzz-graph-prototype

Conversation

@weltling

Copy link
Copy Markdown
Contributor

This adds a second libFuzzer harness that fuzzes the IR builder and optimization pipeline directly, plus two folding fixes the harness found.

Harness

The existing harness fuzz_ir.c feeds raw bytes to the text IR parser, so most mutated inputs are rejected before reaching the optimizer. The new harness fuzz_graph.c decodes each input blob into a structurally valid IR function instead, so inputs reach the optimizer and the code emitter. This is the core of the harness. Later features like loops and conditions will be built on top of it.

Decoding maps the leading bytes to a working type and parameter count, then turns each following group of bytes into one type preserving operation over earlier nodes, ending with a return of the last value. The build targets fuzz-graph-O0, fuzz-graph-O1 and fuzz-graph-O2 select the optimization level. The existing fuzz_ir.c is renamed to fuzz_text.c so the pair reads as text versus graph.

Fixes found

  • MUL(C_U16, C_U16). Both uint16_t operands promote to int, so the product overflowed signed int. Multiply through uint32_t.
  • MUL/DIV(NEG, C_I). The constant was negated on a signed int64_t, which is undefined for INT64_MIN. Negate through uint64_t. The release result is unchanged since the negation wraps to itself.

Both were caught by the UBSan build of the harness, and the folded output is identical on a release build.

weltling added 4 commits June 15, 2026 22:20
This harness decodes the input blob directly into a structurally valid
IR function and runs the full optimization and codegen pipeline on it,
so inputs exercise the optimizer and backend.

The input bytes are turned into a valid IR function step by step. The
first byte picks the type and parameter count, then each small group
of bytes adds one operation that uses earlier nodes as inputs. The
last node becomes the return value.

The optimization level is selected at compile time via FUZZ_GRAPH_O0,
FUZZ_GRAPH_O1 or FUZZ_GRAPH_O2.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
The harness that drives the text parser was named fuzz_ir.c, which did
not say what set it apart from fuzz_graph.c since both fuzz the IR. Name
it after its input format so the pair reads as text versus graph. Update
the build rules to match.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
The two u16 operands are promoted to signed int before the multiply,
so a wide product such as 54300 * 54300 overflows int. The result is
truncated back to u16 and is correct on any two complement target,
but it is still undefined behavior.

Cast both operands to uint32_t so the multiply is unsigned.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
The rules MUL(NEG(x), c) and DIV(NEG(x), c) negate the constant on a
signed int64_t, which is undefined behavior for INT64_MIN. Negate
through uint64_t instead. The result is unchanged on a release build
since the negation of INT64_MIN wraps to itself.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>

@dstogov dstogov left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ir_fold.h fixes look right. Thanks!

According to fuzzer, it would be great to completely separate it, moving everything related from Makefile to fuzz/Mafefile. Is this difficult to do?

In case some day fuzzer is going to execute generated code, it should do this inside a container... This is not necessary right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants