ClickHouse/tests/fuzz/runner.py

161 lines
5.2 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import configparser
2024-10-17 01:00:27 +00:00
import datetime
import logging
import os
import re
import subprocess
2024-09-30 03:43:34 +00:00
from pathlib import Path
2024-10-02 20:24:13 +00:00
DEBUGGER = os.getenv("DEBUGGER", "")
FUZZER_ARGS = os.getenv("FUZZER_ARGS", "")
2024-10-16 21:23:04 +00:00
OUTPUT = "/test_output"
2024-09-30 03:43:34 +00:00
2024-10-17 01:00:27 +00:00
class Stopwatch:
def __init__(self):
self.reset()
@property
def duration_seconds(self) -> float:
return (datetime.datetime.utcnow() - self.start_time).total_seconds()
@property
def start_time_str(self) -> str:
return self.start_time_str_value
def reset(self) -> None:
self.start_time = datetime.datetime.utcnow()
self.start_time_str_value = self.start_time.strftime("%Y-%m-%d %H:%M:%S")
2024-10-16 02:15:04 +00:00
def run_fuzzer(fuzzer: str, timeout: int):
2024-07-16 14:17:51 +00:00
logging.info("Running fuzzer %s...", fuzzer)
seed_corpus_dir = f"{fuzzer}.in"
with Path(seed_corpus_dir) as path:
if not path.exists() or not path.is_dir():
seed_corpus_dir = ""
2024-10-17 16:29:19 +00:00
active_corpus_dir = f"corpus/{fuzzer}"
2024-10-20 21:24:10 +00:00
if not os.path.exists(active_corpus_dir):
os.makedirs(active_corpus_dir)
options_file = f"{fuzzer}.options"
custom_libfuzzer_options = ""
fuzzer_arguments = ""
with Path(options_file) as path:
if path.exists() and path.is_file():
parser = configparser.ConfigParser()
parser.read(path)
if parser.has_section("asan"):
os.environ["ASAN_OPTIONS"] = (
2024-07-16 15:01:43 +00:00
f"{os.environ['ASAN_OPTIONS']}:{':'.join(f'{key}={value}' for key, value in parser['asan'].items())}"
)
if parser.has_section("msan"):
os.environ["MSAN_OPTIONS"] = (
2024-07-16 15:01:43 +00:00
f"{os.environ['MSAN_OPTIONS']}:{':'.join(f'{key}={value}' for key, value in parser['msan'].items())}"
)
if parser.has_section("ubsan"):
os.environ["UBSAN_OPTIONS"] = (
2024-07-16 15:01:43 +00:00
f"{os.environ['UBSAN_OPTIONS']}:{':'.join(f'{key}={value}' for key, value in parser['ubsan'].items())}"
)
if parser.has_section("libfuzzer"):
custom_libfuzzer_options = " ".join(
2024-10-10 00:52:58 +00:00
f"-{key}={value}"
for key, value in parser["libfuzzer"].items()
2024-10-16 21:47:00 +00:00
if key not in ("jobs", "exact_artifact_path")
)
if parser.has_section("fuzzer_arguments"):
fuzzer_arguments = " ".join(
2024-07-16 14:17:51 +00:00
(f"{key}") if value == "" else (f"{key}={value}")
for key, value in parser["fuzzer_arguments"].items()
)
2024-10-16 21:23:04 +00:00
exact_artifact_path = f"{OUTPUT}/{fuzzer}.unit"
status_path = f"{OUTPUT}/{fuzzer}.status"
out_path = f"{OUTPUT}/{fuzzer}.out"
2024-10-22 15:22:59 +00:00
stdout_path = f"{OUTPUT}/{fuzzer}.stdout"
2024-10-16 21:23:04 +00:00
2024-10-22 04:40:34 +00:00
cmd_line = f"{DEBUGGER} ./{fuzzer} {active_corpus_dir} {seed_corpus_dir}"
2024-10-02 20:24:13 +00:00
2024-10-16 21:23:04 +00:00
cmd_line += f" -exact_artifact_path={exact_artifact_path}"
if custom_libfuzzer_options:
cmd_line += f" {custom_libfuzzer_options}"
if fuzzer_arguments:
cmd_line += f" {fuzzer_arguments}"
if not "-dict=" in cmd_line and Path(f"{fuzzer}.dict").exists():
cmd_line += f" -dict={fuzzer}.dict"
2024-07-16 14:17:51 +00:00
logging.info("...will execute: %s", cmd_line)
2024-10-16 01:10:57 +00:00
stopwatch = Stopwatch()
try:
2024-10-22 15:22:59 +00:00
with open(out_path, "wb") as out, open(stdout_path, "wb") as stdout:
2024-10-20 18:38:35 +00:00
subprocess.run(
2024-10-22 01:48:17 +00:00
cmd_line.split(),
2024-10-22 02:46:28 +00:00
stdin=subprocess.DEVNULL,
2024-10-22 15:22:59 +00:00
stdout=stdout,
2024-10-22 02:46:28 +00:00
stderr=out,
2024-10-16 21:39:03 +00:00
text=True,
check=True,
2024-10-22 01:48:17 +00:00
shell=False,
2024-10-16 21:39:03 +00:00
errors="replace",
timeout=timeout,
)
2024-10-20 18:38:35 +00:00
except subprocess.CalledProcessError:
2024-10-22 01:48:17 +00:00
logging.info("Fail running %s", fuzzer)
2024-10-16 22:34:40 +00:00
with open(status_path, "w", encoding="utf-8") as status:
2024-10-16 21:39:03 +00:00
status.write(
f"FAIL\n{stopwatch.start_time_str}\n{stopwatch.duration_seconds}\n"
)
2024-10-20 18:38:35 +00:00
except subprocess.TimeoutExpired:
2024-10-22 12:25:15 +00:00
logging.info("Successful running %s", fuzzer)
2024-10-16 22:34:40 +00:00
with open(status_path, "w", encoding="utf-8") as status:
2024-10-16 21:39:03 +00:00
status.write(
2024-10-22 12:25:15 +00:00
f"OK\n{stopwatch.start_time_str}\n{stopwatch.duration_seconds}\n"
2024-10-16 21:39:03 +00:00
)
2024-10-22 15:22:59 +00:00
except Exception as e:
logging.info("Unexpected exception running %s: %s", fuzzer, e)
with open(status_path, "w", encoding="utf-8") as status:
status.write(
f"ERROR\n{stopwatch.start_time_str}\n{stopwatch.duration_seconds}\n"
)
else:
2024-10-22 12:25:15 +00:00
logging.info("Error running %s", fuzzer)
2024-10-16 22:34:40 +00:00
with open(status_path, "w", encoding="utf-8") as status:
2024-10-16 21:39:03 +00:00
status.write(
2024-10-22 12:25:15 +00:00
f"ERROR\n{stopwatch.start_time_str}\n{stopwatch.duration_seconds}\n"
2024-10-16 21:39:03 +00:00
)
2024-09-30 03:43:34 +00:00
2024-07-15 19:10:07 +00:00
def main():
logging.basicConfig(level=logging.INFO)
subprocess.check_call("ls -al", shell=True)
2024-10-22 04:25:08 +00:00
timeout = 60
2024-10-01 14:02:17 +00:00
match = re.search(r"(^|\s+)-max_total_time=(\d+)($|\s)", FUZZER_ARGS)
if match:
2024-10-22 04:25:53 +00:00
timeout = int(match.group(2))
2024-10-01 14:02:17 +00:00
with Path() as current:
for fuzzer in current.iterdir():
if (current / fuzzer).is_file() and os.access(current / fuzzer, os.X_OK):
2024-10-16 21:23:04 +00:00
run_fuzzer(fuzzer.name, timeout)
subprocess.check_call(f"ls -al {OUTPUT}", shell=True)
2024-07-15 19:10:07 +00:00
if __name__ == "__main__":
main()