Papers
Topics
Authors
Recent
Search
2000 character limit reached

Go-LibAFL-DiFuzz: Directed Fuzzing for Go

Updated 6 February 2026
  • Go-LibAFL-DiFuzz is a directed greybox fuzzing framework for Go that integrates static analysis with source-level instrumentation to target specific program points.
  • It combines call graph and control-flow analysis with enhanced target sequence (ETS) construction and adaptive mutation scheduling for efficient bug discovery.
  • Empirical evaluations show significant improvements in time-to-exposure and target coverage compared to traditional Go fuzzers like go-fuzz.

Go-LibAFL-DiFuzz is a directed greybox fuzzing framework tailored for Go applications, extending the LibAFL-DiFuzz architecture originally designed for C/C++ and Rust. The system is optimized for reaching user-specified program points (e.g., locations of interest, bug-triggering panics) by combining static program analysis with source-level instrumentation and adaptive mutation scheduling. Through call graph (CG) and control-flow graph (CFG) analysis, custom proximity metrics, and runtime feedback, Go-LibAFL-DiFuzz achieves significant improvements in time-to-exposure and target coverage versus prior Go fuzzing tools such as go-fuzz (Mezhuev et al., 30 Jan 2026).

1. System Architecture and Pipeline

Go-LibAFL-DiFuzz operates in two principal phases: static analysis (graph construction and target path extraction) and source-level instrumentation, followed by in-process fuzzing using the LibAFL framework. The workflow can be summarized as follows:

  1. Static Analysis: The DiFuzz-Go library parses the Go project source directory using the standard Go toolchain components: go/packages, go/parser, go/ast, and go/ssa for IR recovery (Section 5.1 of (Mezhuev et al., 30 Jan 2026)). It constructs the overall CG and extracts the CFG for each function, integrating them in a unified DOT-based representation annotated with structural debug information.
  2. Enhanced Target Sequence (ETS) Construction: For each user-specified target point tt, the tool computes all basic blocks vv that lie on a shortest path from program entry to tt; these blocks form the ETS. Static per-block weights w(v)w(v) are assigned and output in a TOML configuration (ets.toml).
  3. Source Instrumentation:
    • The ETS instrumentor parses ets.toml and injects InstrumentETS(v_id) at selected AST positions within each basic block vv \in ETS, routing control information to the fuzzer’s runtime shared memory via a lightweight Go runtime library.
    • The SanCov instrumentor injects SancovGuard(v_id) at every basic block to enable fine-grained coverage recording, compatible with sanitizer_cov_trace_pc_guard invoked via cgo.
  4. Build and Link: Instrumented Go sources are built with linker flags to integrate a forkserver and sanitizer coverage hooks.
  5. Fuzzing Execution: The resulting binary is executed under LibAFL-DiFuzz, which applies dynamic input mutation and monitors both coverage and ETS-based metrics for feedback-driven scheduling.

This pipeline enables precise targeting of deep or rare execution paths within Go programs while preserving source-level compatibility and minimal runtime overhead [(Mezhuev et al., 30 Jan 2026), Section 5–6.4].

2. Static Analysis, ETS Construction, and Instrumentation

Central to the approach is ETS construction and per-block weight assignment:

  • Call Graph and CFG Extraction: Using Go’s SSA, the system forms a CG G=(V,E)G = (V, E) covering all functions, while also deriving a CFG per function from the AST.
  • Identification and Marking: Each basic block vv is uniquely identified with a 64-bit ID and mapped to its source location.
  • Target Marking and ETS Generation: The system accepts a set of targets T={t1,...,tk}T = \{t_1, ..., t_k\} to guide the search. For each target, an ETS is computed as the set of basic blocks on some shortest path from entry to tt:

ETS={vV: path entryvt}ETS = \{ v \in V : \exists \text{ path } entry \to v \to t \}

All relevant information—including file, function, block ID, and static weight w(v)w(v)—is encapsulated in ets.toml [(Mezhuev et al., 30 Jan 2026), Section 5.1].

  • Instrumentation Implementation:
    • ETS Instrumentor rewrites AST nodes, injecting calls to InstrumentETS(v_id) so that runtime block hits are reported to shared ETS memory.
    • SanCov Instrumentor injects coverage logging. The combination preserves correct Go semantics (Section 6.4).
    • At runtime, Go’s init() automatically registers the forkserver, maps shared memory, and initializes sanitizer coverage.

This combination of static SSA/AST analysis with modular instrumentation is central to the scalability and flexibility of Go-LibAFL-DiFuzz.

3. Directed Fuzzing Loop, Proximity Metrics, and Mutation Scheduling

Fuzzing campaigns are managed by the LibAFL-DiFuzz backend via a loop that selects seed inputs, mutates them, executes the instrumented target, and updates the corpus using ETS/coverage feedback:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// (see [2601.22772], Section 5.2 for Go-centric sketch)
Corpus ← load_corpus(seed_dir)
Feedback ← combine(CoverageFeedback, ETSFeedback)
State ← FuzzerState(Corpus, Feedback, Scheduler)

while not timeout:
    seed_input ← State.Scheduler.select(Corpus)
    candidate ← State.Mutator.mutate(seed_input)

    exec_result ← run_target(candidate)
    cov_bits ← exec_result.coverage_bitmap
    ets_hits ← exec_result.ets_shared_memory

    new_cov ← Feedback.Coverage.process(cov_bits)
    dist_metric ← Feedback.ETS.process(ets_hits)

    if new_cov or dist_metric < seed_input.best_distance:
        Corpus.add(candidate, dist_metric)

    if exec_result.crashed:
        save_crash(candidate)
end

Proximity Metric

For each execution, the shortest-path proximity to target TT is computed:

  • Let ds(v)=mintTshortest_path_length(vt)d_s(v) = \min_{t \in T} \text{shortest\_path\_length}(v \rightarrow t) (computed via multi-source Dijkstra). Over the execution trace BB, set D(B)=minvBds(v)D(B) = \min_{v \in B} d_s(v).
  • Normalize:

dnorm(v)=ds(v)dmindmaxdmind_{\text{norm}}(v) = \frac{d_s(v) - d_{\min}}{d_{\max} - d_{\min}}

where dmin=0d_{\min}=0, dmax=maxvds(v)d_{\max} = \max_v d_s(v).

  • Per-run metric:

M(B)=minvBdnorm(v)M(B) = \min_{v \in B} d_{\text{norm}}(v)

Inputs with smaller M(B)M(B) are prioritized, driving the exploration toward TT [(Mezhuev et al., 30 Jan 2026), Section 3].

Mutation Operators

The mutational core leverages LibAFL’s operators:

  • Bit/byte/word flips
  • Block insert/delete/shuffle
  • Dictionary token insertion
  • Seed splicing Selection is controlled by a power-law scheduler with user configurability [(Mezhuev et al., 30 Jan 2026), Section 7].

Instrumentation

  • SancovGuard(vidv_{id}): writes BB coverage to a 2162^{16}-byte shared-memory bitmap indexed by vidv_{id}.
  • InstrumentETS(vidv_{id}): records block hits into a runtime FIFO, used for online proximity feedback.

4. Performance and Comparative Evaluation

Go-LibAFL-DiFuzz was empirically evaluated on a series of open-source Go projects. The benchmark methodology involved injecting panics at target sites, then comparing Time to Exposure (TTE, time until the first target execution) against go-fuzz, across 20 independent runs of 1 hr each.

Target Go-LibAFL-DiFuzz (best/avg, s) go-fuzz (best/avg, s)
ollama₁ 0.5 / 7.9 0.5 / 1.7
ollama₂ 9.0 / 9.5 (50% TO) 2.0 / 48.0 (20% TO)
image-go₁ 0.4 / 1.88 1.0 / 1.9
image-go₂ 0.4 / 2.2 (50% TO) 1.0 / 1.3
image-go₃ 0.5 / 16.9 (20% TO) 745.0 / 1153.7 (70% TO)
image-go₄ 0.6 / 2.1 1183.0 / 1406.8 (60% TO)
fyne₁ 1.9 / 10.5 3.0 / 4.5
fyne₂ 4.4 / 11.3 6.0 / 13.7

Key observations from the results [(Mezhuev et al., 30 Jan 2026), Table 1]:

  • Go-LibAFL-DiFuzz achieved the best TTE in 7/8 targets.
  • Achieved two to three orders of magnitude improvements in TTE on deep targets (e.g., image-go₃/₄).
  • go-fuzz exhibited lower average TTE in a subset of instances, attributed to broader mutation coverage and lower timeout rates, indicating the effect of generic vs. targeted mutation strategies.

This demonstrates the effectiveness of directed, proximity-guided fuzzing in surfacing deep or hard-to-reach defects in Go codebases.

5. Usability, Configuration, and Output

Workflow:

  1. Static Analysis and ETS Generation:
    1
    
    difuzz-go-analyze --src ./my-go-project --targets targets.toml --out ets.toml
  2. ETS Instrumentation:
    1
    
    difuzz-go-instrument-ets --src ./my-go-project --ets ets.toml --out ./my-go-instr1
  3. Coverage Instrumentation:
    1
    
    difuzz-go-instrument-cov --src ./my-go-instr1 --out ./my-go-instr2
  4. Build:
    1
    2
    
    export CGO_LDFLAGS="-L/path/to/libforkserver -lforkserver"
    go build -o myfuzz ./my-go-instr2
  5. Directed Fuzzing:
    1
    
    target/DiFuzz_Fuzzer --target ./myfuzz --corpus ./seed_corpus --artifacts ./findings --timeout 3600

Configuration options include: specifying the instrumented binary, corpus, artifact directory, timeout, logging level, and custom mutation dictionaries [(Mezhuev et al., 30 Jan 2026), Section 5.2].

Output formats:

  • Crash artifacts (crash-⟨n⟩.bin)
  • Periodic coverage summaries (coverage_report.json)
  • ETS trace logs (trace.log)

6. Limitations and Open Challenges

  • Instrumentation Stability: Source-level AST rewriting can fail or introduce instability with complex Go constructs (e.g., deeply nested generics, inline assembly).
  • Mutation Heuristics: Current mutation operators are general-purpose; directed fuzzing in Go could benefit from further context/language-specific mutational strategies.
  • ETS Expressiveness: Only flat, single-target ETSs are supported per fuzzing run. Multi-target or staged ETSs could improve scalability in projects with many target points.
  • Performance Overheads: Instrumentation overhead and forkserver/communication latency may marginally impact throughput.
  • Future Directions:
    • SSA-level passes for minimized instrumentation overhead
    • Adaptive and dynamic proximity weighting
    • Integration of hybrid symbolic and directed execution [(Mezhuev et al., 30 Jan 2026), Section 8]

A plausible implication is that extension to multi-target campaigns and context-sensitive mutation could further improve both reachability and crash discovery in large-scale Go projects.

7. Significance and Relation to Broader Work

Go-LibAFL-DiFuzz represents an application-specific adaptation of directed fuzzing adapted from the general LibAFL-DiFuzz system (Parygina et al., 2024) to Go. In contrast to coverage-guided fuzzers, which broadly explore execution space, directed fuzzers focus search resources toward known areas of concern—making them more suitable for bug reproduction, verification of static analysis alerts, and regression testing around security-sensitive boundaries (Mezhuev et al., 30 Jan 2026, Parygina et al., 2024).

Go-LibAFL-DiFuzz, as demonstrated in empirical studies, delivers higher efficiency (faster TTE, higher coverage rates) than go-fuzz in most benchmark scenarios, confirming the benefit of explicit distance-based feedback for nontrivial target reachability (Mezhuev et al., 30 Jan 2026). The system’s modular design, leveraging the combined Go AST/SSA stack for precise instrumentation and feedback, is generalizable to broader language ecosystems.


References:

Definition Search Book Streamline Icon: https://streamlinehq.com
References (2)

Topic to Video (Beta)

Whiteboard

No one has generated a whiteboard explanation for this topic yet.

Follow Topic

Get notified by email when new papers are published related to Go-LibAFL-DiFuzz.