Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 533dee0

Browse files
committed
Add fuzzer for pickle module
1 parent 71ede86 commit 533dee0

3 files changed

Lines changed: 132 additions & 2 deletions

File tree

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
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
1+
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-pickle
22

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

77
fuzzer-html:
88
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"html.py\"" -ldl $(LDFLAGS) -o fuzzer-html
@@ -40,3 +40,6 @@ fuzzer-xml:
4040
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"xml.py\"" -ldl $(LDFLAGS) -o fuzzer-xml
4141
fuzzer-zoneinfo:
4242
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"zoneinfo.py\"" -ldl $(LDFLAGS) -o fuzzer-zoneinfo
43+
44+
fuzzer-pickle:
45+
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"pickle.py\"" -ldl $(LDFLAGS) -o fuzzer-pickle

fuzz_targets.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ email email.py
77
html html.py
88
httpclient httpclient.py
99
json json.py
10+
pickle pickle.py
1011
plistlib plist.py
1112
re re.py
1213
tarfile tarfile.py

pickle.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
from fuzzeddataprovider import FuzzedDataProvider
2+
import pickle
3+
import io
4+
5+
MAX_CONTAINER_SIZE = 200 # cap on generated container/string sizes to avoid OOM
6+
7+
# Top-level operation constants for FuzzerRunOne dispatch
8+
OP_DUMPS = 0
9+
OP_LOADS = 1
10+
OP_PICKLER = 2
11+
OP_ROUNDTRIP = 3
12+
13+
# Container type constants for build_container
14+
CTYPE_BYTES = 0
15+
CTYPE_STRING = 1
16+
CTYPE_INT_LIST = 2
17+
CTYPE_TUPLE = 3
18+
CTYPE_SET = 4
19+
CTYPE_FROZENSET = 5
20+
CTYPE_BYTEARRAY = 6
21+
CTYPE_DICT = 7
22+
23+
# Unpickler variant constants for op_loads
24+
VARIANT_RESTRICTED = 0
25+
VARIANT_PERSISTENT = 1
26+
VARIANT_RESTRICTED_FIX_IMPORTS = 2
27+
28+
class RestrictedUnpickler(pickle.Unpickler):
29+
def find_class(self, module, name):
30+
raise pickle.UnpicklingError('restricted')
31+
32+
class PersistentUnpickler(pickle.Unpickler):
33+
def persistent_load(self, pid):
34+
return pid
35+
def find_class(self, module, name):
36+
raise pickle.UnpicklingError('restricted')
37+
38+
def build_container(fdp, ctype):
39+
n = fdp.ConsumeIntInRange(0, min(fdp.remaining_bytes(), MAX_CONTAINER_SIZE))
40+
if ctype == CTYPE_BYTES:
41+
return fdp.ConsumeBytes(n)
42+
elif ctype == CTYPE_STRING:
43+
return fdp.ConsumeUnicode(n)
44+
elif ctype == CTYPE_INT_LIST:
45+
return fdp.ConsumeIntList(n, 1)
46+
elif ctype == CTYPE_TUPLE:
47+
return tuple(fdp.ConsumeIntList(n, 1))
48+
elif ctype == CTYPE_SET:
49+
return set(fdp.ConsumeIntList(n, 1))
50+
elif ctype == CTYPE_FROZENSET:
51+
return frozenset(fdp.ConsumeIntList(n, 1))
52+
elif ctype == CTYPE_BYTEARRAY:
53+
return bytearray(fdp.ConsumeBytes(n))
54+
elif ctype == CTYPE_DICT:
55+
d = {}
56+
entries = fdp.ConsumeIntInRange(0, min(n, 64))
57+
for _ in range(entries):
58+
if fdp.remaining_bytes() == 0:
59+
break
60+
kn = fdp.ConsumeIntInRange(1, 20)
61+
key = fdp.ConsumeUnicode(kn)
62+
val = fdp.ConsumeRandomValue()
63+
d[key] = val
64+
return d
65+
return fdp.ConsumeBytes(n)
66+
67+
def op_dumps(fdp):
68+
ctype = fdp.ConsumeIntInRange(CTYPE_BYTES, CTYPE_DICT)
69+
protocol = fdp.ConsumeIntInRange(0, 5)
70+
fix_imports = fdp.ConsumeBool()
71+
obj = build_container(fdp, ctype)
72+
pickle.dumps(obj, protocol=protocol, fix_imports=fix_imports)
73+
74+
def op_loads(fdp):
75+
variant = fdp.ConsumeIntInRange(VARIANT_RESTRICTED, VARIANT_RESTRICTED_FIX_IMPORTS)
76+
data = fdp.ConsumeBytes(fdp.remaining_bytes())
77+
bio = io.BytesIO(data)
78+
if variant == VARIANT_RESTRICTED:
79+
unpickler = RestrictedUnpickler(bio)
80+
elif variant == VARIANT_PERSISTENT:
81+
unpickler = PersistentUnpickler(bio)
82+
else:
83+
unpickler = RestrictedUnpickler(bio, fix_imports=True, encoding='bytes')
84+
unpickler.load()
85+
86+
def op_pickler(fdp):
87+
protocol = fdp.ConsumeIntInRange(0, 5)
88+
n = fdp.ConsumeIntInRange(1, min(fdp.remaining_bytes(), MAX_CONTAINER_SIZE)) if fdp.remaining_bytes() > 0 else 0
89+
if n == 0:
90+
return
91+
obj1 = fdp.ConsumeIntList(n, 1)
92+
s = fdp.ConsumeUnicode(fdp.ConsumeIntInRange(0, MAX_CONTAINER_SIZE))
93+
bio = io.BytesIO()
94+
p = pickle.Pickler(bio, protocol)
95+
p.dump(obj1)
96+
p.clear_memo()
97+
p.dump(s)
98+
bio.getvalue()
99+
100+
def op_roundtrip(fdp):
101+
ctype = fdp.ConsumeIntInRange(CTYPE_BYTES, CTYPE_DICT)
102+
obj = build_container(fdp, ctype)
103+
dumped = pickle.dumps(obj)
104+
pickle.loads(dumped)
105+
106+
# Fuzzes the _pickle C module (Modules/_pickle.c). Exercises pickle.dumps()
107+
# with protocols 0-5 on various container types (bytes, strings, int lists,
108+
# tuples, sets, frozensets, bytearrays, dicts), pickle.loads() with
109+
# restricted and persistent-load unpickler variants, Pickler.dump() with
110+
# memo clearing, and dumps/loads roundtrips.
111+
def FuzzerRunOne(FuzzerInput):
112+
if len(FuzzerInput) < 1 or len(FuzzerInput) > 0x100000:
113+
return
114+
fdp = FuzzedDataProvider(FuzzerInput)
115+
op = fdp.ConsumeIntInRange(OP_DUMPS, OP_ROUNDTRIP)
116+
try:
117+
if op == OP_DUMPS:
118+
op_dumps(fdp)
119+
elif op == OP_LOADS:
120+
op_loads(fdp)
121+
elif op == OP_PICKLER:
122+
op_pickler(fdp)
123+
else:
124+
op_roundtrip(fdp)
125+
except Exception:
126+
pass

0 commit comments

Comments
 (0)