diff --git a/Makefile b/Makefile index 9103a1c..890db77 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -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 +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 PYTHON_CONFIG_PATH=$(CPYTHON_INSTALL_PATH)/bin/python3-config CXXFLAGS += $(shell $(PYTHON_CONFIG_PATH) --cflags) -LDFLAGS += -rdynamic $(shell $(PYTHON_CONFIG_PATH) --ldflags --embed) +LDFLAGS += -rdynamic $(shell $(PYTHON_CONFIG_PATH) --ldflags --embed) $(CPYTHON_MODLIBS) -Wl,--allow-multiple-definition fuzzer-html: 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: clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"xml.py\"" -ldl $(LDFLAGS) -o fuzzer-xml fuzzer-zoneinfo: clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"zoneinfo.py\"" -ldl $(LDFLAGS) -o fuzzer-zoneinfo + +fuzzer-datetime: + clang++ $(CXXFLAGS) $(LIB_FUZZING_ENGINE) -std=c++17 fuzzer.cpp -DPYTHON_HARNESS_PATH="\"datetime.py\"" -ldl $(LDFLAGS) -o fuzzer-datetime diff --git a/datetime.py b/datetime.py new file mode 100644 index 0000000..2554daf --- /dev/null +++ b/datetime.py @@ -0,0 +1,86 @@ +from fuzzeddataprovider import FuzzedDataProvider +from datetime import date, time, datetime + +# Parse target constants (op_parse) +PARSE_DATE_FROMISOFORMAT = 0 +PARSE_TIME_FROMISOFORMAT = 1 +PARSE_DATETIME_FROMISOFORMAT = 2 +PARSE_DATETIME_STRPTIME = 3 + +# Format target constants (op_format) +FORMAT_DATE = 0 +FORMAT_TIME = 1 +FORMAT_DATETIME = 2 + +OP_PARSE = 0 +OP_FORMAT = 1 + +STRPTIME_FORMATS = [ + "%Y-%m-%d", + "%Y-%m-%d %H:%M:%S", + "%d/%m/%Y", + "%m/%d/%Y", + "%Y%m%d", + "%H:%M:%S", + "%I:%M %p", + "%Y-%m-%dT%H:%M:%S", + "%a %b %d %H:%M:%S %Y", + "%c", +] + + +def op_parse(fdp): + s = fdp.ConsumeUnicode(fdp.ConsumeIntInRange(1, 100)) + if not s: + return + target = fdp.ConsumeIntInRange(PARSE_DATE_FROMISOFORMAT) + if target == PARSE_DATE_FROMISOFORMAT: + date.fromisoformat(s) + elif target == PARSE_TIME_FROMISOFORMAT: + time.fromisoformat(s) + elif target == PARSE_DATETIME_FROMISOFORMAT: + datetime.fromisoformat(s) + + +def op_format(fdp): + fmt = fdp.ConsumeUnicode(fdp.ConsumeIntInRange(1, 100)) + if not fmt: + return + target = fdp.ConsumeIntInRange(FORMAT_DATE, FORMAT_DATETIME) + if target == FORMAT_DATE: + year = fdp.ConsumeIntInRange(1, 9999) + month = fdp.ConsumeIntInRange(1, 12) + day = fdp.ConsumeIntInRange(1, 28) + date(year, month, day).strftime(fmt) + elif target == FORMAT_TIME: + hour = fdp.ConsumeIntInRange(0, 23) + minute = fdp.ConsumeIntInRange(0, 59) + second = fdp.ConsumeIntInRange(0, 59) + time(hour, minute, second).strftime(fmt) + elif target == FORMAT_DATETIME: + year = fdp.ConsumeIntInRange(1, 9999) + month = fdp.ConsumeIntInRange(1, 12) + day = fdp.ConsumeIntInRange(1, 28) + hour = fdp.ConsumeIntInRange(0, 23) + minute = fdp.ConsumeIntInRange(0, 59) + second = fdp.ConsumeIntInRange(0, 59) + datetime(year, month, day, hour, minute, second).strftime(fmt) + + +# Fuzzes the _datetime C module (Modules/_datetimemodule.c). +# Exercises ISO format parsing (date/time/datetime.fromisoformat), +# strptime with multiple predefined format strings, and strftime with +# fuzz-generated format strings. Only operations that pass fuzzed +# text into the C parser are included. +def FuzzerRunOne(FuzzerInput): + if len(FuzzerInput) < 1 or len(FuzzerInput) > 0x10000: + return + fdp = FuzzedDataProvider(FuzzerInput) + op = fdp.ConsumeIntInRange(OP_PARSE, OP_FORMAT) + try: + if op == OP_PARSE: + op_parse(fdp) + elif op == OP_FORMAT: + op_format(fdp) + except Exception: + pass diff --git a/fuzz_targets.txt b/fuzz_targets.txt index 8710a5f..cf7d8ef 100644 --- a/fuzz_targets.txt +++ b/fuzz_targets.txt @@ -1,6 +1,7 @@ ast ast.py configparser configparser.py csv csv.py +datetime datetime.py decode decode.py difflib difflib.py email email.py