Atheris
SkillCloud & infraUse when a user needs coverage-guided fuzzing for Python code or a Python native extension using Atheris. Not for remote, credential, publish, deploy, or irreversible changes.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Atheris skill
What this skill tells your AI
The instructions your AI receives, as published by outlinedriven/odin-claude-plugin in plugins/odin-fuzzing/skills/atheris/SKILL.md and read by ahel’s review.
Contract
| Field | Bound contract |
|---|---|
| Trigger | User needs coverage-guided fuzzing for Python code or a Python native extension using Atheris. |
| Authority | Reversible local: writes only the Atheris harness file, a corpus directory, and when required for dependency management pyproject.toml and uv.lock in the harness directory; rollback is deleting the harness file and corpus directory and restoring pyproject.toml and uv.lock to their pre-run state. No remote mutation. No source under test is mutated. |
| Side effect | Local writes: a fuzz.py (or named) harness, a corpus/ directory of seed and crash artifacts, and a transient fuzzing process. |
| Done | Atheris executes an instrumented target through a deterministic TestOneInput harness, reports coverage, and any saved crash artifact reproduces the same failure when replayed. |
Inputs
- Target: the Python function or module to fuzz, or the Python C extension to fuzz. Required.
- Target kind: pure Python, or native C extension. Required; it selects the instrumentation and build path.
- Expected exceptions: the exception types the target legitimately raises on bad input, so the harness catches them instead of crashing. Optional but recommended.
- Seed corpus: initial input files for
corpus/. Optional; Atheris can start empty. - Time/length budget:
-max_total_timeand-max_lenvalues. Optional; defaults are libFuzzer defaults. - Sanitizers: whether AddressSanitizer and/or UndefinedBehaviorSanitizer are enabled. Optional; ASan is the default for native extensions.
Procedure
-
Determine target kind. If the target is pure Python, follow the pure-Python path. If it is a C extension compiled from source, follow the native-extension path. Do not guess; ask the user when the kind is ambiguous. Done when: the target kind is determined as pure Python or native C extension.
-
Install Atheris. If the harness directory is not already a uv project (no
pyproject.tomlpresent), runuv init --bareonce. Thenuv add atheris. Verify withpython -c "import atheris; print(atheris.__version__)". Done when: Atheris is installed and importable. -
Write the harness (
fuzz.pyor a named file). The harness must be deterministic: norandom,time, or other nondeterministic input insideTestOneInput.- Decorate the entry point with
@atheris.instrument_func. - Define
TestOneInput(data: bytes). Return early on inputs too short to be meaningful. Call the target. Catch only the expected exception types andpass; let every other exception propagate so the fuzzer records it as a crash. - In
main(), callatheris.Setup(sys.argv, TestOneInput)thenatheris.Fuzz(). - For structured input, use
atheris.FuzzedDataProvider(data)to split onebytesinput into typed values (ConsumeUnicodeNoSurrogates,ConsumeBool,ConsumeIntInRange, etc.). Draw in a fixed order; once the buffer runs dry each remaining method returns a zero-value of its type. Do not slicedataby hand when the target takes several typed arguments, because every mutation shifts the byte offsets of everything after it. Done when: the harness is written with a deterministicTestOneInputandmain().
- Decorate the entry point with
-
Instrument pure-Python targets. Wrap imports of the code under test in
with atheris.instrument_imports():so coverage is collected. Do not import the target module afteratheris.Setup(). Useatheris.instrument_funcfor a single function,atheris.instrument_imports()for selected modules, oratheris.instrument_all()only when system-wide instrumentation is intended. Done when: the target is instrumented with the appropriate Atheris mechanism. -
Build native C extensions with instrumentation. Before installing the extension from source, export:
CC=clang CXX=clang++ CFLAGS="-fsanitize=address,fuzzer-no-link" CXXFLAGS="-fsanitize=address,fuzzer-no-link" LDSHARED="clang -shared" LDSHAREDXX="clang++ -shared"For uv-managed projects, set
no-binary = ["<pkg>"]under[tool.uv]inpyproject.tomland runuv sync --reinstall-package <pkg>so the package is built from source; a lateruv synccan otherwise silently swap in an uninstrumented wheel. Addundefinedto the sanitizer list (-fsanitize=address,undefined,fuzzer-no-link) when UBSan is requested. Done when: the native extension is built from source with sanitizer and fuzzer instrumentation flags. -
Configure the native-extension runtime. Set
LD_PRELOADto the Atheris sanitizer shared library:export LD_PRELOAD="$(python -c 'import atheris, os; print(os.path.join(os.path.dirname(atheris.__file__), "asan_with_fuzzer.so"))')"Set
ASAN_OPTIONS="allocator_may_return_null=1,detect_leaks=0"to suppress allocation-failure and leak noise. SetASAN_SYMBOLIZER_PATHto thellvm-symbolizerfor the installed clang when stack traces are needed. Done when:LD_PRELOADandASAN_OPTIONSare set for the native-extension runtime. -
Create the corpus.
mkdir corpusand add seed inputs as individual files. Runuv run python fuzz.py corpus/so libFuzzer loads and grows the corpus. Minimize a merged corpus withuv run python fuzz.py -merge=1 new_corpus/ old_corpus/. Done when: the corpus directory is created and seeded. -
Run the campaign.
uv run python fuzz.py corpus/with optional-max_total_time=<seconds>,-max_len=<bytes>, and-workers=N -jobs=Nfor parallel exploration. Read the output:NEW cov: Xmeans new coverage and corpus growth;ERROR: libFuzzermeans a crash was detected and a crash artifact was written. Done when: the campaign runs and produces coverage output or crash artifacts. -
Reproduce failures. A crash artifact (named
crash-*orleak-*) is written next to the harness. Replay it deterministically withuv run python fuzz.py <artifact>and confirm the same failure recurs. Done when: each saved artifact reproduces the same failure or is classified as nondeterministic.
Failure and recovery
- No coverage increase. Cause: poor seed corpus or target not instrumented. Recovery: add representative seeds; confirm
instrument_imports()wraps the target imports and@atheris.instrument_funcwraps the entry point. Do not declare success on a stall. - Import errors / modules imported before instrumentation. Recovery: move the target imports inside the
atheris.instrument_imports()context manager, beforeatheris.Setup(). - Segfault with no ASan output. Cause:
LD_PRELOADnot set for a native extension. Recovery: exportLD_PRELOADtoasan_with_fuzzer.soand rerun. - Build failures for a native extension. Cause: wrong compiler or missing flags. Recovery: verify
CC,CXX,CFLAGS,CXXFLAGS, and the clang version; configureno-binary = ["<pkg>"]under[tool.uv]and runuv sync --reinstall-package <pkg>. - Memory-allocation or leak noise. Recovery: set
ASAN_OPTIONS=allocator_may_return_null=1,detect_leaks=0. - Crash artifact does not reproduce. Cause: nondeterminism in the harness (randomness, time, unordered iteration over mutable state). Recovery: remove the nondeterminism so
TestOneInputis a pure function ofdata, then rerun. A non-reproducing crash is not a confirmed defect. - Partial-result rule. A campaign that finds no crash is a partial result (coverage gained, no defect proven), not proof of absence. Report coverage and corpus growth; do not claim the target is bug-free.
- Rollback. Delete the harness file and the
corpus/directory. Restorepyproject.tomlanduv.lockto their pre-run state, or if they were created by this skill, remove the added atheris entry andno-binaryconfiguration. The source under test is never modified by this skill.
Output
- A deterministic, instrumented Atheris harness file.
- A
corpus/directory of seed and discovered inputs, plus anycrash-*/leak-*artifacts. - A campaign report: coverage reached, corpus size, executions per second, and for each crash artifact a confirmed reproduction (same failure on replay) or a nondeterminism flag.
- Terminal classification per crash: confirmed defect (reproduces), nondeterministic (does not reproduce), or no crash found (partial result, not proof of absence).
Signals
- GitHub stars
- 35
- Last commit
- Sep 2026
Others that do the same job
Advanced
- Catalog kind
- skill
- Gateway key
atheris- Source
- github.com/outlinedriven/odin-claude-plugin