Safeguard
Developer Tools•

Fuzzing Python code and C extensions with Atheris

Atheris brings libFuzzer to Python. Set up a harness, instrument imports, and pair it with sanitizers when you test native extension modules.

Safeguard Security Team
3 min read

Atheris is a coverage-guided fuzzer for Python, built on libFuzzer. You write a function that takes bytes, call atheris.Setup and atheris.Fuzz, and wrap the imports you want instrumented. Any uncaught exception is reported as a failure. For native extensions, pair it with a sanitizer.

What Python fuzzing finds

Pure Python rarely corrupts memory, so the findings are different: uncaught exceptions on odd input, infinite loops and runaway memory in parsers, and logic bugs if you add assertions. Code that wraps C, such as image libraries or parsers with native speedups, can still hit real memory errors, and that is where sanitizers come in.

A minimal harness

Based on the Atheris README:

import sys
import atheris

with atheris.instrument_imports():
    import mylib

def test_one_input(data: bytes):
    mylib.parse(data)

atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()

Setup takes the command line arguments and your entry function, which receives bytes. Fuzz starts the run and never returns. Splitting the two lets the fuzzer consume its own flags first.

Instrument what you want measured

Coverage in Python comes from bytecode instrumentation. The README offers three ways: the instrument_imports context manager, an instrument_func decorator, and an experimental instrument_all for modules already imported. Imports inside the with block, and their dependencies, are instrumented. A module imported before the block is not, so import order matters.

If coverage never grows, the first thing to check is whether the code under test was actually instrumented.

Turning bytes into typed values

Real functions often need a string and an integer, not raw bytes. Atheris offers a FuzzedDataProvider that carves typed values out of the input. This keeps the harness short and the mutation effective.

Native extensions

For C extension modules the README says the extension must be instrumented for fuzzing to be effective, and Atheris can be paired with AddressSanitizer or UndefinedBehaviorSanitizer to catch more bugs. Building from source may be needed so the libFuzzer version matches your Clang. Expect this to be the fiddly part, so start with pure Python targets to get comfortable.

Version support

The README says the current source supports Python 3.11 to 3.14 on Linux and macOS, with older versions only on PyPI. Check your interpreter version before debugging an install problem.

Making a failure useful

When the fuzzer finds an input that raises, it saves it. Reproduce by running the harness with that file as the argument, fix the code, and keep the file as a test case in your suite. Decide up front which exceptions are expected. A parser that raises ValueError on bad input is behaving correctly, so catch that type in the harness and let everything else count as a bug.

Where it fits

Run short fuzz sessions in CI over the saved inputs and longer ones on a schedule. Atheris has OSS-Fuzz integration, and OSS-Fuzz lists python as a supported language.

Quick check

Why does my Atheris run show no coverage growth?

Most often the target modules were not instrumented, for example because they were imported before the instrumentation block. Move the import inside it.

Does an exception from bad input count as a bug?

Only if you do not expect it. Catch the exception types your API documents, and let anything else fail the run.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.