Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[submodule "UniTSyn"]
path = UniTSyn
url = https://github.com/SecurityLab-UCD/UniTSyn.git
[submodule "fuzz/oss-fuzz"]
path = fuzz/oss-fuzz
url = https://github.com/joyguoguo/oss-fuzz.git
branch = main
File renamed without changes.
136 changes: 136 additions & 0 deletions fuzz/build_oss_fuzz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
build_oss_fuzz.py

Parallel build of OSS-Fuzz projects (Docker images and Fuzzer compilation).
Uses multiprocessing.Pool to distribute projects across multiple CPU cores for concurrent processing.

Usage: python3 build_oss_fuzz.py [project_list_file] [--sanitizer type] [--workers N]
Example: python3 fuzz/build_oss_fuzz.py data/valid_projects.txt \
--sanitizer address \
--workers 8
"""

import os
import sys
import subprocess
import argparse
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Tuple
from multiprocessing import Pool, cpu_count

# --- Global configuration ---
HOME_DIR = Path.home()
OSS_FUZZ_DIR = HOME_DIR / "FuzzAug" / "fuzz" / "oss-fuzz"
LOG_DIR = OSS_FUZZ_DIR / "build_logs"

def setup_logging(project_name: str) -> Path:
"""Create a timestamped log file for a single project"""
LOG_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
return LOG_DIR / f"build_{project_name}_{timestamp}.log"

def log_and_print(message: str, log_file: Path, to_stdout: bool = True):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the logging module, see examples in other files.

"""Write message to log and print to console"""
if to_stdout:
print(f"[PID:{os.getpid()}] {message}")
with open(log_file, "a", encoding="utf-8") as f:
f.write(f"{datetime.now().isoformat()} {message}\n")

def run_command(
cmd: str,
log_msg: str,
log_file: Path,
allowed_exit_codes: Optional[List[int]] = None
) -> bool:
"""Execute a shell command and stream output to log in real-time"""
allowed_exit_codes = allowed_exit_codes or []
log_and_print(f"▶️ {log_msg}...", log_file, to_stdout=False)
log_and_print(f" $ {cmd}", log_file, to_stdout=False)

try:
process = subprocess.Popen(
f"yes | {cmd}", # Auto-confirm all prompts
shell=True,
stdout=subprocess.PIPE,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't really need to keep the build log for our experiments.

stderr=subprocess.STDOUT,
text=True,
encoding="utf-8",
errors="replace"
)
with open(log_file, "a", encoding="utf-8") as f:
if process.stdout is not None:
for line in iter(process.stdout.readline, ""):
f.write(line)
else:
log_and_print("⚠️ Warning: process.stdout is None", log_file)
process.wait()
exit_code = process.returncode
if exit_code in [0, *allowed_exit_codes]:
log_and_print(f"✅ Command completed successfully", log_file, to_stdout=False)
return True
log_and_print(f"❌ Command failed (exit code: {exit_code})", log_file)
return False
except Exception as e:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When catching exceptions, please be as precise as possible (i.e. know what may fail).

log_and_print(f"💥 Execution exception: {e}", log_file)
return False

def build_project(project_name: str, sanitizer: str) -> Tuple[bool, str]:
"""Build workflow for a single project"""
log_file = setup_logging(project_name)
os.chdir(OSS_FUZZ_DIR)

log_and_print("="*60, log_file)
log_and_print(f"🔨 Starting build for project: {project_name}", log_file)
log_and_print(f"📝 Log path: {log_file}", log_file)
log_and_print("="*60, log_file)

# 1. Build Docker image
if not run_command(
f"python3 infra/helper.py build_image {project_name}",
"Step 1/2: Building Docker image",
log_file
):
return (False, project_name)

# 2. Compile Fuzzers
if not run_command(
f"python3 infra/helper.py build_fuzzers --sanitizer {sanitizer} {project_name}",
f"Step 2/2: Compiling Fuzzers (sanitizer={sanitizer})",
log_file
):
return (False, project_name)

log_and_print(f"✅ Project {project_name} build completed", log_file)
return (True, project_name)

def main():
parser = argparse.ArgumentParser(description="OSS-Fuzz Parallel Build Tool")
parser.add_argument("project_list", help="Project list file path")
parser.add_argument("--sanitizer", default="address", choices=["address", "memory", "undefined"])
parser.add_argument("--workers", type=int, default=cpu_count())
args = parser.parse_args()

# Read project list
try:
with open(args.project_list, "r") as f:
projects = [line.strip() for line in f if line.strip()]
except Exception as e:
print(f"❌ Failed to read project list: {e}")
sys.exit(1)

# Parallel build
with Pool(args.workers) as pool:
results = pool.starmap(build_project, [(p, args.sanitizer) for p in projects])

# Output results
failed = [p for success, p in results if not success]
print(f"\n📊 Build completed: Success {len(projects)-len(failed)}/{len(projects)}")
if failed:
print("❌ Failed projects: " + ", ".join(failed))

if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions fuzz/oss-fuzz
Submodule oss-fuzz added at f73b40
143 changes: 143 additions & 0 deletions fuzz/run_fuzz_target.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
run_fuzz_target.py

Run OSS-Fuzz test targets in parallel.
Uses multiprocessing.Pool to distribute tasks to multiple CPU cores.

Usage: python3 run_fuzz_target.py [project_list_file] [--timeout seconds] [--workers N]
Example: python3 fuzz/run_fuzz_target.py data/valid_projects.txt --timeout 60 --workers 4
"""

import os
import sys
import subprocess
import argparse
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Tuple
from multiprocessing import Pool, cpu_count

# --- Global configuration ---
HOME_DIR = Path.home()
OSS_FUZZ_DIR = HOME_DIR / "FuzzAug" / "fuzz" / "oss-fuzz"
LOG_DIR = OSS_FUZZ_DIR / "run_logs"

def setup_logging(project_name: str) -> Path:
"""Create a timestamped run log"""
LOG_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
return LOG_DIR / f"run_{project_name}_{timestamp}.log"

def log_and_print(message: str, log_file: Path, to_stdout: bool = True):
"""Log and console output"""
if to_stdout:
print(f"[PID:{os.getpid()}] {message}")
with open(log_file, "a", encoding="utf-8") as f:
f.write(f"{datetime.now().isoformat()} {message}\n")

def run_command(
cmd: str,
log_msg: str,
log_file: Path,
allowed_exit_codes: Optional[List[int]] = None
) -> bool:
"""Execute command and log output in real-time"""
allowed_exit_codes = allowed_exit_codes or []
log_and_print(f"▶️ {log_msg}...", log_file, to_stdout=False)
log_and_print(f" $ {cmd}", log_file, to_stdout=False)

try:
process = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding="utf-8",
errors="replace"
)
with open(log_file, "a", encoding="utf-8") as f:
if process.stdout is not None:
for line in iter(process.stdout.readline, ""):
f.write(line)
else:
log_and_print("⚠️ Warning: process.stdout is None", log_file)
process.wait()
return process.returncode in [0, *allowed_exit_codes]
except Exception as e:
log_and_print(f"💥 Execution exception: {e}", log_file)
return False

def discover_targets(project_name: str) -> List[str]:
"""Discover available Fuzz targets"""
out_dir = OSS_FUZZ_DIR / "build" / "out" / project_name
targets = []
if out_dir.exists():
for f in out_dir.iterdir():
if f.is_file() and f.name.startswith("fuzz_") and os.access(f, os.X_OK):
targets.append(f.name)
return targets

def run_project(project_name: str, timeout: int) -> Tuple[bool, str]:
"""Testing workflow for a single project"""
log_file = setup_logging(project_name)
os.chdir(OSS_FUZZ_DIR)

log_and_print("="*60, log_file)
log_and_print(f"🚀 Starting testing for project: {project_name}", log_file)
log_and_print(f"📝 Log path: {log_file}", log_file)
log_and_print("="*60, log_file)

# 1. Discover test targets
targets = discover_targets(project_name)
if not targets:
log_and_print("⚠️ No test targets found", log_file)
return (False, project_name)
log_and_print(f"🔍 Discovered {len(targets)} test targets", log_file)

# 2. Run all targets
all_success = True
for i, target in enumerate(targets, 1):
cmd = f"python3 infra/helper.py run_fuzzer {project_name} {target} -- -max_total_time={timeout}"
success = run_command(
cmd,
f"Running target [{i}/{len(targets)}] {target} (timeout={timeout}s)",
log_file,
allowed_exit_codes=[1, 124] # Allow timeout exit codes
)
all_success &= success

# 3. Generate report (placeholder)
log_and_print("📊 Coverage report generation (not implemented in current version)", log_file)
return (all_success, project_name)

def main():
parser = argparse.ArgumentParser(description="OSS-Fuzz Parallel Testing Tool")
parser.add_argument("project_list", help="Project list file path")
parser.add_argument("--timeout", type=int, default=60, help="Timeout per target test (seconds)")
parser.add_argument("--workers", type=int, default=cpu_count())
args = parser.parse_args()

# Read project list
try:
with open(args.project_list) as f:
projects = [line.strip() for line in f if line.strip()]
except Exception as e:
print(f"❌ Failed to read project list: {e}")
sys.exit(1)

# Parallel execution
with Pool(args.workers) as pool:
results = pool.starmap(run_project, [(p, args.timeout) for p in projects])

# Output results
failed = [p for success, p in results if not success]
print(f"\n📊 Testing completed: Success {len(projects)-len(failed)}/{len(projects)}")
if failed:
print("❌ Failed projects: " + ", ".join(failed))

if __name__ == "__main__":
main()
Loading