Skip to content

Commit 9dd0ba6

Browse files
committed
Add fuzzer for compression module
1 parent 71ede86 commit 9dd0ba6

File tree

3 files changed

+146
-2
lines changed

3 files changed

+146
-2
lines changed

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-compression
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-compression:
45+
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"compression.py\"" -ldl $(LDFLAGS) -o fuzzer-compression

compression.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
from fuzzeddataprovider import FuzzedDataProvider
2+
import zlib
3+
import bz2
4+
import lzma
5+
6+
WBITS_CHOICES = [-15, 0, 15, 31, 47]
7+
MAX_DECOMPRESS_LEN = 1024 * 1024 # 1 MiB cap to prevent OOM from small inputs
8+
9+
OP_ZLIB_DECOMPRESS = 0
10+
OP_ZLIB_COMPRESS = 1
11+
OP_ZLIB_CHECKSUM = 2
12+
OP_BZ2_COMPRESS_DECOMPRESS = 3
13+
OP_LZMA_DECOMPRESS = 4
14+
OP_LZMA_COMPRESS = 5
15+
NUM_OPS = 6
16+
17+
18+
def op_zlib_decompress(fdp):
19+
wbits = fdp.PickValueInList(WBITS_CHOICES)
20+
use_zdict = fdp.ConsumeBool()
21+
do_flush = fdp.ConsumeBool()
22+
do_copy = fdp.ConsumeBool()
23+
zdict = b""
24+
if use_zdict:
25+
zdict_size = fdp.ConsumeIntInRange(1, 32768)
26+
zdict = fdp.ConsumeBytes(zdict_size)
27+
data = fdp.ConsumeBytes(fdp.remaining_bytes())
28+
kwargs = {}
29+
if zdict:
30+
kwargs["zdict"] = zdict
31+
dobj = zlib.decompressobj(wbits, **kwargs)
32+
dobj.decompress(data, MAX_DECOMPRESS_LEN)
33+
if do_flush:
34+
dobj.flush()
35+
if do_copy:
36+
copy_obj = dobj.copy()
37+
copy_obj.decompress(data, MAX_DECOMPRESS_LEN)
38+
39+
40+
def op_zlib_compress(fdp):
41+
level = fdp.ConsumeIntInRange(0, 9)
42+
use_obj = fdp.ConsumeBool()
43+
do_copy = fdp.ConsumeBool()
44+
n = fdp.ConsumeIntInRange(1, 10000)
45+
data = fdp.ConsumeBytes(n)
46+
if not data:
47+
return
48+
if use_obj:
49+
cobj = zlib.compressobj(level)
50+
cobj.compress(data)
51+
if do_copy:
52+
copy_obj = cobj.copy()
53+
copy_obj.flush()
54+
cobj.flush()
55+
else:
56+
zlib.compress(data, level)
57+
58+
59+
def op_zlib_checksum(fdp):
60+
use_crc = fdp.ConsumeBool()
61+
data = fdp.ConsumeBytes(fdp.remaining_bytes())
62+
if use_crc:
63+
zlib.crc32(data)
64+
else:
65+
zlib.adler32(data)
66+
67+
68+
def op_bz2(fdp):
69+
do_compress = fdp.ConsumeBool()
70+
n = (
71+
fdp.ConsumeIntInRange(1, min(fdp.remaining_bytes(), 10000))
72+
if fdp.remaining_bytes() > 0
73+
else 0
74+
)
75+
if n == 0:
76+
return
77+
data = fdp.ConsumeBytes(n)
78+
if do_compress:
79+
bz2.compress(data)
80+
else:
81+
dobj = bz2.BZ2Decompressor()
82+
dobj.decompress(data, MAX_DECOMPRESS_LEN)
83+
84+
85+
def op_lzma_decompress(fdp):
86+
formats = [lzma.FORMAT_AUTO, lzma.FORMAT_XZ, lzma.FORMAT_ALONE, lzma.FORMAT_RAW]
87+
fmt = fdp.PickValueInList(formats)
88+
n = (
89+
fdp.ConsumeIntInRange(1, min(fdp.remaining_bytes(), 10000))
90+
if fdp.remaining_bytes() > 0
91+
else 0
92+
)
93+
if n == 0:
94+
return
95+
data = fdp.ConsumeBytes(n)
96+
kwargs = {"format": fmt, "memlimit": 16 * 1024 * 1024}
97+
if fmt == lzma.FORMAT_RAW:
98+
kwargs["filters"] = [{"id": lzma.FILTER_LZMA2}]
99+
del kwargs["memlimit"]
100+
dobj = lzma.LZMADecompressor(**kwargs)
101+
dobj.decompress(data, MAX_DECOMPRESS_LEN)
102+
103+
104+
def op_lzma_compress(fdp):
105+
n = (
106+
fdp.ConsumeIntInRange(1, min(fdp.remaining_bytes(), 10000))
107+
if fdp.remaining_bytes() > 0
108+
else 0
109+
)
110+
if n == 0:
111+
return
112+
data = fdp.ConsumeBytes(n)
113+
lzma.compress(data)
114+
115+
116+
# Fuzzes zlib, bz2, and lzma C modules (Modules/zlibmodule.c,
117+
# Modules/_bz2module.c, Modules/_lzmamodule.c). Exercises decompression
118+
# with various wbits/format settings and optional zlib dictionaries,
119+
# compression at different levels with compressobj/compress, CRC32/Adler32
120+
# checksums, and BZ2/LZMA decompressor objects with memory limits.
121+
def FuzzerRunOne(FuzzerInput):
122+
if len(FuzzerInput) < 1 or len(FuzzerInput) > 0x100000:
123+
return
124+
fdp = FuzzedDataProvider(FuzzerInput)
125+
op = fdp.ConsumeIntInRange(0, NUM_OPS - 1)
126+
try:
127+
if op == OP_ZLIB_DECOMPRESS:
128+
op_zlib_decompress(fdp)
129+
elif op == OP_ZLIB_COMPRESS:
130+
op_zlib_compress(fdp)
131+
elif op == OP_ZLIB_CHECKSUM:
132+
op_zlib_checksum(fdp)
133+
elif op == OP_BZ2_COMPRESS_DECOMPRESS:
134+
op_bz2(fdp)
135+
elif op == OP_LZMA_DECOMPRESS:
136+
op_lzma_decompress(fdp)
137+
else:
138+
op_lzma_compress(fdp)
139+
except Exception:
140+
pass

fuzz_targets.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
ast ast.py
2+
compression compression.py
23
configparser configparser.py
34
csv csv.py
45
decode decode.py

0 commit comments

Comments
 (0)