Skip to content
Draft
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
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
all : fuzzer-html fuzzer-email fuzzer-httpclient fuzzer-json fuzzer-difflib fuzzer-csv fuzzer-decode fuzzer-ast fuzzer-tarfile fuzzer-tarfile-hypothesis fuzzer-zipfile fuzzer-zipfile-hypothesis fuzzer-re fuzzer-configparser fuzzer-tomllib fuzzer-plistlib fuzzer-xml fuzzer-zoneinfo
all : fuzzer-html fuzzer-email fuzzer-httpclient fuzzer-json fuzzer-difflib fuzzer-csv fuzzer-decode fuzzer-ast fuzzer-tarfile fuzzer-tarfile-hypothesis fuzzer-zipfile fuzzer-zipfile-hypothesis fuzzer-re fuzzer-configparser fuzzer-tomllib fuzzer-plistlib fuzzer-xml fuzzer-zoneinfo fuzzer-json-encode

PYTHON_CONFIG_PATH=$(CPYTHON_INSTALL_PATH)/bin/python3-config
CXXFLAGS += $(shell $(PYTHON_CONFIG_PATH) --cflags)
LDFLAGS += -rdynamic $(shell $(PYTHON_CONFIG_PATH) --ldflags --embed)
LDFLAGS += -rdynamic $(shell $(PYTHON_CONFIG_PATH) --ldflags --embed) $(CPYTHON_MODLIBS) -Wl,--allow-multiple-definition

fuzzer-html:
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"html.py\"" -ldl $(LDFLAGS) -o fuzzer-html
Expand Down Expand Up @@ -40,3 +40,6 @@ fuzzer-xml:
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"xml.py\"" -ldl $(LDFLAGS) -o fuzzer-xml
fuzzer-zoneinfo:
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"zoneinfo.py\"" -ldl $(LDFLAGS) -o fuzzer-zoneinfo

fuzzer-json-encode:
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"json_encode.py\"" -ldl $(LDFLAGS) -o fuzzer-json-encode
1 change: 1 addition & 0 deletions fuzz_targets.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ email email.py
html html.py
httpclient httpclient.py
json json.py
json-encode json_encode.py
plistlib plist.py
re re.py
tarfile tarfile.py
Expand Down
86 changes: 86 additions & 0 deletions json_encode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from fuzzeddataprovider import FuzzedDataProvider
import json

# Container type constants for build_container
CONTAINER_INT_LIST = 0
CONTAINER_STRING = 1
CONTAINER_DICT = 2
CONTAINER_TUPLE = 3
CONTAINER_FLOAT = 4
CONTAINER_INT = 5

# Encode operation constants for FuzzerRunOne
ENCODE_DEFAULT = 0
ENCODE_ASCII = 1
ENCODE_NON_ASCII = 2
ENCODE_SORTED = 3
ENCODE_INDENTED = 4
ENCODE_CUSTOM = 5


def build_container(fdp):
ctype = fdp.ConsumeIntInRange(CONTAINER_INT_LIST, CONTAINER_INT)
if ctype == CONTAINER_INT_LIST:
n = fdp.ConsumeIntInRange(0, min(fdp.remaining_bytes(), 200))
return fdp.ConsumeIntList(n, 1)
elif ctype == CONTAINER_STRING:
n = (
fdp.ConsumeIntInRange(1, min(fdp.remaining_bytes(), 1000))
if fdp.remaining_bytes() > 0
else 0
)
return fdp.ConsumeBytes(n).decode("latin-1") if n > 0 else ""
elif ctype == CONTAINER_DICT:
n = fdp.ConsumeIntInRange(0, min(fdp.remaining_bytes(), 50))
d = {}
for _ in range(n):
if fdp.remaining_bytes() == 0:
break
kn = fdp.ConsumeIntInRange(1, min(fdp.remaining_bytes(), 20))
key = fdp.ConsumeBytes(kn).decode("latin-1")
val = fdp.ConsumeRandomValue()
d[key] = val
return d
elif ctype == CONTAINER_TUPLE:
n = fdp.ConsumeIntInRange(0, min(fdp.remaining_bytes(), 200))
return tuple(fdp.ConsumeIntList(n, 1))
elif ctype == CONTAINER_FLOAT:
return fdp.ConsumeFloat()
else:
return fdp.ConsumeInt(4)


# Fuzzes the _json C module's encoding paths (Modules/_json.c).
# Builds Python containers (int lists, string dicts, tuples, floats)
# from fuzzed data and encodes them with json.dumps() using varied
# options (ensure_ascii, sort_keys, indent) and custom JSONEncoder
# settings (separators, allow_nan, default handler).
def FuzzerRunOne(FuzzerInput):
if len(FuzzerInput) < 1 or len(FuzzerInput) > 0x100000:
return
fdp = FuzzedDataProvider(FuzzerInput)
target = fdp.ConsumeIntInRange(ENCODE_DEFAULT, ENCODE_CUSTOM)
try:
obj = build_container(fdp)
if target == ENCODE_DEFAULT:
json.dumps(obj)
elif target == ENCODE_ASCII:
json.dumps(obj, ensure_ascii=True)
elif target == ENCODE_NON_ASCII:
json.dumps(obj, ensure_ascii=False)
elif target == ENCODE_SORTED:
json.dumps(obj, sort_keys=True)
elif target == ENCODE_INDENTED:
indent = fdp.ConsumeIntInRange(0, 8)
json.dumps(obj, indent=indent)
else:
enc = json.JSONEncoder(
ensure_ascii=fdp.ConsumeBool(),
sort_keys=fdp.ConsumeBool(),
indent=fdp.ConsumeIntInRange(0, 4) if fdp.ConsumeBool() else None,
)
enc.encode(obj)
except (ValueError, TypeError, RecursionError, OverflowError):
pass
except Exception:
pass