Skip to content

Commit 9f80b17

Browse files
committed
Add fuzzer for datetime module
1 parent 71ede86 commit 9f80b17

File tree

3 files changed

+92
-2
lines changed

3 files changed

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

datetime.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from fuzzeddataprovider import FuzzedDataProvider
2+
from datetime import date, time, datetime
3+
4+
# Parse target constants (op_parse)
5+
PARSE_DATE_FROMISOFORMAT = 0
6+
PARSE_TIME_FROMISOFORMAT = 1
7+
PARSE_DATETIME_FROMISOFORMAT = 2
8+
PARSE_DATETIME_STRPTIME = 3
9+
10+
# Format target constants (op_format)
11+
FORMAT_DATE = 0
12+
FORMAT_TIME = 1
13+
FORMAT_DATETIME = 2
14+
15+
OP_PARSE = 0
16+
OP_FORMAT = 1
17+
18+
STRPTIME_FORMATS = [
19+
"%Y-%m-%d",
20+
"%Y-%m-%d %H:%M:%S",
21+
"%d/%m/%Y",
22+
"%m/%d/%Y",
23+
"%Y%m%d",
24+
"%H:%M:%S",
25+
"%I:%M %p",
26+
"%Y-%m-%dT%H:%M:%S",
27+
"%a %b %d %H:%M:%S %Y",
28+
"%c",
29+
]
30+
31+
32+
def op_parse(fdp):
33+
s = fdp.ConsumeUnicode(fdp.ConsumeIntInRange(1, 100))
34+
if not s:
35+
return
36+
target = fdp.ConsumeIntInRange(PARSE_DATE_FROMISOFORMAT)
37+
if target == PARSE_DATE_FROMISOFORMAT:
38+
date.fromisoformat(s)
39+
elif target == PARSE_TIME_FROMISOFORMAT:
40+
time.fromisoformat(s)
41+
elif target == PARSE_DATETIME_FROMISOFORMAT:
42+
datetime.fromisoformat(s)
43+
44+
45+
def op_format(fdp):
46+
fmt = fdp.ConsumeUnicode(fdp.ConsumeIntInRange(1, 100))
47+
if not fmt:
48+
return
49+
target = fdp.ConsumeIntInRange(FORMAT_DATE, FORMAT_DATETIME)
50+
if target == FORMAT_DATE:
51+
year = fdp.ConsumeIntInRange(1, 9999)
52+
month = fdp.ConsumeIntInRange(1, 12)
53+
day = fdp.ConsumeIntInRange(1, 28)
54+
date(year, month, day).strftime(fmt)
55+
elif target == FORMAT_TIME:
56+
hour = fdp.ConsumeIntInRange(0, 23)
57+
minute = fdp.ConsumeIntInRange(0, 59)
58+
second = fdp.ConsumeIntInRange(0, 59)
59+
time(hour, minute, second).strftime(fmt)
60+
elif target == FORMAT_DATETIME:
61+
year = fdp.ConsumeIntInRange(1, 9999)
62+
month = fdp.ConsumeIntInRange(1, 12)
63+
day = fdp.ConsumeIntInRange(1, 28)
64+
hour = fdp.ConsumeIntInRange(0, 23)
65+
minute = fdp.ConsumeIntInRange(0, 59)
66+
second = fdp.ConsumeIntInRange(0, 59)
67+
datetime(year, month, day, hour, minute, second).strftime(fmt)
68+
69+
70+
# Fuzzes the _datetime C module (Modules/_datetimemodule.c).
71+
# Exercises ISO format parsing (date/time/datetime.fromisoformat),
72+
# strptime with multiple predefined format strings, and strftime with
73+
# fuzz-generated format strings. Only operations that pass fuzzed
74+
# text into the C parser are included.
75+
def FuzzerRunOne(FuzzerInput):
76+
if len(FuzzerInput) < 1 or len(FuzzerInput) > 0x10000:
77+
return
78+
fdp = FuzzedDataProvider(FuzzerInput)
79+
op = fdp.ConsumeIntInRange(OP_PARSE, OP_FORMAT)
80+
try:
81+
if op == OP_PARSE:
82+
op_parse(fdp)
83+
elif op == OP_FORMAT:
84+
op_format(fdp)
85+
except Exception:
86+
pass

fuzz_targets.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
ast ast.py
22
configparser configparser.py
33
csv csv.py
4+
datetime datetime.py
45
decode decode.py
56
difflib difflib.py
67
email email.py

0 commit comments

Comments
 (0)