Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/builds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13", "3.14"]
needs: [ruff]
steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Atheris is a coverage-guided Python fuzzing engine. It supports fuzzing of Pytho

## Installation Instructions

Atheris supports Linux (32- and 64-bit) and Mac OS X, Python versions 3.11-3.13.
Atheris supports Linux (32- and 64-bit) and Mac OS X, Python versions 3.11-3.14.
Versions 3.10 and below are not supported in the current source version, but
remain accessible in PyPI.

Expand Down
4 changes: 3 additions & 1 deletion deployment/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ RUN set -e -x -v; \
RUN set -e -x -v; \
/opt/python/cp311-cp311/bin/python3 -m pip install setuptools && \
/opt/python/cp312-cp312/bin/python3 -m pip install setuptools && \
/opt/python/cp313-cp313/bin/python3 -m pip install setuptools;
/opt/python/cp313-cp313/bin/python3 -m pip install setuptools && \
/opt/python/cp314-cp314/bin/python3 -m pip install setuptools;

WORKDIR /atheris

CMD export LIBFUZZER_LIB="/root/llvm-project/build/lib/clang/$(ls /root/llvm-project/build/lib/clang/)/lib/x86_64-unknown-linux-gnu/libclang_rt.fuzzer_no_main.a"; \
/opt/python/cp311-cp311/bin/python3 setup.py bdist_wheel -d /tmp/dist && \
/opt/python/cp312-cp312/bin/python3 setup.py bdist_wheel -d /tmp/dist && \
/opt/python/cp313-cp313/bin/python3 setup.py bdist_wheel -d /tmp/dist && \
/opt/python/cp314-cp314/bin/python3 setup.py bdist_wheel -d /tmp/dist && \
( cd /tmp/dist && find /tmp/dist/* | xargs -I{} auditwheel repair --plat manylinux2014_x86_64 {} ) && \
mkdir -p /atheris/dist && cp /tmp/dist/wheelhouse/* /atheris/dist/
3 changes: 2 additions & 1 deletion src/instrument_bytecode.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from .version_dependent import CALLABLE_STACK_ENTRIES
from .version_dependent import CMP_OP_SHIFT_AMOUNT
from .version_dependent import CONDITIONAL_JUMPS
from .version_dependent import CONST_LOADS
from .version_dependent import ExceptionTable
from .version_dependent import ExceptionTableEntry
from .version_dependent import generate_exceptiontable
Expand Down Expand Up @@ -810,7 +811,7 @@ def trace_data_flow(self) -> None:
seen_consts = []

for c, instr in enumerate(self.instructions):
if instr.mnemonic == "LOAD_CONST":
if instr.mnemonic in CONST_LOADS:
seen_consts.append(stack_size)
elif instr.mnemonic == "COMPARE_OP" and (
instr.arg >> CMP_OP_SHIFT_AMOUNT
Expand Down
13 changes: 7 additions & 6 deletions src/instrument_bytecode_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,16 +617,17 @@ def near_extended_arg(x, y):
pass
pass
pass
pass
return x
return y

original_code = near_extended_arg.__code__

# Ensure that the original code does not contain any EXTENDED_ARG
# instructions.
for inst in original_code.co_code:
self.assertNotEqual(dis.opname[inst], "EXTENDED_ARG")
# instructions. `dis.get_instructions` decodes properly so argument
# bytes that happen to equal the EXTENDED_ARG opcode value are not
# mistaken for opcodes.
for instr in dis.get_instructions(original_code):
self.assertNotEqual(instr.opname, "EXTENDED_ARG")

patched_code = instrument_bytecode.patch_code(
original_code, trace_dataflow=True
Expand All @@ -635,8 +636,8 @@ def near_extended_arg(x, y):
mockutils.UpdateCounterArrays()

# Ensure that the patched code contains EXTENDED_ARG instructions.
for inst in patched_code.co_code:
if dis.opname[inst] == "EXTENDED_ARG":
for instr in dis.get_instructions(patched_code):
if instr.opname == "EXTENDED_ARG":
break
else:
self.fail("No EXTENDED_ARG instructions found in patched code.")
Expand Down
26 changes: 24 additions & 2 deletions src/version_dependent.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- 3.11
- 3.12
- 3.13
- 3.14
"""

import sys
Expand All @@ -34,10 +35,10 @@

PYTHON_VERSION = sys.version_info[:2]

if PYTHON_VERSION < (3, 6) or PYTHON_VERSION > (3, 13):
if PYTHON_VERSION < (3, 6) or PYTHON_VERSION > (3, 14):
raise RuntimeError(
"You are fuzzing on an unsupported python version: "
+ f"{PYTHON_VERSION[0]}.{PYTHON_VERSION[1]}. Only 3.6 - 3.12 are "
+ f"{PYTHON_VERSION[0]}.{PYTHON_VERSION[1]}. Only 3.6 - 3.14 are "
+ "supported by atheris 2.0. Use atheris 1.0 for older python versions."
)

Expand Down Expand Up @@ -118,6 +119,27 @@
"POP_JUMP_IF_NOT_NONE",
])

if PYTHON_VERSION >= (3, 14):
# 3.14 added non-popping conditional jumps `JUMP_IF_TRUE` / `JUMP_IF_FALSE`
# as relative-forward branches; the existing pop-then-jump pair stays.
CONDITIONAL_JUMPS.extend([
"JUMP_IF_FALSE",
"JUMP_IF_TRUE",
])
HAVE_REL_REFERENCE.extend([
"JUMP_IF_FALSE",
"JUMP_IF_TRUE",
])

# Instructions that push a constant onto the stack. The constant-compare
# instrumentation tracks which stack slots hold constants so it can pass the
# constant value to `_trace_cmp` as `obj1`. 3.14 introduced `LOAD_SMALL_INT`
# as a specialized constant-loader for small integers; before 3.14 all
# constants flowed through `LOAD_CONST` so this list was implicit.
CONST_LOADS = ["LOAD_CONST"]
if PYTHON_VERSION >= (3, 14):
CONST_LOADS.append("LOAD_SMALL_INT")

HAVE_ABS_REFERENCE = [
# common
"JUMP_ABSOLUTE",
Expand Down
Loading