Skip to content

Commit ba2c719

Browse files
committed
suite scripts: allow collecting timeouts as well as crashes
1 parent f50f226 commit ba2c719

2 files changed

Lines changed: 66 additions & 41 deletions

File tree

harness-suite/suite-crashers.py

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,100 +5,125 @@
55
import subprocess
66
import shutil
77
from pathlib import Path
8+
from typing import Literal
89

910

1011
parser = argparse.ArgumentParser()
11-
parser.add_argument('--wasmfuzz', default="wasmfuzz")
12-
parser.add_argument('--harness-dir', default="./out")
13-
parser.add_argument('--corpus-dir', default="./corpus")
14-
parser.add_argument('--crashes-dir', default="./crashes")
15-
parser.add_argument('--tags', default="./tags.csv")
16-
parser.add_argument('--only-target', default=None)
12+
parser.add_argument("--wasmfuzz", default="wasmfuzz")
13+
parser.add_argument("--harness-dir", default="./out")
14+
parser.add_argument("--corpus-dir", default="./corpus")
15+
parser.add_argument("--crashes-dir", default="./crashes")
16+
parser.add_argument("--ooms-dir", default="./ooms")
17+
parser.add_argument("--timeouts-dir", default="./timeouts")
18+
parser.add_argument("--tags", default="./tags.csv")
19+
parser.add_argument("--only-target", default=None)
20+
parser.add_argument("--mode", default="crash")
1721

1822
args = parser.parse_args()
1923
harness_dir = Path(args.harness_dir)
2024
corpus_dir = Path(args.corpus_dir)
2125
crashes_dir = Path(args.crashes_dir)
26+
ooms_dir = Path(args.ooms_dir)
27+
timeouts_dir = Path(args.timeouts_dir)
2228
tags_path = Path(args.tags)
23-
assert all(x.exists() for x in [harness_dir, crashes_dir, tags_path])
29+
mode = args.mode
30+
# Literal["crash"] | Literal["oom"] | Literal["timeout"]
31+
assert mode in ["crash", "oom", "timeout"]
32+
mode_path = {"crash": crashes_dir, "oom": ooms_dir, "timeout": timeouts_dir}[mode]
33+
mode_path.mkdir(exist_ok=True)
34+
csv_key = {"crash": "crashed", "timeout": "timeout", "oom": "oom"}[mode]
35+
36+
assert all(x.exists() for x in [harness_dir, mode_path, tags_path])
2437
if not corpus_dir.exists():
2538
print("[!] corpus directory not found, can't cross-check")
2639

27-
def check_reproduces(harness_path, crash_path):
40+
41+
def check_reproduces(harness_path, crash_path, mode="crash"):
2842
print(f"[*] reproducing {crash_path} ...")
29-
res = subprocess.check_output([
30-
args.wasmfuzz,
31-
"run-input",
32-
harness_path,
33-
crash_path
34-
])
35-
print("[>]", res.decode('utf8', errors='ignore').splitlines()[-1].strip())
36-
if b"execution trapped with" in res and b"which indicates that the target crashed" in res:
37-
return True
43+
res = subprocess.check_output(
44+
[args.wasmfuzz, "run-input", harness_path, crash_path]
45+
)
46+
print("[>]", res.decode("utf8", errors="ignore").splitlines()[-1].strip())
47+
if mode == "crash":
48+
return (
49+
b"execution trapped with" in res
50+
and b"which indicates that the target crashed" in res
51+
)
52+
if mode == "oom":
53+
raise RuntimeError("TODO")
54+
if mode == "timeout":
55+
return b"execution stopped with OutOfFuel" in res
56+
3857

3958
harness_paths = sorted(list(harness_dir.glob("*.wasm")))
4059

4160
verified = set()
4261

62+
4363
def check_harness(harness_path):
4464
harness = harness_path.name
45-
crash_path = crashes_dir / f"{harness_path.stem}.bin"
65+
crash_path = mode_path / f"{harness_path.stem}.bin"
4666
if crash_path.exists():
47-
if check_reproduces(harness_path, crash_path):
67+
if check_reproduces(harness_path, crash_path, mode=mode):
4868
verified.add(harness)
4969
return
5070

51-
if not corpus_dir.exists(): return
71+
if not corpus_dir.exists():
72+
return
5273
for corpus_snapshot in corpus_dir.glob(f"{harness_path.stem}/snapshot-*.csv"):
5374
with open(corpus_snapshot) as f:
5475
for elem in csv.DictReader(f):
55-
if elem["crashed"] and int(elem["crashed"]):
56-
input_path = corpus_snapshot.parent / corpus_snapshot.stem / elem["input"]
57-
assert check_reproduces(harness_path, input_path)
76+
if elem[csv_key] and int(elem[csv_key]):
77+
input_path = (
78+
corpus_snapshot.parent / corpus_snapshot.stem / elem["input"]
79+
)
80+
assert check_reproduces(harness_path, input_path, mode=mode)
5881
verified.add(harness)
5982
shutil.copy(input_path, crash_path)
6083
return
6184

85+
6286
for harness_path in harness_paths:
6387
if args.only_target is not None and args.only_target not in harness_path.name:
6488
continue
6589
check_harness(harness_path)
6690

6791
mismatches = set()
68-
with open(tags_path) as f:
69-
for row in csv.DictReader(f):
70-
harness = Path(row["harness"]).name
71-
if args.only_target is not None and args.only_target not in harness:
72-
continue
73-
tagged_crash = row["crashing"] and bool(int(row["crashing"]))
74-
if tagged_crash != (harness in verified):
75-
mismatches.add(harness)
92+
if mode == "crash":
93+
with open(tags_path) as f:
94+
for row in csv.DictReader(f):
95+
harness = Path(row["harness"]).name
96+
if args.only_target is not None and args.only_target not in harness:
97+
continue
98+
tagged_crash = row["crashing"] and bool(int(row["crashing"]))
99+
if tagged_crash != (harness in verified):
100+
mismatches.add(harness)
76101

77-
for crash_path in crashes_dir.glob("*.bin"):
78-
if args.only_target is not None and args.only_target not in crash_path.stem:
102+
for input_path in mode_path.glob("*.bin"):
103+
if args.only_target is not None and args.only_target not in input_path.stem:
79104
continue
80-
harness = crash_path.stem
105+
harness = input_path.stem
81106
harness_path = harness_dir / f"{harness}.wasm"
82107
if not harness_path.exists():
83108
print(f"[!] {harness}: Missing harness!")
84109

85110
print()
86-
print("-"*80)
111+
print("-" * 80)
87112
print(f"{len(mismatches)} mismatches found" + ".:"[bool(mismatches)])
88113
with open(tags_path) as f:
89114
for row in csv.DictReader(f):
90115
harness = Path(row["harness"])
91116
if args.only_target is not None and args.only_target not in harness.name:
92117
continue
93-
crash_path = crashes_dir / f"{harness.stem}.bin"
118+
input_path = mode_path / f"{harness.stem}.bin"
94119
tagged_crash = row["crashing"] and bool(int(row["crashing"]))
95120
if tagged_crash and harness.name not in verified:
96-
if crash_path.exists():
121+
if input_path.exists():
97122
print(f"[!] {harness.name}: Input for tagged crash didn't reproduce!")
98123
else:
99124
print(f"[!] {harness.name}: Missing reproducer for tagged harness!")
100-
if crash_path.exists() and not tagged_crash:
125+
if input_path.exists() and not tagged_crash:
101126
if harness.name in verified:
102127
print(f"[!] {harness.name}: Crash reproduced but not tagged!")
103128
else:
104-
print(f"[!] {crash_path} exists but doesn't reproduce!")
129+
print(f"[!] {input_path} exists but doesn't reproduce!")

harness-suite/suite-run.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ async def run_target(target):
117117
f"--dir={snapshot_dir}",
118118
f"--csv-out={corpus_info_csv}",
119119
],
120-
pipe_stdout=f"/tmp/wasmfuzz-logs/{target.stem}-{snapshot}.log",
120+
pipe_stdout=f"/tmp/wasmfuzz-logs/{target.stem}-{snapshot}-corpus-info.log",
121121
stdout_prefix=target.stem,
122122
mix_stderr=True
123123
)
@@ -126,7 +126,7 @@ async def run_target(target):
126126
async def main():
127127
jobs = []
128128
for target in Path(args.harness_dir).glob("*.wasm"):
129-
if args.target and all(tgt not in target.stem for tgt in args.target):
129+
if args.target and not any(tgt in target.stem for tgt in args.target):
130130
continue
131131
if args.no_parallel:
132132
await run_target(target)

0 commit comments

Comments
 (0)