A coverage-guided fuzzer for Python and Python extensions based on libFuzzer
91
Essential functions for setting up and running the Atheris fuzzer. These functions provide the fundamental fuzzing capabilities including configuration, execution control, and input mutation.
Configure the fuzzer with your test function and options before starting the fuzzing process.
def Setup(args, test_one_input, **kwargs):
"""
Configure the fuzzer with test function and options.
Args:
args (list): Command-line arguments, typically sys.argv. This list may be
modified in-place to remove arguments consumed by the fuzzer.
test_one_input (callable): Your fuzzer's entry point function. Must accept
a single bytes argument and perform the testing.
**kwargs: Optional keyword arguments:
internal_libfuzzer (bool, optional): Controls libfuzzer mode:
- None: Auto-detect (default)
- True: Use internal libfuzzer
- False: Use external libfuzzer
custom_mutator (callable, optional): Custom mutation function with signature:
(data: bytes, max_size: int, seed: int) -> bytes
custom_crossover (callable, optional): Custom crossover function with signature:
(data1: bytes, data2: bytes, max_out_size: int, seed: int) -> bytes
Returns:
list: Remaining command-line arguments after fuzzer argument consumption
"""Usage Example:
import atheris
import sys
def TestOneInput(data):
# Your fuzzing logic here
some_function_to_test(data)
# Basic setup
atheris.Setup(sys.argv, TestOneInput)
# Setup with custom mutator
def custom_mutator(data, max_size, seed):
# Custom mutation logic
return mutated_data
atheris.Setup(sys.argv, TestOneInput, custom_mutator=custom_mutator)Start and control the fuzzing process.
def Fuzz():
"""
Start the fuzzing loop. Must call Setup() first.
This function takes control of the program and does not return under normal
circumstances. The fuzzer will run until one of these conditions:
- A crash is found (uncaught exception in test_one_input)
- Run limits are reached (via command-line flags)
- External termination (SIGINT, SIGTERM, etc.)
- libFuzzer exits due to other conditions
Raises:
RuntimeError: If Setup() was not called first
"""Usage Example:
# Must call Setup() before Fuzz()
atheris.Setup(sys.argv, TestOneInput)
# Start fuzzing - this call does not return
atheris.Fuzz()
# Code after this point will not be reached
print("This will never be printed")Access libFuzzer's built-in mutation capabilities for custom mutators.
def Mutate(data, max_size):
"""
Mutate input data using libFuzzer's built-in mutator.
This function provides access to the same mutation algorithms that libFuzzer
uses internally. Useful for implementing custom mutators that combine
domain-specific mutations with libFuzzer's general-purpose mutations.
Args:
data (bytes): Input data to mutate. Can be empty bytes.
max_size (int): Maximum size of the mutated output in bytes.
The actual output may be smaller than this limit.
Returns:
bytes: Mutated data. The length will be <= max_size.
"""Usage Example:
def CustomMutator(data, max_size, seed):
# Use libFuzzer's built-in mutation as a base
mutated = atheris.Mutate(data, max_size)
# Apply domain-specific mutations
if len(mutated) > 4:
# Example: occasionally corrupt a magic header
if seed % 10 == 0:
mutated = b"CORRUPT" + mutated[4:]
return mutated
atheris.Setup(sys.argv, TestOneInput, custom_mutator=CustomMutator)
atheris.Fuzz()Atheris processes command-line arguments to configure fuzzing behavior. Common options include:
-atheris_runs=N: Limit fuzzing to N executions-max_len=N: Maximum input length-timeout=N: Timeout in seconds for each execution-dict=file: Use dictionary file for mutations-jobs=N: Number of parallel fuzzing jobs-workers=N: Number of simultaneous worker processesUsage Example:
import sys
import atheris
def TestOneInput(data):
target_function(data)
# sys.argv might contain: ['fuzzer.py', '-atheris_runs=1000', '-max_len=100']
remaining_args = atheris.Setup(sys.argv, TestOneInput)
print(f"Remaining arguments: {remaining_args}")
atheris.Fuzz()Atheris reports failures when the test function throws uncaught exceptions:
def TestOneInput(data):
try:
risky_function(data)
except ExpectedException:
# Expected exceptions should be caught - they don't indicate bugs
pass
# Uncaught exceptions will be reported as crashes by AtherisFor debugging, you can limit runs and examine specific inputs:
# Run fuzzer for limited iterations to debug
# Command: python fuzzer.py -atheris_runs=10
def TestOneInput(data):
print(f"Testing with {len(data)} bytes: {data[:10]}...")
target_function(data)Install with Tessl CLI
npx tessl i tessl/pypi-atherisevals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10