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

Skip to content

Commit 058970e

Browse files
committed
Add fuzzer for array module
Fuzzes the CPython array C module (Modules/arraymodule.c). Picks one of the 12 typecodes (b/B/h/H/i/I/l/L/q/Q/f/d) and seeds an array via frombytes() from a fuzzed byte buffer sized as a multiple of the typecode's itemsize, then dispatches per input across three targets: a frombytes/tobytes/tolist roundtrip; a sequence of up to 20 element operations (reverse, byteswap, pop, count, index, insert, remove, tobytes) driven with random-typed values to reach type-error and ValueError paths in the C code; and slice read/write that assigns a freshly-built array back into a fuzzed [start:end] range to exercise the buffer resize logic.
1 parent a7e2ec1 commit 058970e

3 files changed

Lines changed: 109 additions & 1 deletion

File tree

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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-binascii
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-binascii fuzzer-array
22

33
PYTHON_CONFIG_PATH=$(CPYTHON_INSTALL_PATH)/bin/python3-config
44
CXXFLAGS += $(shell $(PYTHON_CONFIG_PATH) --cflags)
@@ -43,3 +43,6 @@ fuzzer-zoneinfo:
4343

4444
fuzzer-binascii:
4545
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"binascii.py\"" -ldl $(LDFLAGS) -o fuzzer-binascii
46+
47+
fuzzer-array:
48+
clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"array.py\"" -ldl $(LDFLAGS) -o fuzzer-array

array.py

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

0 commit comments

Comments
 (0)