Skip to content
Open
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: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
all: fuzzer-binascii 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-binascii 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-marshal fuzzer-pickle

PYTHON_CONFIG_PATH=$(CPYTHON_INSTALL_PATH)/bin/python3-config
CXXFLAGS += $(shell $(PYTHON_CONFIG_PATH) --cflags)
Expand Down Expand Up @@ -42,3 +42,8 @@ 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-marshal:
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"marshal.py\"" -ldl $(LDFLAGS) -o fuzzer-marshal
fuzzer-pickle:
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"pickle.py\"" -ldl $(LDFLAGS) -o fuzzer-pickle
Binary file added corp-marshal/big_tuple_v4.marshal
Binary file not shown.
Binary file added corp-marshal/code_nested_v4.marshal
Binary file not shown.
Binary file added corp-marshal/code_object_v4.marshal
Binary file not shown.
Binary file added corp-marshal/containers_v2.marshal
Binary file not shown.
Binary file added corp-marshal/containers_v3.marshal
Binary file not shown.
Binary file added corp-marshal/containers_v4.marshal
Binary file not shown.
Binary file added corp-marshal/float_variants_v4.marshal
Binary file not shown.
Binary file added corp-marshal/negative_and_huge_ints_v4.marshal
Binary file not shown.
Binary file added corp-marshal/recursive_list_v4.marshal
Binary file not shown.
Binary file added corp-marshal/strings_v2.marshal
Binary file not shown.
Binary file added corp-marshal/strings_v3.marshal
Binary file not shown.
Binary file added corp-marshal/strings_v4.marshal
Binary file not shown.
Binary file added corp-marshal/types_v2.marshal
Binary file not shown.
Binary file added corp-marshal/types_v3.marshal
Binary file not shown.
Binary file added corp-marshal/types_v4.marshal
Binary file not shown.
27 changes: 27 additions & 0 deletions corp-pickle/basic_dict_p0.pickle
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
(dp0
Valpha
p1
I1
sVbeta
p2
(lp3
F2.5
aVx
p4
ac_codecs
encode
p5
(Vy
p6
Vlatin1
p7
tp8
Rp9
aNaI01
aI00
asVgamma
p10
(I1
I2
tp11
s.
Binary file added corp-pickle/basic_dict_p1.pickle
Binary file not shown.
Binary file added corp-pickle/basic_dict_p2.pickle
Binary file not shown.
Binary file added corp-pickle/basic_dict_p3.pickle
Binary file not shown.
Binary file added corp-pickle/basic_dict_p4.pickle
Binary file not shown.
Binary file added corp-pickle/basic_dict_p5.pickle
Binary file not shown.
Binary file added corp-pickle/bytes_and_bytearray.pickle
Binary file not shown.
Binary file added corp-pickle/complex_and_ellipsis.pickle
Binary file not shown.
Binary file added corp-pickle/empty_stream_variants.pickle
Binary file not shown.
Binary file added corp-pickle/float_values.pickle
Binary file not shown.
Binary file added corp-pickle/framed_proto4.pickle
Binary file not shown.
Binary file added corp-pickle/global_ref_type.pickle
Binary file not shown.
Binary file added corp-pickle/long_int.pickle
Binary file not shown.
Binary file added corp-pickle/memo_reuse.pickle
Binary file not shown.
1 change: 1 addition & 0 deletions corp-pickle/neg_int_p2.pickle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
€Jë2¤ø.
41 changes: 41 additions & 0 deletions corp-pickle/nested_p0.pickle
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
(dp0
Vlist
p1
(lp2
I1
a(lp3
I2
a(lp4
I3
a(dp5
Vdeep
p6
(I4
I5
tp7
saaasVset
p8
c__builtin__
set
p9
((lp10
I1
aI2
aI3
atp11
Rp12
sVfrozenset
p13
c__builtin__
frozenset
p14
((lp15
Va
p16
aVb
p17
aVc
p18
atp19
Rp20
s.
Binary file added corp-pickle/nested_p1.pickle
Binary file not shown.
Binary file added corp-pickle/nested_p2.pickle
Binary file not shown.
Binary file added corp-pickle/nested_p3.pickle
Binary file not shown.
Binary file added corp-pickle/nested_p4.pickle
Binary file not shown.
Binary file added corp-pickle/nested_p5.pickle
Binary file not shown.
12 changes: 12 additions & 0 deletions corp-pickle/proto0_text.pickle
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(dp0
Vkey
p1
Vvalue
p2
sVn
p3
(lp4
I1
aI2
aI3
as.
Binary file added corp-pickle/recursive_list.pickle
Binary file not shown.
Binary file added corp-pickle/stopiteration_and_range.pickle
Binary file not shown.
Binary file added corp-pickle/unicode_strs.pickle
Binary file not shown.
2 changes: 2 additions & 0 deletions fuzz_targets.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ email email.py
html html.py
httpclient httpclient.py
json json.py
marshal marshal.py
pickle pickle.py
plistlib plist.py
re re.py
tarfile tarfile.py
Expand Down
53 changes: 53 additions & 0 deletions fuzzer-marshal.dict
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# marshal type codes (with and without the 0x80 REF flag) and structure tokens
"N"
"F"
"T"
"S"
"i"
"I"
"f"
"g"
"x"
"y"
"A"
"s"
"t"
"u"
"r"
"e"
"c"
"("
"["
"{"
"<"
">"
")"
"0"
"C"
"w"
"\x80"
"\x81"
"\x84"
"\x88"
"\x89"
"\x8a"
"\x8d"
"\x8e"
"\x91"
"\xa0"
"\xa1"
"\xa2"
"\xa3"
"\xa4"
"\xa5"
"\xa6"
"\xa7"
"\xa8"
"\xa9"
"\xc0"
"\xc1"
"\xe0"
"\xe9"
"ab"
"aaa"
"\x00\x01"
71 changes: 71 additions & 0 deletions fuzzer-pickle.dict
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# pickle opcode bytes (protocol 0-5) and typical atoms
"\x80"
"\x80\x02"
"\x80\x04"
"\x80\x05"
"("
")"
"]"
"}"
"."
"t"
"G"
"X"
"U"
"T"
"V"
"S"
"C"
"B"
"L"
"K"
"M"
"J"
"F"
"I"
"o"
"i"
"R"
"P"
"Q"
"N"
"e"
"a"
"s"
"u"
"r"
"p"
"q"
"h"
"j"
"0"
"1"
"g"
"f"
"\x8a"
"\x8b"
"\x8c"
"\x8d"
"\x8e"
"\x8f"
"\x90"
"\x91"
"\x92"
"\x93"
"\x94"
"\x95"
"\x96"
"\x97"
"\x98"
"builtins"
"__main__"
"copyreg"
"_reconstructor"
"collections"
"OrderedDict"
"defaultdict"
"encode"
"loads"
"\x8cbuiltins"
"\x8cdict"
"\x94"
30 changes: 30 additions & 0 deletions marshal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Fuzz marshal.loads() over untrusted bytes.

marshal is the deserialiser behind .pyc files and marshal.dumps()/loads();
unmarshalling attacker-supplied bytes must stay memory-safe: invalid type

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong, we explicitly note:

marshal module is not intended to be secure against erroneous or maliciously constructed data. Never unmarshal data received from an untrusted or unauthenticated source.

I'm worried this will just be additional noise.

tags, negative or oversized length fields, dangling ref tags and malformed
code objects are all attacker-controlled. Input is capped at 1 MiB; the
deserialisation itself is expected to reject malformed streams with
EOFError/ValueError/TypeError/RecursionError/MemoryError -- anything else
propagates and aborts the fuzzer as a finding.
"""
import marshal

MAX_MARSHAL_TEST_SIZE = 0x100000 # 1 MiB, matching the other size-capped targets


def FuzzerRunOne(FuzzerInput):
if len(FuzzerInput) > MAX_MARSHAL_TEST_SIZE:
return
try:
marshal.loads(FuzzerInput)
except EOFError:
pass
except ValueError:
pass
except TypeError:
pass
except RecursionError:
pass
except MemoryError:
pass
31 changes: 31 additions & 0 deletions pickle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Fuzz the C unpickler over untrusted bytes.

pickle is the classic "never unpickle untrusted data" surface: the load
loop lives in the C accelerator _pickle.Unpickler. We subclass it only to
disable find_class, so mutated payloads cannot import arbitrary modules or
execute code inside the fuzzer process while every opcode-handling code
path (stack/memo manipulation, string and framing parsing, object
reconstruction of built-in types) stays covered. Invalid streams raise
UnpicklingError/EOFError, and object reconstruction (REDUCE, SETSTATE,
APPEND on built-in containers) can raise arbitrary exceptions -- those are
expected for attacker bytes; memory-safety bugs in the opcode machinery
still crash the sanitiser.
"""
import _pickle
import io

MAX_PICKLE_TEST_SIZE = 0x100000 # 1 MiB


class FuzzerUnpickler(_pickle.Unpickler):
def find_class(self, module, name):
raise _pickle.UnpicklingError("find_class is disabled")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, I'm concerned this will just be additional noise, and these aren't particularly important issues most of the time.



def FuzzerRunOne(FuzzerInput):
if len(FuzzerInput) > MAX_PICKLE_TEST_SIZE:
return
try:
FuzzerUnpickler(io.BytesIO(FuzzerInput)).load()
except Exception:
pass
Loading