ClickHouse/tests/fuzz/runner.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

184 lines
5.6 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import configparser
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-09-30 03:43:34 +00:00
def report(source: str, reason: str, call_stack: list, test_unit: str):
print(f"########### REPORT: {source} {reason} {test_unit}")
for line in call_stack:
print(f" {line}")
print("########### END OF REPORT ###########")
2024-09-30 03:43:34 +00:00
2024-09-30 04:02:25 +00:00
# pylint: disable=unused-argument
def process_fuzzer_output(output: str):
pass
2024-09-30 03:43:34 +00:00
def process_error(error: str):
2024-10-02 14:01:02 +00:00
ERROR = r"^==\d+==\s?ERROR: (\S+): (.*)"
2024-09-30 03:43:34 +00:00
error_source = ""
error_reason = ""
TEST_UNIT_LINE = r"artifact_prefix='.*/'; Test unit written to (.*)"
call_stack = []
is_call_stack = False
2024-09-30 04:02:25 +00:00
# pylint: disable=unused-variable
for line_num, line in enumerate(error.splitlines(), 1):
if is_call_stack:
2024-10-02 14:01:02 +00:00
if re.search(r"^==\d+==", line):
2024-09-30 04:02:25 +00:00
is_call_stack = False
2024-10-02 14:01:02 +00:00
continue
call_stack.append(line)
2024-09-30 04:02:25 +00:00
continue
2024-10-02 14:01:02 +00:00
if call_stack:
match = re.search(TEST_UNIT_LINE, line)
if match:
2024-10-02 14:01:02 +00:00
report(error_source, error_reason, call_stack, match.group(1))
call_stack.clear()
continue
2024-10-02 14:01:02 +00:00
match = re.search(ERROR, line)
if match:
2024-10-02 14:01:02 +00:00
error_source = match.group(1)
error_reason = match.group(2)
is_call_stack = True
2024-09-30 03:43:34 +00:00
2024-10-01 14:02:17 +00:00
def run_fuzzer(fuzzer: str, timeout: int):
2024-10-02 20:07:02 +00:00
s3 = S3Helper()
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 = ""
active_corpus_dir = f"{fuzzer}.corpus"
2024-10-02 20:36:31 +00:00
s3.download_files(
bucket=S3_BUILDS_BUCKET,
2024-10-02 20:24:13 +00:00
s3_path=f"fuzzer/corpus/{fuzzer}/",
file_suffix="",
local_directory=active_corpus_dir,
)
2024-10-02 20:07:02 +00:00
new_corpus_dir = f"{fuzzer}.corpus_new"
if not os.path.exists(new_corpus_dir):
os.makedirs(new_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-07-16 15:16:11 +00:00
f"-{key}={value}" for key, value in parser["libfuzzer"].items()
)
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-02 20:24:13 +00:00
cmd_line = f"{DEBUGGER} ./{fuzzer} {FUZZER_ARGS} {new_corpus_dir} {active_corpus_dir} {seed_corpus_dir}"
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"
cmd_line += " < /dev/null"
2024-07-16 14:17:51 +00:00
logging.info("...will execute: %s", cmd_line)
2024-09-30 03:43:34 +00:00
# subprocess.check_call(cmd_line, shell=True)
try:
result = subprocess.run(
cmd_line,
stderr=subprocess.PIPE,
stdout=subprocess.DEVNULL,
text=True,
check=True,
2024-09-30 03:43:34 +00:00
shell=True,
2024-09-30 15:03:00 +00:00
errors="replace",
2024-10-01 14:02:17 +00:00
timeout=timeout,
)
except subprocess.CalledProcessError as e:
2024-09-30 03:43:34 +00:00
# print("Command failed with error:", e)
2024-10-01 18:25:22 +00:00
print("Stderr output: ", e.stderr)
process_error(e.stderr)
2024-10-01 18:25:22 +00:00
except subprocess.TimeoutExpired as e:
2024-10-02 20:07:02 +00:00
print("Timeout for ", cmd_line)
2024-10-01 18:25:22 +00:00
process_fuzzer_output(e.stderr)
else:
process_fuzzer_output(result.stderr)
2024-10-02 21:09:31 +00:00
with open(f"{new_corpus_dir}/testfile", "a", encoding="ascii") as f:
2024-10-02 20:57:16 +00:00
f.write("Now the file has more content!")
2024-10-02 20:07:02 +00:00
s3.upload_build_directory_to_s3(new_corpus_dir, "fuzzer/corpus/")
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-01 14:02:17 +00:00
timeout = 30
match = re.search(r"(^|\s+)-max_total_time=(\d+)($|\s)", FUZZER_ARGS)
if match:
2024-10-01 15:49:26 +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-01 14:02:17 +00:00
run_fuzzer(fuzzer, timeout)
2024-07-15 19:10:07 +00:00
if __name__ == "__main__":
2024-10-02 23:03:08 +00:00
from os import path, sys
2024-10-02 23:19:06 +00:00
2024-10-02 22:39:20 +00:00
ACTIVE_DIR = path.dirname(path.abspath(__file__))
2024-10-03 05:39:42 +00:00
sys.path.append((Path(path.dirname(ACTIVE_DIR)) / "ci").as_posix())
2024-10-03 03:12:58 +00:00
from env_helper import ( # pylint: disable=import-error,no-name-in-module
2024-10-03 00:52:51 +00:00
S3_BUILDS_BUCKET,
)
2024-10-03 03:12:58 +00:00
from s3_helper import S3Helper # pylint: disable=import-error,no-name-in-module
2024-10-02 23:19:06 +00:00
2024-07-15 19:10:07 +00:00
main()