| name | google-continuous-fuzzing |
| description | Apply Google's continuous fuzzing methodology using OSS-Fuzz and ClusterFuzz. Emphasizes coverage-guided fuzzing, automated bug triage, and integration into CI/CD. Use when building robust testing infrastructure or finding security vulnerabilities at scale. |
| tags | fuzzing, oss-fuzz, coverage-guided, sanitizers, security, testing, crash, vulnerability, automated |
Google Continuous Fuzzing
Overview
Google's continuous fuzzing infrastructure (OSS-Fuzz + ClusterFuzz) has found over 10,000 bugs in 1,000+ open source projects, including critical security vulnerabilities like Heartbleed-class bugs. This technique turns fuzzing from a one-time activity into a continuous quality gate.
References
Core Philosophy
"Fuzzing should be continuous, not a one-time event."
"Every bug found by fuzzing is a bug not found by attackers."
Fuzzing is most effective when it runs continuously against the latest code, with automatic bug reporting and regression tracking.
Key Concepts
Coverage-Guided Fuzzing
Traditional Fuzzing: Random input generation
Coverage-Guided Fuzzing: Inputs that increase code coverage are kept
Corpus → Mutate → Execute → Measure Coverage → Keep interesting inputs
↑ |
└──────────────────────────────────────────────────────┘
The Fuzzing Pipeline
- Build: Compile with sanitizers (ASan, MSan, UBSan)
- Fuzz: Run fuzzers continuously on cluster
- Triage: Automatically deduplicate and file bugs
- Reproduce: Generate minimal reproducer
- Verify: Confirm fix eliminates the bug
- Regress: Add reproducer to regression corpus
When Implementing
Always
- Use sanitizers (AddressSanitizer, MemorySanitizer, UndefinedBehaviorSanitizer)
- Build seed corpus from existing tests and real inputs
- Integrate fuzzing into CI/CD pipeline
- Track coverage metrics over time
- Minimize reproducers for easier debugging
- Keep regression tests for all found bugs
Never
- Fuzz only once and declare victory
- Ignore crashes in dependencies
- Skip sanitizers to "improve performance"
- Discard valuable corpus data
- Treat fuzzing as separate from testing
Prefer
- LibFuzzer/AFL++ over basic random testing
- Structure-aware fuzzing for complex formats
- Continuous fuzzing over periodic runs
- Automated triage over manual analysis
- Coverage metrics over time-based metrics
Implementation Patterns
Basic Fuzz Target (C/C++)
#include <stdint.h>
#include <stddef.h>
#include "parser.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
parse_input(data, size);
return 0;
}
Fuzz Target with Structure
#include <stdint.h>
#include <stddef.h>
#include <string.h>
struct Header {
uint32_t magic;
uint32_t version;
uint32_t length;
uint8_t flags;
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < sizeof(Header)) {
return 0;
}
Header header;
memcpy(&header, data, sizeof(Header));
if (header.magic != 0xDEADBEEF) {
return 0;
}
size_t payload_size = size - sizeof(Header);
if (header.length > payload_size) {
header.length = payload_size;
}
const uint8_t *payload = data + sizeof(Header);
process_packet(&header, payload, header.length);
return ;
}
Python Fuzzing with Atheris
import atheris
import sys
import json
def test_one_input(data):
"""Fuzz target: called with random bytes"""
fdp = atheris.FuzzedDataProvider(data)
json_str = fdp.ConsumeUnicodeNoSurrogates(
fdp.ConsumeIntInRange(0, 1024)
)
try:
json.loads(json_str)
except (json.JSONDecodeError, ValueError):
pass
except Exception as e:
raise
def main():
atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()
if __name__ == "__main__":
main()
Go Fuzzing (Native)
package parser
import (
"testing"
)
func FuzzParseInput(f *testing.F) {
f.Add([]byte("valid input"))
f.Add([]byte("{\"key\": \"value\"}"))
f.Add([]byte(""))
f.Fuzz(func(t *testing.T, data []byte) {
result, err := ParseInput(data)
if err != nil {
return
}
if result != nil && result.Length < 0 {
t.Errorf("negative length: %d", result.Length)
}
})
}
OSS-Fuzz Integration
# Dockerfile for OSS-Fuzz integration
FROM gcr.io/oss-fuzz-base/base-builder
RUN apt-get update && apt-get install -y \
make \
autoconf \
automake \
libtool
# Clone your project
RUN git clone --depth 1 https://github.com/your/project.git
WORKDIR project
COPY build.sh $SRC/
#!/bin/bash
./configure
make clean
make -j$(nproc) CC="$CC" CXX="$CXX" CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS"
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE \
fuzz_target.cc -o $OUT/fuzz_target \
-I. libproject.a
zip -j $OUT/fuzz_target_seed_corpus.zip seeds/*
cp project.dict $OUT/fuzz_target.dict
Corpus Management
import subprocess
import hashlib
import os
from pathlib import Path
class CorpusManager:
def __init__(self, corpus_dir: str):
self.corpus_dir = Path(corpus_dir)
self.corpus_dir.mkdir(exist_ok=True)
def add(self, data: bytes) -> str:
"""Add input to corpus with content-based filename"""
hash_name = hashlib.sha256(data).hexdigest()[:16]
path = self.corpus_dir / hash_name
if not path.exists():
path.write_bytes(data)
return str(path)
def minimize(self, fuzzer_binary: str) -> int:
"""Minimize corpus using fuzzer's merge feature"""
minimized_dir = self.corpus_dir.parent / "corpus_minimized"
minimized_dir.mkdir(exist_ok=True)
result = subprocess.run([
fuzzer_binary,
"-merge=1",
str(minimized_dir),
str(self.corpus_dir)
], capture_output=True)
((minimized_dir.iterdir()))
() -> :
result = subprocess.run([
fuzzer_binary,
,
(.corpus_dir)
], capture_output=, text=)
{: ((.corpus_dir.iterdir()))}
CI/CD Integration
name: Continuous Fuzzing
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * *'
jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build fuzzer
run: |
clang++ -g -O1 \
-fsanitize=address,fuzzer \
-fno-omit-frame-pointer \
fuzz_target.cc -o fuzzer
- name: Download corpus
uses: actions/cache@v3
with:
path: corpus
key: fuzz-corpus-${{ github.sha }}
restore-keys: fuzz-corpus-
- name: Run fuzzer
run: |
mkdir -p corpus
timeout 600 ./fuzzer corpus/ -max_total_time=600 || true
- name: Upload crash
Mental Model
Google's fuzzing approach asks:
- Is this running continuously? One-time fuzzing misses regression bugs
- Are sanitizers enabled? Crashes without sanitizers miss real bugs
- Is the corpus growing? Coverage should increase over time
- Are bugs being tracked? Automatic filing and deduplication
- Are fixes verified? Reproducers become regression tests
Signature Moves
- Coverage-guided mutation (LibFuzzer, AFL++)
- Sanitizer builds (ASan, MSan, UBSan, TSan)
- Automatic corpus management and minimization
- CI/CD integration for every commit
- Regression corpus from found bugs
- Structure-aware fuzzing for protocols