ConceptioArchivearXiv CS
arXiv CSopen access

Thinking More, Harnessing Better: State Machine Guided Harness Automatic Generation with Project Digestion and Workflow Decomposition

Unknown · 2026 · arxiv_cs
arXiv CS · Papers · License: Open Access · 2026
Open Source ↗Direct PDF ↓
softwarearchitecturesoftwareengineeringtesting
software engineering, software architecture, testing

Thinking More, Harnessing Better: Automatic Harness Generation with Dataflow Aggregation and Workflow Decomposition Xing Zhang1,† , Zikang Huang2,1,† , Gang Yang3, , CongChong Wang1 , Lu Liu4,1 , Bin Yin1 , Mingyi Wang1 , Ziquan Zhao1 , Min Li1 , Zhenyu Chen1 , Bo Wu3 , Lingyun Ying1, 1 QI-ANXIN Technology Research Institute, Beijing, China 2 Wuhan University, Wuhan, China 3 Information Support Force Engineering University, Wuhan, China 4 Shandong University, Jinan, China † Both authors contributed equally to this research.

Corresponding authors: [email protected], [email protected]

arXiv:2607.07007v1 [cs.CR] 8 Jul 2026

Abstract High-quality fuzz harnesses are essential for effective gray-box fuzzing. While Large Language Models (LLMs) offer promise for automating this task, existing one-turn generation methods suffer from hallucinations and inadequate coverage due to coarsegrained function targeting and misaligned generation workflows. We present SynapseFlow, an automatic harness generator that addresses these limitations through two key innovations: dataflowaware function aggregation and a staged, rollback-enabled generation workflow decomposition. SynapseFlow first analyzes source code to construct Structural Flow Graphs and extract coherent Function Triplets. It then synthesizes harnesses via a decomposed fourstage process governed by a staged rollback algorithm to ensure correctness. We evaluated SynapseFlow on 25 real-world opensource software projects. The experimental results indicate that SynapseFlow outperforms state-of-the-art tools (OSS-Fuzz-Gen, CKGFuzzer, PromeFuzz), achieving 3.07×, 1.71×, and 4.26× higher branch coverage, and 1.77×, 1.51×, and 1.36× higher bug detection rates, respectively. Most importantly, SynapseFlow discovered 7 previously unreported bugs (5 assigned CVEs), demonstrating its practical effectiveness in real-world bug discovery.

CCS Concepts • Security and privacy → Software security engineering.

Keywords Fuzzing, Fuzzing Harness Generation, Large Language Model, Vulnerability Discovery

Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights for components of this work owned by others than the author(s) must be honored. Abstracting with credit is permitted. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Request permissions from [email protected]. CCS ’26, The Hague, The Netherlands © 2026 Copyright held by the owner/author(s). Publication rights licensed to ACM. ACM ISBN 978-1-4503-XXXX-X/XXXX/XX https://doi.org/XXXXXXX.XXXXXXX

ACM Reference Format: Xing Zhang1,† , Zikang Huang2,1,† , Gang Yang3, , CongChong Wang1 , Lu Liu4,1 , Bin Yin1 , Mingyi Wang1 , Ziquan Zhao1 , Min Li1 , Zhenyu Chen1 , Bo Wu3 , Lingyun Ying1, . 2026. Thinking More, Harnessing Better: Automatic Harness Generation with Dataflow Aggregation and Workflow Decomposition. In Proceedings of Proceedings of the 2026 ACM SIGSAC Conference on Computer and Communications Security (CCS ’26). ACM, New York, NY, USA, 20 pages. https://doi.org/XXXXXXX.XXXXXXX

1

Introduction

Fuzzing is a cornerstone technique in modern software security analysis. Among its various paradigms, white-box fuzzing, which leverages source code analysis to guide test generation, offers the potential for deep program exploration. High-quality fuzz harnesses—code segments that invoke target functions with fuzzing inputs—are pivotal for effective function-oriented gray-box fuzzing. They enable direct testing of internal program logic, circumventing the coverage limitations often encountered when fuzzing only through external application interfaces. This need is particularly urgent for C projects, which constitute the backbone of critical system software, libraries, and embedded systems. Due to the prevalence and severity of memory safety vulnerabilities in C, achieving comprehensive coverage is essential [1]. However, modern C applications and libraries exhibit intricate function dependencies, making comprehensive coverage via whole-application fuzzing challenging [2]. Consequently, functionoriented fuzzers, which rely on specialized harnesses to test individual functions directly, have gained widespread adoption [3]. The core challenge lies in automatically synthesizing such harnesses, which demands a deep understanding of source code to logically compose relevant functions. Large Language Models (LLMs), with their strong code comprehension and generation capabilities, have thus emerged as promising candidates for this task [4, 5]. Prior research endeavors have integrated prompt engineering with proprietary LLMs to generate harnesses through different combinations of API mutations [6]. Those methods employ zero-shot or few-shot prompting and have also explored various code composition strategies [6–9]. However, prevalent LLM-based approaches are hampered by a monolithic generation paradigm, leading to two core shortcomings: 1) imprecise, often ad-hoc function selection and 2) severe LLM hallucinations. Firstly, lacking systematic dataflow

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

analysis leads to ad-hoc function selection (e.g., misidentifying internal functions), which fails to form a minimal, coherent set for effective testing. This yields harnesses that are either incomplete or burdened with irrelevant code. Secondly, the monolithic generation task commonly overwhelms LLMs, causing hallucinations (e.g., generating incorrect or redundant code) that cripple the harness’s correctness and effectiveness. These limitations collectively result in low code coverage and missed vulnerabilities. Therefore, generating high-quality harnesses presents a dual challenge: 1) intelligently identifying and aggregating a functionally coherent cluster of functions, and 2) orchestrating a robust, multi-step synthesis process that mitigates LLM errors. This process inherently involves a trade-off between granularity (testing focused units) and comprehensiveness (covering enough logic to trigger deep bugs) [10]. More importantly, a well-designed harness should ensure that any crash it produces during fuzzing is a meaningful, reproducible bug in the original application context, not an artifact of the harness itself. Thus, automated harness generation can be viewed as a constrained optimization and planning problem over the project’s function call graph. To address these issues, we propose a paradigm shift: moving from monolithic LLM invocation to a structured, decompositionbased workflow akin to expert human reasoning. This workflow first digests the project to form semantically meaningful function groups, then decomposes the code writing into manageable, verifiable steps to synthesize a high-coverage harness. Furthermore, the process incorporates feedback and rollback mechanisms inspired by task decomposition for AI agents [11] to contain errors and hallucinations. Following this rationale, achieving high-quality, automated graybox harness generation necessitates addressing the following two core research questions: How to identify and group involved functions for harness generation from source code? This requires analyzing source code to identify functions that process security-critical data (often from external inputs) and group them logically. A key challenge is to move beyond simple metrics (e.g., complexity) and understand dataflow, parameter semantics, and functional cohesion to form minimal yet sufficient groups. Current LLM-only approaches lack the precise program analysis for this. How to mitigate the risks of hallucinations and error accumulation in the long-chain generation workflow? When tasked with generating complex harness code in one turn, LLMs frequently hallucinate details: they may invent non-existent functions, misuse macros, or insert inefficient I/O operations (e.g., fprintf), leading to compilation failures or crippled fuzzing performance. While some methods inject knowledge via prompts [9, 12], this proves inadequate for long, complex generation chains. A robust solution requires decomposing the workflow into sequential, verifiable sub-tasks with built-in error detection and recovery. To tackle these challenges, we propose SynapseFlow, an LLMpowered framework grounded in dataflow aggregation and workflow decomposition for automatic harness generation. It first employs hybrid static analysis and LLM reasoning to digest the whole project and construct minimal, coherent function groups. It then orchestrates a multi-stage generation workflow with a staged rollback algorithm to ensure correctness and mitigate hallucinations. This

Xing Zhang, Zikang Huang, Gang Yang, Lingyun Ying, and et al.

constitutes a paradigm shift from prior monolithic generation: generation steps are dictated by extracted dataflow, and the workflow self-corrects via targeted rollback to isolate hallucinations. We evaluate SynapseFlow on 25 real-world, open-source C projects. It significantly outperforms state-of-the-art (SOTA) tools (OSS-FuzzGen [5], CKGFuzzer [9], and PromeFuzz [12]), achieving 3.07x, 1.71x, and 4.26x higher branch coverage and 1.77x, 1.51x, and 1.36x higher crash discovery rates, respectively. Notably, even on these extensively fuzzed projects, harnesses generated by SynapseFlow led to the discovery of 7 previously unreported bugs (4 assigned CVEs), demonstrating its ability to find deep, overlooked Bugs. This paper makes the following contributions: • A Method for Dataflow-Aware Function Aggregation: We propose a novel method integrating lightweight static dataflow analysis with LLM-based semantic reasoning to identify and aggregate functionally related functions into minimal, self-contained groups. This provides a sound foundation for harness generation beyond API-level testing. • A Framework for Robust, Decomposed Synthesis: We design a stepwise and reversible prompting framework governed by a staged rollback algorithm. This decomposes complex harness synthesis into a sequence of manageable LLM sub-tasks, effectively mitigating hallucinations and error propagation common in single-prompt approaches. • Implementation and Extensive Evaluation: We implement our approach in SynapseFlow, an automated harness generation tool for C programs. Extensive evaluation on real-world projects shows that SynapseFlow-generated harnesses achieve higher coverage and uncover more bugs than SOTA tools, including the discovery of 7 previously unreported bugs.

2 Background and Motivation 2.1 Harness in Gray-Box Fuzzing In the context of gray-box fuzzing for C source projects, a fuzz harness (or fuzz driver) is a specialized piece of code that acts as a test interface for a specific functional unit. Its primary role is to receive raw fuzzing input (typically a byte array), transform this input into the appropriate program state (e.g., by initializing data structures), and then invoke a sequence of target functions to test their behavior under varied inputs. By isolating and directly testing internal components, harnesses enable more focused and efficient vulnerability discovery compared to whole-application fuzzing. A high-quality harness must satisfy several critical criteria to be effective in practice: • Functional Completeness: It must invoke a logically coherent and minimal set of functions that together implement a meaningful feature. Omitting necessary functions leads to low coverage; including irrelevant ones adds noise and reduces fuzzing efficiency. • Semantic Correctness: The harness must respect the usage specifications and data dependencies of each invoked function (e.g., correct parameter types, proper initialization order, and valid state transitions).

SYNAPSEFlow

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

• Syntactic Soundness: The generated code must be compilable and free of syntax errors. It should also avoid introducing constructs that hinder fuzzing performance, such as file I/O operations or excessive logging. Crafting such a harness manually requires deep understanding of the codebase and is labor-intensive, motivating the need for reliable automation. Existing automated methods, as we discuss next, often fall short of meeting these quality standards.

2.2

The core issue is that these tools fail to identify the relationships formed by input and output structures between functions. We define the structural flow graph (SFG) as the representation of these structural dependencies that logically connects functions via their struct parameters. Without this perspective, existing tools cannot derive the SFG (illustrated in Figure 2) required to group the seven functions correctly. Furthermore, their monolithic generation workflows offer no chance to detect and rectify the introduced hallucinations or logical errors.

Motivation Example

To ground the aforementioned challenges in a concrete scenario, we use the j40 project [16], a widely-used C library for parsing JPEG XL images, as a running example. Constructing an effective harness for j40 requires invoking a specific sequence of functions that manipulate core data structures, as shown in Figure 1 (Listing 1) and as listed in Table 1. Table 1: Functions with key data structures in the j40 project. Seq. 1 2 3 4 5 6 7

• OSS-Fuzz-Gen (Listing 2) focuses on single-function generation. It completely misses the necessary dataflow, selecting only two unrelated functions and producing a harness that cannot meaningfully test the library (line 7), failing functional completeness. • CKGFuzzer (Listing 3) attempts to use external knowledge but lacks fine-grained dataflow understanding. It generates a plausible but incomplete sequence (only two functions in line 11, 13) and introduces unnecessary operations like manual memory allocation/copying (malloc, memcpy), which is noisy and potentially incorrect, thus compromising semantic correctness. • PromeFuzz (Listing 4) identifies more related functions (line 7, 17) yet still fails to cover the full group. It erroneously includes j40_from_memory and j40_from_file (line 5, 14), introducing two external input handlers and violating the single-entry principle of libfuzzer. The added file operations (line 10) further compromise fuzzing efficiency.

Harness Generation

Automated harness generation techniques can be broadly categorized into two paradigms: traditional program-analysis-based methods and modern LLM-based approaches. Traditional methods (e.g., template-based [13], slicing-based [14, 15]) rely on static/dynamic analysis. Their synthesis methods are deterministic but rigid, often failing to generate complex logic and lacking semantic understanding, which leads to poor functional completeness. LLM-based methods (e.g., OSS-Fuzz-Gen, CKGFuzzer, PromeFuzz) leverage code comprehension to produce more sophisticated code. However, they introduce new critical flaws: 1) their function selection strategies are ad-hoc, lacking principled, dataflow-aware aggregation of functions; 2) they predominantly use a monolithic, oneshot generation workflow, which is prone to LLM hallucinations and error cascades without recovery mechanisms. Consequently, they often compromise semantic correctness and functional completeness. In essence, existing methods fall short because they fail to address two core aspects of the problem: systematic function grouping and robust, error-tolerant generation.

2.3

process multiple frames) and syntactic soundness (no extraneous operations). In contrast, the auto-generated harnesses reveal the limitations of current methods:

Function Name j40_from_memory j40_output_format j40_next_frame j40_current_frame j40_frame_pixels j40_row j40_free

Input Struct ByteStream j40_image j40_image j40_image j40_frame j40_pixels j40_image

Output Struct j40_image

j40_frame j40_pixels

Figure 1 contrasts a high-quality, developer-written harness (Listing 1) with harnesses generated by SOTA LLM-based tools (Listings 2, 3, 4). The ideal harness (Listing 1) demonstrates functional completeness by correctly sequencing all seven key functions, following the dataflow from j40_image to j40_pixels. It also exhibits semantic correctness (e.g., proper initialization before use, a loop to

2.4

Problem Definition

The motivating example crystallizes the core task of this work: automatically generating high-quality fuzz harnesses for C source code projects. To achieve this automatically, the process must fundamentally address two intertwined sub-problems: Coherent Function Grouping: Given a source project 𝑆, identify a set of functions 𝐺 = {𝑓1, 𝑓2, ..., 𝑓𝑘 } that collectively implement a distinct, testable feature. The grouping must be grounded in the actual dataflow between functions (via shared data structures), not just call relations or semantic similarity, to ensure functional completeness. Robust Harness Synthesis: Given a well-formed function group 𝐺, synthesize a harness 𝐻 that correctly sequences the calls to functions in 𝐺, manages all necessary data structures, and adheres to C semantics. The synthesis process must be robust against LLM hallucinations and errors. To tackle the grouping issue, we require a principled model for understanding and categorizing function-data interactions. To group functions effectively based on dataflow, we analyze their roles in processing data structures. We define three semantic categories for functions in a project: • Input Stream Function (ISF): A function that serves as a unique entry point, consuming unstructured, external data (e.g., from memory, a file) and producing or initializing an

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

1 int LLVMFuzzerTestOneInput ( const uint8_t * data ,

Xing Zhang, Zikang Huang, Gang Yang, Lingyun Ying, and et al.

1 int LLVMFuzzerTestOneInput ( const uint8_t * data ,

size_t size ) { 2

j40_image image ;

2

3

j40_from_memory (& image ,( void *) data , size , NULL ) ;

3

j40_err err ;

4

j40_output_format (& image , J40_RGBA , J40_U8X4 ) ;

4

5

while ( j40_next_frame (& image ) ) {

5

size_t Size ) { 2

j40_image * image = ( j40_image *) calloc (1 ,

if ( size < 1) {

3

if (! image ) return 0;

return 0;}

4

if ( Size > 0) {

j40_image image ;

sizeof ( j40_image ) ) ;

6

j40_frame frame = j40_current_frame (& image ) ;

6

void * input_buffer = malloc ( size ) ;

7

j40_pixels pixels ;

7

if (! input_buffer ) {

8

pixels = j40_frame_pixels (& frame , J40_RGBA ) ;

8

9

for ( int y = 0; y < pixels . height ; ++ y ) {

9

10

j40_row ( pixels , y ) ;}}

11

j40_free (& image ) ;

12

return 0;}

1 int LLVMFuzzerTestOneInput ( const uint8_t * Data ,

size_t size ) {

6

if ( err != 0) {

7

10

memset (& image , 0 , sizeof ( image ) ) ;

11

err = j40_from_memory (& image , input_buffer ,

12

if ( err == 0) {

13

j40_err err = j40_from_memory ( image , ( void *) Data , Size , NULL ) ;

return 0;} memcpy ( input_buffer , data , size ) ;

const char * err_str = j40_error_string ( image ) ;}

8 9

size , nullptr ) ;

Listing 1: An idealical example harness

5

else { while ( j40_next_frame ( image ) == 0) {}}}

int result = j40_next_frame (& image ) ;}

10

FILE * input = fopen ( " input " , " wb "); if ( input ) {

14

free ( input_buffer ) ;

11

15

return 0;}

12

fwrite ( Data , 1 , Size , input ) ;

size_t size ) {

13

fclose ( input ) ;

2

j40_image * image ;

3

j40_frame frame ;

Listing 3: A harness generated by 14 15 CKGFuzzer

if ( e != 0) {

4

if ( size < sizeof ( j40_image ) ) {

1 int LLVMFuzzerTestOneInput ( const uint8_t * data ,

5

16

j40_err e = j40_from_file ( image , " input "); const char * err_str = j40_error_string

return 0;}

( image ) ;}}

6

image = ( j40_image *) data ;

17

j40_free ( image ) ;

7

frame = j40_next_frame ( image ) ;

18

return 0;}

8

return 0;}

Listing 4: A harness generated by PromeFuzz

Listing 2: A harness generated by OSSFuzz-Gen

Figure 1: A comparison of harnesses generated by distinct approaches for j40 project. j40_from_memory(ByteStream) j40_image j40_next_frame(j40_image) j40_output_format(j40_image) j40_current_frame(j40_image) j40_frame j40_frame_pixels(j40_frame) j40_pixels j40_row(j40_pixels) j40_free(j40_image)

Figure 2: Structural flow graph for j40 depicting data structure transformations between key APIs, which underpins the logical grouping of functions for harness generation.

internal data structure, e.g., j40_from_memory in the motivation example. • Helper Function (HPF): A function responsible for the lifecycle management (allocation, initialization, deallocation) of a data structure, e.g., j40_free and j40_from_memory in the motivation example. • Process Function (PRF): A function that transforms, validates, or reads the content of an already-initialized data structure but does not manage its lifecycle, e.g., j40_next_ frame, j40_frame_pixels and j40_row in the motivation example. Noteworthy, a function can belong to multiple categories simultaneously, e.g., j40_from_memory in the motivation example is both an ISF and an HPF. Building upon these categories, we define Function Triplet (FT) as the atomic unit for harness generation. An FT represents a minimal, self-contained data processing unit centered around one primary data structure flow. Formally, an FT is an ordered triplet: 𝐹𝑇 = (𝐼, 𝑃, 𝐻 ) subject to the following constraints:

• 𝐼 is a unique singleton ISF. • 𝑃 is an optional set of PRFs. • 𝐻 is an optional set of HPFs. We can formalize FT as:

𝐹𝑇 = (𝐼, 𝑃, 𝐻 )

 |𝐼 | = 1     𝐼 ⊆ F 

𝐼𝑆𝐹

where

 𝑃 ⊆ F𝑃𝑅𝐹 ∪ ∅    𝐻 ⊆ F 𝐻𝑃𝐹 ∪ ∅  where F𝐼𝑆𝐹 , F𝑃𝑅𝐹 and F𝐻 𝑃 𝐹 denote the sets of all ISFs, PRFs and HPFs in the project, respectively, and | · | denotes set cardinality. A source project 𝑆 can be digested into multiple, distinct FTs, each anchored by a different ISF: 𝑆 → {𝐹𝑇1, 𝐹𝑇2, . . . , 𝐹𝑇𝑛 }, where each 𝐹𝑇𝑖 = (𝐼𝑖 , 𝑃𝑖 , 𝐻𝑖 ) and ∀𝑖 ≠ 𝑗, 𝐼𝑖 ≠ 𝐼 𝑗 . The process of discovering these FTs from source code directly addresses the grouping issue. The FT provides a semantically coherent function group (𝐺 = 𝐼 ∪ 𝑃 ∪ 𝐻 ) that is grounded in dataflow (the input/output structures connecting 𝐼 , 𝑃, and 𝐻 ). For our running example, the functions in Table 1 naturally form a single FT: 𝐼 = {j40_from_memory}, while 𝑃 = {j40_output_format, j40_next_frame, j40_current_frame, j40_frame_pixels, j40_ row}, 𝐻 = {j40_from_memory, j40_free}. This model elegantly captures the functional completeness requirement.

2.5

Challenges

Given the problem definition above, realizing an automated solution like SynapseFlow involves overcoming two major implementation challenges that correspond to sub-problems (in Section 2.4): Challenge 1: Dataflow-Aware Function Grouping. How can we accurately identify ISF, HPF, and PRF functions from C source

SYNAPSEFlow

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

code and cluster them into correct FTs? This requires going beyond syntactic call graphs to understand how data structures are created, transformed, and passed between functions—a task that demands a fusion of lightweight static analysis for structural tracking and LLM-based reasoning for semantic role disambiguation. Challenge 2: Hallucination-Robust Multi-Stage Synthesis. Given an FT, how can we design a generation workflow that decomposes the complex harness synthesis task into a sequence of simpler, verifiable sub-tasks (e.g., structure definition, initialization, function chaining, cleanup)? This workflow must incorporate mechanisms for error detection and recovery (e.g., rollback) to prevent hallucinations and error cascades, ensuring the final harness is both semantically correct and syntactically sound. The design of SynapseFlow, presented in the next section, is our concrete response to these challenges.

3 Methodology 3.1 Overview SynapseFlow automates high-quality harness generation for source code by decomposing the process into two core phases (Figure 3) that directly address the challenges defined in Section 2.5. Phase 1: Function Grouping with Dataflow Aggregation. This phase digests the source project (Section 3.2.1) to extract coherent FTs—minimal, testable function groups grounded in dataflow. The process involves classifying functions by semantic role (ISF/PRF/HPF), constructing a SFG to model structure propagation (Section 3.2.2), and applying graph algorithms to extract FTs (Section 3.2.3). Phase 2: Harness Generation with Workflow Decomposition. Given an FT, this phase synthesizes the final harness. To mitigate the hallucinations and errors inherent in monolithic LLM prompting, we decompose synthesis into a sequence of four simpler, verifiable stages (Section 3.3.1). Meanwhile, a staged rollback algorithm (Section 3.3.2) orchestrates this workflow, enabling recovery from failures by rolling back to previous stages. Running Example. We continue with the j40 project. Phase 1 constructs its SFG (Figure 2) and extracts an FT (Table 2) containing the seven key API functions. Phase 2 then transforms this FT into the high-quality harness of Listing 1. The following subsections detail each phase. Table 2: Function Triplet of the j40 project. Functions are annotated with their types: ISF, PRF, and HPF. Function Name j40_from_memory j40_output_format j40_next_frame j40_current_frame j40_frame_pixels j40_row j40_free

Type ISF&HPF PRF PRF PRF PRF PRF HPF

Structural Flow (null), j40_image j40_image, (null) j40_image, j40_frame j40_frame, j40_pixels j40_pixels, (null)

3.2

Function Triplet Extraction with Dataflow Aggregation

This phase aims to automatically discover FTs from the source code. An FT represents a minimal, coherent set of functions that should be tested together, grounded in their dataflow dependencies. The extraction involves three main steps: 1) annotating each function with its semantic type (ISF, PRF, or HPF), 2) constructing an SFG that models data structure propagation between functions, and 3) applying a graph algorithm to the SFG to extract the FTs. We now elaborate on each step. 3.2.1 Function Annotation. The goal of this step is to label each function in the project as an ISF, PRF, or HPF according to the definitions in Section 3.1. Accurate annotation requires understanding both syntactic features (parameter types) and functional semantics (what the function does). We therefore employ a hybrid approach that combines lightweight static analysis for syntax with LLM-based reasoning for semantics. Identifying ISFs. We first use syntax analysis to filter functions that have pointer-type parameters (e.g., void*, uint8_t*) which could represent raw byte streams. For these candidate functions, we need to distinguish true byte-stream handlers from those that process semantic strings (e.g., filenames). We leverage the LLM’s semantic understanding through a set of distinct prompt templates (detailed in Appendix B.1). These prompts ask the LLM to judge, in different formats, whether a specific parameter represents a contiguous byte input. Next, an LLM uses multiple distinct prompts (detailed in Appendix B.2) to filter functions that reference specific meaningful strings, such as those passed by references, filenames, or path names. A voting mechanism on the LLM’s responses yields the final identification of ISFs. Distinguishing PRFs and HPFs. This is a two-stage process. First, syntax analysis isolates functions that operate on structs (have struct parameters or return a struct). Second, for these candidate functions, specialized LLM prompts (Appendix B.3) infer their functional role. Functions described as performing initialization or cleanup of a struct are labeled HPFs. Functions described as manipulating, transforming, or reading an already-initialized struct are labeled PRFs. A single function can possess multiple labels (e.g., a function that reads a stream and allocates a struct is both an ISF and an HPF). We treat these labels as descriptive attributes. 3.2.2 Structural Flow Graph Construction. The goal of this step is to construct an SFG, a directed graph that provides a high-level abstraction of how data structures flow between functions. This graph is crucial for understanding the connectivity and dependencies that will guide FT extraction. Structure Directionality Inference. To build the SFG, we must first determine each function’s input and output structures. Return types and non-pointer struct parameters are straightforward. For ambiguous pointer-to-struct parameters, we need to infer if they are input (read-only), output (write-only), or both. We resolve this by prompting the LLM to analyze the function’s operational semantics on that parameter. Graph Formalization. With input/output structures identified, we formalize the SFG as a directed graph 𝐺 = (𝑉 , 𝐸):

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

Xing Zhang, Zikang Huang, Gang Yang, Lingyun Ying, and et al.

Step 1 Function Annotation

Step 4 Staged-rollback Harness Generation HPF

ISF:

Input Struct Ouput Struct

func3 doc doc func2

4 nc

func3 doc

fu

func4 sig

Function Triplet Flow Graph

Stage 1 Func Doc Generation

Rough Code of FT

Stage 2 Edge Snippet Stitching

Stage 3 Rough Code Assembly

Harness Code

Stage 4 Harness Optimation

Validataion

c3

C Project Source Code

Pointer Reference Analysis

func2 func3 HPF: func_4 func_4 func4

fun

Functions

c2

f un c4

fun

PRF:

func3

func1

...

func1 doc

func1

Step 2 Structural Flow Graph Construction

func2

PRF

Annotated Functions

Functionality Analysis

1 nc fu

ISF

Step 3 Function Triplet Extraction

func5 Structural Flow Graph

Phase 1: Function Grouping with Dataflow Aggregation (§ 3.2)

Staged Rollback Algorithm

Phase 2: Harness Generation with Workflow Decomposition (§ 3.3)

Figure 3: The workflow of SynapseFlow. • Nodes (𝑉 ): Each node represents a unique structure type. A special “(null)” node represents the absence of an input/output structure. • Edges (𝐸): A directed edge 𝑒 = ⟨𝑛𝑖𝑛 , 𝑛𝑜𝑢𝑡 ⟩ ∈ 𝐸 represents a function 𝑓 that consumes an input structure 𝑛𝑖𝑛 and produces an output structure 𝑛𝑜𝑢𝑡 . The edge is labeled with 𝑓 . By processing all project functions, we construct the complete SFG. For our running example, the SFG for j40 is visualized in Figure 2, clearly showing the flow from ByteStream to j40_pixels. 3.2.3 Function Triplet Extraction. The goal of this final step is to extract one FT for each unique ISF in the project. The ISF serves as the natural entry point and anchor for a harness. Our extraction algorithm, detailed in Algorithm 1, works on the annotated SFG. Extraction Process. For a given ISF 𝑓𝐼𝑆𝐹 , the algorithm first prunes the SFG by removing edges corresponding to all other ISFs (their harnesses will be generated separately). From the pruned graph, it performs a forward dataflow analysis starting from the ISF’s output structure, collecting all reachable functions. It also performs a backward analysis if needed. The functions within this reachable subgraph are then filtered based on their annotations: all PRFs are included, and relevant HPFs (e.g., destructors for allocated structures) are added. The resulting set {𝑓𝐼𝑆𝐹 } ∪ 𝑃 ∪ 𝐻 forms the FT. A function appearing in multiple FTs (e.g., a common free function) is handled appropriately in each harness. Handling Multi-role Functions. A key nuance is handling functions with multiple annotations (e.g., a function that is both an ISF and a PRF). During pruning, only its ISF role is removed from the graph for other FTs; it remains present for its PRF role. If a function in the final FT is both a PRF and an HPF, it is treated as a PRF for generation priority, ensuring the core processing logic is captured.

3.3

Harness Generation with Staged Decomposition and Rollback

This phase takes an FT as input and synthesizes the corresponding fuzz harness. The core insight is that generating the entire harness in one LLM call is error-prone. Therefore, we decompose the task into a sequence of four simpler stages, each with a clear objective and validation criteria. A staged rollback algorithm orchestrates these stages, providing fault tolerance by allowing the process to roll back to a previous stage upon failure. The detail theoretical

Algorithm 1 Function Triplet Extraction Algorithm Require: 𝑓𝐼𝑆𝐹 = (𝑛𝑖𝑛 , 𝑛𝑜𝑢𝑡 ), F𝐼𝑆𝐹 ,F𝑃𝑅𝐹 ,F𝐻 𝑃 𝐹 , Structural Flow Graph 𝐺. Ensure: FT for 𝑓𝐼𝑆𝐹 . 1: 𝑛𝑒𝑤𝐺 ← 𝐺.remove_edges_from(F𝐼𝑆𝐹 − {𝑓𝐼𝑆𝐹 }) 2: 𝐼𝑛𝑁𝑜𝑑𝑒𝑠 ← 𝑛𝑒𝑤𝐺.ancestors(𝑛𝑖𝑛 ) + 𝑛𝑖𝑛 3: 𝑂𝑢𝑡𝑁𝑜𝑑𝑒𝑠 ← 𝑛𝑒𝑤𝐺.descendants(𝑛𝑜𝑢𝑡 ) + 𝑛𝑜𝑢𝑡 4: 𝐹𝑇𝐺𝑟𝑎𝑝ℎ ← 𝑛𝑒𝑤𝐺.subgraph(𝐼𝑛𝑁𝑜𝑑𝑒𝑠 + 𝑂𝑢𝑡𝑁𝑜𝑑𝑒𝑠) 5: 𝐸𝑑𝑔𝑒𝑠 ← 𝐹𝑇𝐺𝑟𝑎𝑝ℎ.edges() 6: 𝐹 𝑃𝑅𝐹 ← {𝑓 |𝑓 ∈ 𝐸𝑑𝑔𝑒𝑠, 𝑓 ∈ F𝑃𝑅𝐹 } 7: 𝐹𝐻 𝑃 𝐹 ← {𝑓 |𝑓 ∈ 𝐸𝑑𝑔𝑒𝑠, 𝑓 ∈ F𝐻 𝑃 𝐹 , 𝑓 ∉ 𝐹 𝑃𝑅𝐹 } 8: 𝐹𝑇 ← (𝑓𝐼𝑆𝐹 , 𝐹 𝑃𝑅𝐹 , 𝐹𝐻 𝑃 𝐹 ) 9: return 𝐹𝑇

proof of the advantages of decomposition and rollback is shown in Appendix A. 3.3.1 Stage Design. The stage decomposition follows a progressive refinement strategy. We transform the complex goal of “generate a full harness” into simpler sub-goals: first understand the functions, then generate code for local data transformations, then assemble the pieces, and finally polish the result. This approach aligns with the LLM’s capabilities and limits context length per step. Stage 1: Function Documentation Generation. The goal is to convert the raw source code of each function in the FT into structured, textual API documentation. This includes the function signature, a description of its purpose, its usage scenario, and example invocation patterns. This step abstracts away low-level syntax, providing the LLM with high-level semantic knowledge for subsequent code synthesis (by employing prompts in Appendix B.4). For our j40 example, this stage converts the seven functions in Table 2 into their corresponding API documentation. Stage 2: Structure Snippet Stitching. The goal is to generate small, correct code snippets for localized data transformations. Using the SFG, we identify groups of functions that share the same input and output structure (forming a processing “unit”). For each unit, the LLM is prompted to generate a code snippet that correctly sequences the involved function calls to transform the input structure into the output structure. This breaks down the global code

SYNAPSEFlow

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

generation problem into manageable local problems. Figure 4 illustrates this process for the five snippets identified in j40’s FT (Table 2). The prompt used is listed in Appendix B.5. j40_from_memory(null-image) j40_next_frame image-null j40_output_format snippet j40_current_frame(image-frame) j40_frame_pixels(frame-pixels) j40_row(pixels-null) j40_free

... j40_output_format(image,...); while(!j40_next_frame(image)){ ...

Figure 4: Structural processing snippet generation for the j40 project (Stage 2). Each snippet corresponds to a coherent data transformation identified from the FT in Table 2. Stage 3: Rough Code Assembly. The goal is to combine the individual snippets from Stage 2 into a complete, executable prototype. The LLM iteratively merges adjacent snippets (following the dataflow order in the SFG) by writing code that connects their inputs and outputs using the prompt in Appendix B.6. The output of this stage is a rough but complete code sequence that correctly invokes all functions in the FT. Figure 5 shows the assembly process for j40, where the five snippets are logically merged. Sruct Snippets null-image Gen Code image-null I:ByteStream O:j40_frame image-frame frame-pixels pixels-null j40_free

Gen Code I:ByteStream O:j40_pixels

Gen Code I:ByteStream O:null

Rough Code I:ByteStream O:

... j40_image *image = malloc(sizeof(j40_image)); int size = 2000; uint8 *data = malloc(size); j40_from_memory(image, data, size, NULL); j40_output_format(image,...); while(!j40_next_frame(image)){ j40_frame f = j40_current_frame(image,...); j40_pixels p = j40_frame_pixels(&f,...); for(int i=0;i<p.weight;i++){ printf(...); j40_row(p,i);} j40_free(image);

Figure 5: Rough code assembly for the j40 project (Stage 3). Structural snippets are iteratively merged following the dataflow order. Stage 4: Code Optimization and Harness Transformation. The goal is to convert the rough prototype into a final, fuzzingready harness. The LLM performs several tasks: it wraps the code into the standard LLVMFuzzerTestOneInput function signature, removes any operations harmful to fuzzing performance (e.g., file I/O, excessive prints), and ensures proper resource cleanup. The output is the final harness code, which for j40 matches the highquality harness shown in Listing 1. 3.3.2 Staged Rollback Algorithm. Decomposing the workflow improves quality but introduces more steps. To manage potential failures efficiently and avoid restarting from scratch, we introduce the staged rollback algorithm (Algorithm 2). Its core idea is simple:

if the final harness (Stage 4 output) fails compilation or a basic runtime test, instead of discarding all previous work, we roll back to the output of Stage 3 and regenerate Stage 4. If repeated failures occur, we roll back further (to Stage 2, then Stage 1). This provides a cost-effective recovery mechanism, saving significant LLM inference time compared to a full restart. Algorithm Mechanics. The algorithm maintains a current stage pointer (cur) and a rollback target pointer (cycState). It executes stages sequentially from 1 to 4. Upon a Stage 4 failure, the failure counter (regen) increments, and the process rolls back to cycState (initially Stage 3). If failures persist (exceeding a threshold), cycState is set to an earlier stage, enabling a deeper rollback. This mechanism ensures that persistent errors in later stages can be addressed by regenerating earlier, potentially problematic components. Algorithm 2 Staged Rollback Algorithm for Harness Generation Require: 𝑟𝑒𝑔𝑒𝑛, 𝑐𝑢𝑟 , 𝑐𝑦𝑐𝑆𝑡𝑎𝑡𝑒 and state union {𝑠𝑡𝑎𝑔𝑒1, 𝑠𝑡𝑎𝑔𝑒2, 𝑠𝑡𝑎𝑔𝑒3, 𝑠𝑡𝑎𝑔𝑒4}. Ensure: 𝐻𝑎𝑟𝑛𝑒𝑠𝑠 code or FAIL. 1: 𝑐𝑢𝑟 ← 𝑠𝑡𝑎𝑔𝑒1 2: 𝑐𝑦𝑐𝑆𝑡𝑎𝑡𝑒 ← 𝑠𝑡𝑎𝑔𝑒4 ⊲ Initial rollback target 3: 𝑟𝑒𝑔𝑒𝑛 ← 0 4: while 𝑇𝑟𝑢𝑒 do 5: if 𝑐𝑢𝑟 == 𝑠𝑡𝑎𝑔𝑒1 then 6: 𝑔𝑒𝑛𝐴𝑃𝐼𝐷𝑜𝑐 () 7: else if 𝑐𝑢𝑟 == 𝑠𝑡𝑎𝑔𝑒2 then 8: 𝑔𝑒𝑛𝑆𝑡𝑟𝑢𝑐𝑡𝑢𝑟𝑒𝐶𝑜𝑑𝑒 () 9: else if 𝑐𝑢𝑟 == 𝑠𝑡𝑎𝑔𝑒3 then 10: 𝑔𝑒𝑛𝑅𝑜𝑢𝑔ℎ𝐶𝑜𝑑𝑒 () 11: else if 𝑐𝑢𝑟 == 𝑠𝑡𝑎𝑔𝑒4 then 12: 𝑐𝑜𝑑𝑒 ← 𝑔𝑒𝑛𝐻𝑎𝑟𝑛𝑒𝑠𝑠𝐶𝑜𝑑𝑒 () 13: 𝑟𝑒𝑠𝑢𝑙𝑡 ← 𝑐𝑜𝑚𝑝𝑖𝑙𝑒_𝑎𝑛𝑑_𝑟𝑢𝑛𝐶ℎ𝑒𝑐𝑘 (𝑐𝑜𝑑𝑒) 14: if 𝑟𝑒𝑠𝑢𝑙𝑡 then return 𝑐𝑜𝑑𝑒 ⊲ Success 15: else 16: 𝑟𝑒𝑔𝑒𝑛 ← 𝑟𝑒𝑔𝑒𝑛 + 1 17: 𝑐𝑢𝑟 ← 𝑐𝑦𝑐𝑆𝑡𝑎𝑡𝑒 ⊲ Rollback to target stage 18: if 𝑟𝑒𝑔𝑒𝑛 > 3 then ⊲ Too many attempts 19: if 𝑐𝑦𝑐𝑆𝑡𝑎𝑡𝑒 == 𝑠𝑡𝑎𝑔𝑒1 then return FAIL ⊲ Fail 20: end if 21: 𝑟𝑒𝑔𝑒𝑛 ← 0 22: 𝑐𝑦𝑐𝑆𝑡𝑎𝑡𝑒 ← 𝑐𝑦𝑐𝑆𝑡𝑎𝑡𝑒 − 1 ⊲ Move rollback target earlier 23: end if 24: end if 25: end if 26: 𝑐𝑢𝑟 ← 𝑐𝑢𝑟 + 1 ⊲ Proceed to next stage 27: end while

3.4

Implementation

We implemented SynapseFlow in Python, using tree-sitter [17] for lightweight, robust syntax analysis. This approach trades off precise dataflow for generality, with subsequent LLM reasoning (Section 3.2.1) resolving ambiguities. The codebase comprises approximately 4.7K lines of code and 139 prompt templates (modular components constituting our multi-stage pipeline; we list only the

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

core templates in the Appendix B, with the full set available in our repository). While our methodology is general, the current implementation targets C code. Below, we highlight key implementation decisions that differentiate our approach. Prompt Design Strategy. SynapseFlow uses LLMs for two distinct tasks: function identification (in the grouping phase) and code generation (in the harness phase). For identification tasks (e.g., determining whether a parameter is a byte stream), validation is inherently difficult. We therefore employ a three-prompt voting scheme: we craft three prompt variants (direct extraction, yes/no question, multiple-choice) for the same query and take the majority vote of the LLM’s responses. This significantly improves accuracy over single-prompt or repeated-same-prompt approaches. For code generation tasks, we rely on syntactic validation and the rollback algorithm for error correction. Validation of Intermediate Outputs. Each stage’s output undergoes lightweight syntactic validation before proceeding. We check for undefined functions or macros (e.g., calls to non-existent APIs), references to external libraries not in the project, and duplicate definitions. The final harness from Stage 4 must additionally pass compilation and a 30-second test (executing with empty input to ensure no immediate crash). This layered validation catches errors early, prevents them from propagating to later stages, and is therefore essential for the rollback algorithm to function correctly. Why Rollback, Not LLM-Based Repair? A deliberate design choice is to use staged rollback instead of prompting the LLM to “fix” erroneous code. Our experiments show that LLM-based repair is often non-deterministic and can introduce new, unrelated errors while attempting to fix the original one. The rollback mechanism provides a more reliable and efficient recovery path by reverting to a known-good earlier state and regenerating, leveraging the deterministic nature of our staged prompts.

4

Evaluation

In this section, we conducted comprehensive experiments to address the following research questions: • RQ1 (Coverage): How effective are SynapseFlow-generated harnesses in coverage? • RQ2 (Bug Detection Ability): How effective are SynapseFlow-generated harnesses in triggering known bugs? • RQ3 (Real-world Bug Discovery): Can SynapseFlow discover previously unreported bugs? • RQ4 (Efficiency): How efficient is SynapseFlow compared to the competitors? • RQ5 (LLM Sensitivity): How sensitive is SynapseFlow to the choice of LLM? • RQ6 (SFG Quality & Ablation Study): What is the quality of SFG and how do SFG and staged decomposition mechanism individually contribute to overall effectiveness? Environment. All experiments ran on an Ubuntu 24.04 node (Xeon Platinum 8160, 96 GB RAM, 4× NVIDIA V100 GPUs / 64 GB). Baselines. We selected OSS-Fuzz-Gen (git commit hash: 26b3259), CKGFuzzer (git commit hash: bb50d2f) and PromeFuzz (git commit 92df4c2) as baseline comparators. OSS-Fuzz-Gen, an LLM-based harness auto generation tool developed by Google, has been integrated

Xing Zhang, Zikang Huang, Gang Yang, Lingyun Ying, and et al.

into the OSS-Fuzz platform and is widely adopted in open-source projects. CKGFuzzer and PromeFuzz represents the current SOTA in academic research. A functional comparison of these tools is summarized in Table 3. To eliminate LLM choice as a confounding variable, all tools used the same backend model (Qwen3-32B) for the main experiments. For CKGFuzzer, feeding its entire call graph into the prompt caused token counts to exceed 2M, making generation infeasible (a limitation also noted in prior work [12]); we therefore truncated the call graph input to ensure a fair comparison. We exclude traditional non-LLM baselines because the transitive hierarchy (PromeFuzz > Hopper/libErator) is already known [12]. Table 3: A brief functional comparison with baselines. Approach OSS-Fuzz-Gen CKGFuzzer PromeFuzz SynapseFlow

Scope All Funcs API only API only All Funcs

Target Auto. Manual Auto. Auto.

#Funcs Single Multiple Multiple Multiple

Feedback None None None Iterative

Dataset. We select 25 open-source C projects from GitHub (17 libraries, 8 applications; details in Appendix C, Table 10). Our dataset design follows three principles to ensure rigorous and unbiased evaluation: • Benchmarking: We include 6 established targets (c-ares, cjson, zlib, libtiff, lcms, sqlite3) commonly used in prior work [8, 9, 12, 18, 19], ensuring direct comparability. • Stress Testing: We incorporate projects with complex protocol or file-parsing logic to push the boundaries of current generation methods. • Mitigating Data Contamination: We introduce 11 libraries never studied in related work. Since LLMs may have seen harnesses for popular libraries, evaluating on these novel targets better isolates the intrinsic performance of the generation techniques themselves. Additionally, we include 8 applications to test tools beyond their original library-focused scope (CKGFuzzer and PromeFuzz were designed solely for libraries). Experimental Setup. All generated harnesses were compiled with LLVM and executed with libFuzzer; branch coverage was measured using llvm-cov show (Appendix D). All fuzzing campaigns started from empty seeds (no initial corpus). Each harness ran for 24 hours over 5 trials (averaged and then rounded to the nearest integer) on a dedicated CPU core with no additional scheduling—each harness was tested independently immediately after successful generation. Generation settings varied by research question: for RQ1 (coverage), RQ5 (LLM sensitivity), and RQ6 (SFG quality & ablation study), we generated one harness per function triplet (FT); for RQ2 (bug detection), we generated ten distinct harnesses per target bug function (Section 4.2); for RQ3 (real-world bug discovery), we generated ten harnesses per FT; and for RQ4 (efficiency), we measured generation time and token consumption only, without performing fuzzing. Our experimental design follows the prudent evaluation practices recommended for fuzzing research [20]: standardized metrics, uniform fuzzing budgets (24 hours), cross-tool comparison on a unified

SYNAPSEFlow

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

Table 4: Evaluation results of branch coverage (RQ1) and bug detection ability (RQ2). #Br/#Func: Total number of branches/functions, OF: OSS-Fuzz-Gen, CK: CKGFuzzer, PF: PromeFuzz, SF: SynapseFlow, #Harness: Number of harnesses that trigger bugs. #Covered Br/Func Project c-ares fribidi libyaml cjson sqlite3 zlib opus libxml2 lz4 libssh2 expat avahi libxslt libtiff lcms libarchive xz jq pjsip kamailio postfix gdbm file hpn-ssh dropbear

Type Lib Lib Lib Lib Lib Lib Lib Lib Lib Lib Lib Lib Lib Lib Lib Lib Lib App App App App App App App App

8714 / 534 1748 / 60 6402 / 58 1546 / 92 40236 / 2432 2670 / 209 11470 / 532 59620 / 973 1964 / 188 6184 / 347 6650 / 244 5506 / 815 42964 / 155 14328 / 319 8956 / 487 21204 / 637 2672 / 323 18942 / 342 41674 / 2980 143066 / 2494 5778 / 1526 2204 / 156 7886 / 246 8832 / 1661 8012 / 432

CK

PF

SF

Bug ID

OF

CK

PF

SF

3914 / 69 727 / 30 2671 / 23 699 / 25 16879 / 39 543 / 75 1422 / 8 5831 / 160 489 / 59 88 / 79 2598 / 26 654 / 42 5741 / 145 4698 / 102 2578 / 165 576 / 52 968 / 42 596 / 13 98 / 9 887 / 58 631 / 53 205 / 20 765 / 111 55 / 20 34 / 22

3547 / 89 465 / 15 3099 / 38 670 / 27 19874 / 74 1436 / 69 1789 / 12 4876 / 65 552 / 48 96 / 22 3348 / 52 668 / 44 5411 / 86 4896 / 107 2887 / 138 4687 / 98 876 / 48 N/A N/A N/A N/A N/A N/A N/A N/A

3848 / 112 774 / 16 3078 / 57 792 / 78 22580 / 246 1739 / 91 4556 / 53 1965 / 88 874 / 60 66 / 26 3720 / 76 288 / 99 5275 / 198 5290 / 193 3424 / 358 6446 / 382 1356 / 78 235 / 12 145 / 104 112 / 143 52 / 69 96 / 39 1196 / 114 44 / 164 56 / 62

4299 / 66 1039 / 16 4020 / 49 1117 / 78 28063 / 528 1810 / 68 7296 / 143 6316 / 76 1167 / 45 172 / 25 4246 / 48 993 / 41 9190 / 88 5906 / 165 3256 / 277 8313 / 146 1955 / 120 2909 / 28 482 / 198 2024 / 230 1286 / 540 668 / 114 2653 / 166 415 / 495 118 / 27

CVE-2020-22217 CVE-2022-25310 issue:42486502 CVE-2024-31755 CVE-2020-13434 CVE-2013-0899 CVE-2024-47607 CVE-2025-27113 CVE-2023-35955 CVE-2020-22218 CVE-2022-40674 CVE-2023-38473 CVE-2024-55549 CVE-2023-6277 CVE-2025-29069 CVE-2025-5914 CVE-2025-31115 CVE-2025-48060 CVE-2023-27585 CVE-2025-12206 issue:42488602 issue:42533836 issue:391975635 issue:371061096 issue:391975635

5 4 7 7 5 2 5 4 3 3 2 7 1 4 6 6 4 4 6 6 3 7 8 5 8

4 5 8 8 7 3 7 5 6 5 1 6 1 3 7 7 2 5 5 1 2 6 6 4 7

7 8 7 7 8 5 7 6 7 6 4 8 3 5 8 7 6 8 6 5 5 6 7 4 8

9 8 8 10 9 9 8 9 10 10 8 8 8 7 8 10 8 9 7 7 8 7 8 7 9

Aver. Rate(%)

46.9

55.0

61.3

83.3

backend, and full reporting of both coverage and bug discovery results.

4.1

#Harnesses

OF

#Br/#Func

RQ1: Coverage

4.1.1 Experimental Setup. We tested 25 source projects by generating harnesses with each tool and running every harness for 24 hours from empty seeds. For each project, we aggregated the coverage data from all harnesses and computed the overall coverage. For branch coverage, we collected dynamic execution traces using llvm-cov. All tools utilize libFuzzer as the backend in their implementations. The specific operations for collecting branch coverage are detailed in Appendix D. This setup differs from PromeFuzz’s approach, which relies on the AFL-LTO backend. Notably, while PromeFuzz claims to utilize llvm-cov in their paper [12], our inspection of their source code reveals that they actually employ llvm-cov in gcov compatibility mode to collect branch coverage1 . For function coverage, we extracted all callable functions exposed in the header files as the total function set. For each generated harness, we identified the functions directly invoked within it as the covered set. 1 https://github.com/pvz122/PromeFuzz/blob/master/database/utils/gcov.py:219

4.1.2 Metrics. We quantified the relative improvement of SynapseFlow over each baseline as the mean ratio of branch coverage and function coverage across all projects. For each project 𝑖 and baseline 𝐵, we computed 𝑅𝑖,𝐵 = BrCov(SynapseFlow𝑖 )/BrCov(𝐵𝑖 ) and 𝑅𝑖,𝐹 = FuncCov(SynapseFlow𝑖 )/FuncCov(𝐵𝑖 ), then took the average of 𝑅𝑖,𝐵 and 𝑅𝑖,𝐹 over all projects 𝑖. 4.1.3 Results. The coverage evaluation results are shown in Table 4 (the #Covered Br/Func column). SynapseFlow outperforms OSSFuzz-Gen, CKGFuzzer, and PromeFuzz in 24 out of 25 projects in branch coverage. Overall, SynapseFlow achieves 3.07×, 1.71×, and 4.26× higher branch coverage than OSS-Fuzz-Gen, CKGFuzzer, and PromeFuzz, respectively. For function coverage, SynapseFlow achieves 4.97×, 2.33×, and 1.52× higher coverage than OSS-FuzzGen, CKGFuzzer, and PromeFuzz, respectively. Specifically, for libraries, SynapseFlow improves branch coverage by factors of 2.64×, 1.71×, and 1.60× over OSS-Fuzz-Gen, CKGFuzzer, and PromeFuzz. For applications, where CKGFuzzer is inapplicable, SynapseFlow achieves 3.98× and 9.9× higher coverage compared to OSS-Fuzz-Gen and PromeFuzz. Notably, although baselines occasionally cover a larger number of functions in some individual projects, their branch coverage remains substantially lower. Manual inspection of baseline-generated

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

harnesses reveals that many perform superficial invocations without properly routing external fuzz inputs (data parameter) into the target functions. Consequently, they achieve high function counts but fail to exercise internal control-flow paths. In contrast, SynapseFlow’s SFG-driven generation explicitly models data dependencies, ensuring that harnesses correctly channel external inputs into target functions. This data-aware construction enables deeper logical exploration and explains why SynapseFlow consistently triggers more branches despite occasionally covering fewer functions. This indicates that SynapseFlow-generated harnesses exhibit superior path discovery capability. Our manual analysis reveals three key factors contributing to this performance gap: Comprehensive Function Selection. The superior coverage of SynapseFlow stems from identifying a more complete set of relevant functions through deep dataflow aggregation. While PromeFuzz relies on heuristics based on direct code consumption (lacking transitivity), SynapseFlow constructs an SFG to model global function relationships, capturing transitive dependencies and longrange data flows that baselines systematically miss. CKGFuzzer’s API-only focus and OSS-Fuzz-Gen’s single-function approach further restrict their achievable coverage. Staged Generation Process. The quality and reliability of our generated harnesses are ensured by the staged generation pipeline. This contrasts sharply with the monolithic generation paradigm of the baselines. By breaking the complex task into incremental stages with validation at each step, we transform a high-risk process into a series of simple, verifiable steps. The baselines’ monolithic approach is fragile; a single incorrect API usage can invalidate the entire output, yielding low-quality code. Input Function Isolation. SynapseFlow maintains a one-to-one correspondence between ISFs and harnesses, whereas CKGFuzzer and PromeFuzz often combine multiple ISFs into a single harness. When these functions process divergent input formats, the fuzzer’s genetic algorithm may skew coverage and reduce effectiveness—as observed in c-ares where merging ares_parse_soa_reply and ares_parse_aaaa_reply diminished coverage feedback specificity. The primary reason for SynapseFlow’s slightly lower coverage on lcms compared to PromeFuzz stems from our use of a treesitter-based parser. lcms uses complex macro definitions that can confuse syntax-based analysis, causing some functions to be missed during extraction. Five protocol implementations (hpn-ssh, pjsip, libssh2, dropbear, proftpd) presented challenges for all tools due to strict input validation and cryptographic requirements. This delineates the effective scope of our approach: SynapseFlow excels at testing data-processing libraries with clear structural dataflow, while stateful protocols with complex validation logic represent a known boundary for fuzz driver-based testing in general. Answer to RQ1: SynapseFlow achieves superior branch coverage by systematically generating high-quality harnesses for a broader range of functions. This stems from two key innovations: a principled function classification and grouping mechanism based on deep dataflow analysis, and a robust staged decomposition and rollback process that ensures code reliability.

Xing Zhang, Zikang Huang, Gang Yang, Lingyun Ying, and et al.

4.2

RQ2: Bug Detection Ability

4.2.1 Experimental Setup. We selected 25 known bugs comprising 19 representative CVEs and 6 documented OSS-Fuzz issues [21] as our evaluation targets. For each tool, we specified the target bug functions, generated corresponding harnesses, then executed them to verify bugtriggering capability: for SynapseFlow, we identified the smallest FTs containing the target bug functions to minimize interference; for OSS-Fuzz-Gen, we directly provided the target bug functions; for CKGFuzzer, we selected from its output API list those interfaces that either contained or might invoke the bug functions; and for PromeFuzz, we identified function sets containing the target bug functions. For each bug function, we generated 10 compilable harnesses per tool and executed them for 24 hours. Manual crash analysis then confirmed successful bug triggering. 4.2.2 Metrics. We calculated the average trigger rate (Aver. Rate) as the fraction of harnesses successfully triggering a bug over the total allocated quota (10 × #targets) for comparison with baselines. 4.2.3 Results. Table 4 (the #Harness column) demonstrates that SynapseFlow’s harnesses achieved significantly higher average bug detection rates. Quantitatively, SynapseFlow achieves 1.77×, 1.51×, and 1.36× higher average trigger rates than OSS-Fuzz-Gen, CKGFuzzer, and PromeFuzz, respectively. Our manual analysis reveals the following factors: Robust Parameter Initialization. These baseline tools often produce harnesses with incorrect parameter initialization, hindering crash reproduction. SynapseFlow addresses this through its staged approach, where Stage 1 handles independent parameter initialization and subsequent stages refine it (detailed in Section 3.3.1). Comprehensive Verification. We observed that baseline harnesses often omitted critical function calls (LLM forgetting) or redundantly defined target functions (masking the real function). SynapseFlow enforces tree-sitter-based syntactic and semantic validation at each generation stage, ensuring functional completeness. Optimal Function Aggregation. PromeFuzz and CKGFuzzer produce harnesses containing multiple ISFs, which reduces bug detection efficiency. SynapseFlow’s one-ISF-per-harness strategy avoids this pitfall. Answer to RQ2: SynapseFlow’s staged generation with multistage validation ensures proper function invocation, leading to significantly higher bug detection rates than baselines.

4.3

RQ3: Real-world Bug Discovery

4.3.1 Experimental Setup. We generated 10 validated harnesses per native target unit for each tool (FTs for SynapseFlow, function sets for PromeFuzz, API combinations for CKGFuzzer, single functions for OSS-Fuzz-Gen), and fuzzed each for 24 hours with an empty seed corpus. Our crash analysis pipeline employed: 1) automated filtering to remove crashes in harness code, 2) call stack-based deduplication using libFuzzer traces to identify unique crash sites and 3) manual inspection to discard false positives (including API misuse cases), with true bugs confirmed through expert review. The manual verification revealed the following crash discovery results and previously unreported bugs.

SYNAPSEFlow

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

4.3.2 Results. Table 5 summarizes the crash discovery results. After crash call-stack deduplication, SynapseFlow obtained 40 unique crashes. Manual analysis classified 9 as false positives stemming from API misuse, and 31 as genuine bugs, resulting in a 77.5% precision. For comparison, PromeFuzz had a precision of 42.9%, and CKGFuzzer and OSS-Fuzz-Gen achieved precisions of 5.6% and 2.1%, confirming SynapseFlow’s superior reliability. This low false-positive rate is primarily due to our dataflow-aware function grouping, which prevents irrelevant API combinations, and the staged rollback validation, which filters out harnesses that misuse APIs before fuzzing.

explaining why they evaded detection despite approximately five years of continuous fuzzing [21]. SynapseFlow effectively generated valid harnesses for these overlooked APIs, uncovering bugs missed by existing infrastructure.

Table 5: Crash discovery results from OSS-Fuzz-Gen, CKGFuzzer, PromeFuzz and SynapseFlow.

4.3.4 Case Study. Listing 5 shows a heap overflow bug in expat. In this case, Function1 calls directly calls VulnFunc without checking the size of mem, VulnFunc writes sizeof(struct normal_encoding) size to mem buffer without checking size of mem either. When the size of mem is less than sizeof(struct normal_encoding) (line 10), heap overflow occurs.

Type

OF

CK

PF

SF

FP

API Contract Violations Resource Lifecycle Mismatches Unsafe Memory Operations

28 12 6

15 14 5

17 8 3

2 4 3

TP

Bugs in Standard Functions Bugs in Deprecated Functions Bugs in Unsafe Functions Precision Rate(%)

0 0 1 2.1

0 0 2 5.6

0 6 15 42.9

7 7 17 77.5

4.3.3 Impact. All discovered bugs constitute high-severity risks. Maintainers responded promptly to our reports: patches for libarchive and file were merged shortly after disclosure, and 5 bugs have been assigned CVE IDs. These rapid remediations and official acknowledgments validate both the criticality of our findings and SynapseFlow’s practical utility [22] in enabling responsible disclosure and timely patching of real-world vulnerabilities.

1

2 3 4

5 6

Table 6: Bugs found by SynapseFlow-generated harness.

7 8 9

Project

Bug Type

Bug ID

Status

expat lz4 file libarchive hpn-ssh hpn-ssh sqlite3

Heap Overflow Heap Overflow Stack Overflow Infinite Loop Stack Overflow Use After Free Heap Overflow

Commit id:acfbd73 CVE-2025-61467 CVE-2025-51519 CVE-2025-51521 CVE-2025-51644

Reported Reported Fixed Fixed Confirmed Confirmed Confirmed

For SynapseFlow, among the 9 false positives, 2 violated documented parameter constraints (e.g., passing non-null-terminated byte streams to functions expecting C-strings, triggering crashes in strlen). 4 resulted from incorrect resource management (e.g., double-free vulnerabilities caused by harnesses explicitly freeing resources already managed internally by the target API). The remaining 3 were harness-induced memory safety errors that manifested as crashes within the target code. SynapseFlow identified 31 genuine bugs. Among them, 7 reside in deprecated (but still callable) functions, and 17 in functions explicitly marked as unsafe—i.e., requiring callers to guarantee parameter validity and discouraged for general use. We have reported all bugs in these two categories to the respective maintainers. The remaining 7 bugs were submitted as standard bug reports; 5 have been confirmed, with 2 already patched, while none of the competing tools discovered any new bugs. These confirmed bugs are summarized in Table 6. Our analysis reveals that the vulnerable functions predominantly reside outside OSS-Fuzz’s target set,

10 11

Function1 ( void * mem , int * table , CONVERTER convert , void * userData ) { ENCODING * enc = VulnFunc ( mem , table , convert , userData ); if ( enc ) (( struct normal_encoding *) enc ) -> type [ ASCII_COLON ] = BT_COLON ; return enc ;} VulnFunc ( void * mem , int * table , CONVERTER convert , void * userData ) { int i; struct unknown_encoding *e = ( struct unknown_encoding *) mem ; // crash here memcpy ( mem ,& latin1_encoding , sizeof ( struct normal_encoding )); ...}

Listing 5: A heap overflow bug in expat. Listing 6 shows a heap overflow bug in lz4. In this case, the bug occurs during the handling of the final uncompressed literals (last_literals) in the VulnFunc (line 9 and 12). A heap overflow is triggered when LZ4_memcpy is executed due to incorrect calculations of the remaining space in the destination buffer. Because the VulnFunc’s parameter must be initialized correctly by LZ4_resetStream function and only SynapseFlow can generate the harness that contains such call chain. Our case study demonstrates the effectiveness of SynapseFlow in detecting real-world bugs. 1

2 3 4 5 6 7

8 9 10 11 12

VulnFunc ( LZ4_stream_t_internal * const cctx , const char * const source ,...) { const BYTE * anchor = ( const BYTE *) source ; ... _last_literals : size_t lastRun = ( size_t )( iend - anchor ); if ( ( outputDirective ) && ( op + lastRun + 1 + (( lastRun +255 - RUN_MASK ) /255) > olimit )) { if ( outputDirective == fillOutput ) { lastRun = ( size_t )( olimit - op ) - 1; // maybe negetive lastRun -= ( lastRun + 256 - RUN_MASK ) / 256;}} ... LZ4_memcpy (op , anchor , lastRun ); // crash here

Listing 6: A heap overflow bug in lz4.

CCS ’26, November 15–19, 2026, The Hague, The Netherlands

Answer to RQ3: The results demonstrate that SynapseFlow’s SFG-based function selection and staged rollback generation collectively enabled the discovery of 7 previously unreported bugs across 25 projects, conclusively validating its efficacy.

4.4

Xing Zhang, Zikang Huang, Gang Yang, Lingyun Ying, and et al.

Table 7: Average efficiency analysis results across all 25 target projects. PreT: mean pre-processing time (hours); GenT: mean generation time per harness (hours); Tokens: mean LLM token consumption (millions); #S: mean number of generation stages per harness.

RQ4: Efficiency PreT(h)

GenT(h)

Tokens(M)

#S

Oss-Fuzz-Gen CKGFuzzer PromeFuzz SynapseFlow

4.33 6.64 3.84

1.64 0.94 1.39 0.33

35.47 17.54 31.26 23.55

7.78

Record · ID 349697 · SHA-256 05e663b96479f4ad
Retrieved via Conceptio — every document is proof-bundled with source, license, and retrieval metadata.