Skip to content

Commit 361a5b8

Browse files
committed
Add fuzzer for datetime module
1 parent 71ede86 commit 361a5b8

3 files changed

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