Skip to content

Commit cb3c172

Browse files
committed
Add fuzzer for array module
1 parent 71ede86 commit cb3c172

3 files changed

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

array.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
from fuzzeddataprovider import FuzzedDataProvider
2+
import array
3+
4+
TYPECODES = list('bBhHiIlLqQfd')
5+
6+
# Top-level operation constants for FuzzerRunOne
7+
OP_FROMBYTES = 0
8+
OP_METHODS = 1
9+
OP_SLICE = 2
10+
11+
# Array method operation constants for op_array_methods
12+
METHOD_REVERSE = 0
13+
METHOD_BYTESWAP = 1
14+
METHOD_POP = 2
15+
METHOD_COUNT = 3
16+
METHOD_INDEX = 4
17+
METHOD_INSERT = 5
18+
METHOD_REMOVE = 6
19+
METHOD_TOBYTES = 7
20+
21+
def _consume_array(fdp):
22+
tc = fdp.PickValueInList(TYPECODES)
23+
itemsize = array.array(tc).itemsize
24+
n_items = fdp.ConsumeIntInRange(0, min(fdp.remaining_bytes() // itemsize, 200))
25+
data = fdp.ConsumeBytes(n_items * itemsize)
26+
a = array.array(tc)
27+
a.frombytes(data)
28+
return a, tc
29+
30+
31+
def op_array_frombytes(fdp):
32+
a, tc = _consume_array(fdp)
33+
a.tobytes()
34+
a.tolist()
35+
36+
def op_array_methods(fdp):
37+
a, tc = _consume_array(fdp)
38+
if len(a) == 0:
39+
return
40+
num_ops = fdp.ConsumeIntInRange(1, 20)
41+
for _ in range(num_ops):
42+
if fdp.remaining_bytes() == 0:
43+
break
44+
op = fdp.ConsumeIntInRange(METHOD_REVERSE, METHOD_TOBYTES)
45+
if op == METHOD_REVERSE:
46+
a.reverse()
47+
elif op == METHOD_BYTESWAP:
48+
a.byteswap()
49+
elif op == METHOD_POP and len(a) > 0:
50+
a.pop()
51+
elif op == METHOD_COUNT and len(a) > 0:
52+
val = fdp.ConsumeRandomValue()
53+
a.count(val)
54+
elif op == METHOD_INDEX and len(a) > 0:
55+
val = fdp.ConsumeRandomValue()
56+
try:
57+
a.index(val)
58+
except ValueError:
59+
pass
60+
elif op == METHOD_INSERT and len(a) > 0:
61+
idx = fdp.ConsumeIntInRange(0, len(a) - 1)
62+
val = fdp.ConsumeRandomValue()
63+
a.insert(idx, val)
64+
elif op == METHOD_REMOVE and len(a) > 0:
65+
val = fdp.ConsumeRandomValue()
66+
try:
67+
a.remove(val)
68+
except ValueError:
69+
pass
70+
elif op == METHOD_TOBYTES:
71+
a.tobytes()
72+
73+
def op_array_slice(fdp):
74+
a, tc = _consume_array(fdp)
75+
if len(a) < 2:
76+
return
77+
start = fdp.ConsumeIntInRange(0, len(a) - 1)
78+
end = fdp.ConsumeIntInRange(start, len(a))
79+
_ = a[start:end]
80+
b = array.array(tc, a[start:end])
81+
a[start:end] = b
82+
83+
# Fuzzes the array module's C implementation (Modules/arraymodule.c).
84+
# Exercises array construction from raw bytes via frombytes(), element-level
85+
# operations (reverse, byteswap, pop, count, index, insert, remove), and
86+
# slice read/write across all 12 typecodes (b/B/h/H/i/I/l/L/q/Q/f/d).
87+
def FuzzerRunOne(FuzzerInput):
88+
if len(FuzzerInput) < 1 or len(FuzzerInput) > 0x10000:
89+
return
90+
fdp = FuzzedDataProvider(FuzzerInput)
91+
op = fdp.ConsumeIntInRange(OP_FROMBYTES, OP_SLICE)
92+
try:
93+
if op == OP_FROMBYTES:
94+
op_array_frombytes(fdp)
95+
elif op == OP_METHODS:
96+
op_array_methods(fdp)
97+
elif op == OP_SLICE:
98+
op_array_slice(fdp)
99+
except Exception:
100+
pass

fuzz_targets.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
array array.py
12
ast ast.py
23
configparser configparser.py
34
csv csv.py

0 commit comments

Comments
 (0)