-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
42a68fc
commit 170e127
Showing
15 changed files
with
1,545 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,3 +20,10 @@ Cargo.lock | |
build/ | ||
fusesoc* | ||
output | ||
|
||
chipyard | ||
build.err | ||
build.log | ||
|
||
# Mac files | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
[package] | ||
name = "chipyard_vcs_fuzzer" | ||
version = "0.0.1" | ||
edition = "2021" | ||
authors = ["Mohamadreza Rostami <[email protected]>"] | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[profile.release] | ||
panic = "abort" | ||
lto = true | ||
codegen-units = 1 | ||
opt-level = 3 | ||
debug = false | ||
|
||
[profile.dev] | ||
opt-level = 0 | ||
strip = "debuginfo" | ||
lto = false | ||
debug = true | ||
panic = "unwind" | ||
|
||
[dependencies] | ||
libpresifuzz_riscv = { path = "../../libpresifuzz_riscv"} | ||
libpresifuzz_ec = { path = "../../libpresifuzz_ec"} | ||
libpresifuzz_mutators = {path="../../libpresifuzz_mutators"} | ||
libpresifuzz_observers = { path = "../../libpresifuzz_observers"} | ||
libpresifuzz_feedbacks = { path = "../../libpresifuzz_feedbacks"} | ||
libpresifuzz_stages = { path = "../../libpresifuzz_stages"} | ||
libafl = { version = "0.11.2"} | ||
libafl_bolts = { version = "0.11.2"} | ||
yaml-rust = "0.4.5" | ||
rand = "0.8.5" | ||
serde_yaml = "0.9.27" | ||
tempdir = "0.3.7" | ||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib | ||
clap = { version = "3.2", features = ["default"] } | ||
fs_extra = "1.2.0" | ||
wait-timeout = "0.1.5" | ||
color-print = "0.3.6" | ||
|
||
[features] | ||
debug = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# Fuzzer Overview | ||
|
||
This documentation outlines the process of setting up and using an RTL fuzzer for the Chipyard framework using libAFL and PreSiFuzz. | ||
This setup demonstrates feedback-guided fuzzing using hardware code coverage reported by a commercial simulator. | ||
|
||
# Prerequisites | ||
|
||
The following tools need to be installed on your own. | ||
|
||
* Latest version of [Spike](https://github.com/riscv-software-src/riscv-isa-sim), the RISC-V ISA simulator, installed. | ||
Follow the installation instructions from the Spike GitHub repository. | ||
|
||
* Synopsys VCS (Verilog Compiler Simulator) installed and properly initialized. VCS is a widely used Verilog simulator. | ||
Ensure it is configured and ready for simulation tasks. | ||
|
||
* [RISC-V GNU Compiler Toolchain] (https://github.com/riscv-collab/riscv-gnu-toolchain) | ||
|
||
## Building | ||
|
||
The build.rs script performs the following tasks: | ||
|
||
* Initially, it downloads the `Chipyard` mainstream, initializes submodules, and applies the `chipyard_cov.patch` patch to support coverage collection. | ||
* Next, build the default `Rocketchip` for VCS based simulation. | ||
* Finally, it compiles the `./src/testcase.S` file and builds the simv self-contained simulator. Generated files are then copied into the `build` folder. | ||
|
||
To complete the steps above, simply run: | ||
```sh | ||
$ cargo build | ||
``` | ||
## Troubleshooting | ||
|
||
|
||
## Running the fuzzer | ||
|
||
When starting, the fuzzer creates a work directory where it saves intermediates files such as mutants, and symbolic links to the `simv` and its dependencies in `./build`. | ||
Work directory are saved into `TMPDIR` with a unique directory per fuzzer instance. Naming follows `presifuzz_<id>`. | ||
Synchronization information are saved into the `sync` directory, it includes `testcase` and associated `coverage map`. | ||
|
||
``` | ||
$ cp ../../target/debug/chipyard_vcs_fuzzer . | ||
$ mkdir sync | ||
``` | ||
|
||
To run a single fuzzer instance: | ||
``` | ||
$ AFL_LAUNCHER_CLIENT=1 ./chipyard_vcs_fuzzer | ||
``` | ||
|
||
To run multiple fuzzer instances: | ||
``` | ||
for i in {1..10}; do AFL_LAUNCHER_CLIENT=$i ./chipyard_vcs_fuzzer & done | ||
``` | ||
|
||
# Customizing | ||
|
||
The fuzzer is bootstraped using the seed files into the `seeds` folder. Feel free to customize the content of this file with any interesting seed. | ||
When starting the fuzzer loads the initial inputs (i.e., the seeds), and only keep interesting ones in the corpus (i.e., coverage novelty). | ||
Coverage novelty consider any changes for all supported code coverage metrics on vcs, i.e., branch, conditional, line, toggle, and FSM. | ||
Then, starts the fuzzer loop that iteratively calls the different stages. | ||
StdMutationalStage is responsible for generating new mutant by applying mutation to the existing testcase in the corpus. | ||
The mutations work at the ISA level by first deserializing the binary testcase into stream of instruction, then different mutations might be applied (e.g., adding instruction, removing instruction, changing opcode, ..). | ||
The mutation can easily be customized by changing `../../libpresifuzz_mutators/src/riscv_isa.rs`. | ||
The generated testcase is then inserted into a template ELF file by simplify injecting the code after the `payload` label. | ||
This template contains epilogue and prologue code. | ||
The current version is very simple. We first init registers to some known values, and we change the `mtvec` to points to our own trap handler. | ||
The trap handler is there to stop earlier the testcase execution if we trop too often. Otherwise, it always try to return to the instruction after the failing one. | ||
This version is a naive implementation, better performance could be achieved with some changes on the testharness (e.g., early simulation stop, irq support). | ||
|
||
|
||
# Ploting data | ||
|
||
The fuzzer saves statistics into the `sync`directory. | ||
It is possible to plot coverage over time using the `plot.py`: | ||
|
||
```python | ||
python3 ./plot.py -m branch -d ./sync | ||
``` | ||
|
||
The `-m` option is there to provide the coverage metric that is either tgl, cond, branch, line, fsm. | ||
The `-d` points to the directory where stats are saved. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// SPDX-FileCopyrightText: 2022 Intel Corporation | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use std::path::{Path}; | ||
use std::env; | ||
use std::path::PathBuf; | ||
use std::process::{Command, Stdio}; | ||
use std::fs; | ||
use std::fs::File; | ||
|
||
fn main() { | ||
println!("cargo:warning=MESSAGE"); | ||
|
||
if Path::new("./build").is_dir() { | ||
assert!(fs::remove_dir_all("./build").is_ok()); | ||
} | ||
assert!(fs::create_dir("./build").is_ok()); | ||
|
||
let build_exec_log = File::create("build.log").expect("Failed to build.log"); | ||
let build_exec_err = File::create("build.err").expect("Failed to build.err"); | ||
|
||
let rtl_dir = PathBuf::from("chipyard"); | ||
if !rtl_dir.exists() { | ||
println!("INFO: Builing chipyard."); | ||
|
||
assert!(Command::new("bash") | ||
.arg("build.sh") | ||
.stdin(Stdio::null()) | ||
.stdout(build_exec_log) | ||
.stderr(build_exec_err) | ||
.status() | ||
.unwrap() | ||
.success()) | ||
} | ||
|
||
println!("INFO: Creating build dir."); | ||
|
||
assert!(Command::new("bash") | ||
.arg("-c") | ||
.arg("cp -r ./chipyard/sims/vcs/* ./build") | ||
.status() | ||
.unwrap() | ||
.success()); | ||
|
||
let key = "VERDI_HOME"; | ||
let mut verdi_lib = match env::var(key) { | ||
Ok(val) => val, | ||
Err(_e) => "".to_string(), | ||
}; | ||
|
||
if verdi_lib.is_empty() { | ||
println!("The env variable 'VERDI_HOME' is not set"); | ||
return; | ||
} | ||
|
||
verdi_lib.push_str("/share/NPI/lib/linux64"); | ||
|
||
println!("cargo:rustc-link-search=native=./build"); | ||
println!("cargo:rustc-link-search=native={}", verdi_lib); | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#!/bin/bash | ||
set -e | ||
|
||
conda install -n base conda-libmamba-solver | ||
conda config --set solver libmamba | ||
conda install -n base conda-lock==1.4.0 | ||
conda activate base | ||
if [ ! -d "chipyard" ] ; then | ||
git clone https://github.com/ucb-bar/chipyard.git | ||
fi | ||
cd chipyard | ||
git checkout 1.11.0 | ||
./build-setup.sh riscv-tools | ||
source ./env.sh | ||
cd sims/vcs/ | ||
git apply ../chipyard_cov.patch | ||
make -j 12 | ||
|
||
echo "Build testcase" | ||
RISCV_BENCHMARKS="./toolchains/riscv-tools/riscv-tests/benchmarks" | ||
riscv64-unknown-linux-gnu-gcc -I $RISCV_BENCHMARKS/../env -I $RISCV_BENCHMARKS/common -I./testcase -DPREALLOCATE=1 -mcmodel=medany -static -std=gnu99 -O2 -ffast-math -fno-common -fno-builtin-printf -fno-tree-loop-distribute-patterns -o ../build/iram.elf $RISCV_BENCHMARKS/common/crt.S ../src/testcase.S -static -nostdlib -nostartfiles -lm -lgcc -T $RISCV_BENCHMARKS/common/test.ld |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
diff --git a/sims/vcs/Makefile b/sims/vcs/Makefile | ||
index 8517fc1d..f942a17b 100644 | ||
--- a/sims/vcs/Makefile | ||
+++ b/sims/vcs/Makefile | ||
@@ -69,7 +69,7 @@ model_dir_debug = $(build_dir)/$(long_name).debug | ||
######################################################################################### | ||
$(sim): $(sim_common_files) $(dramsim_lib) $(EXTRA_SIM_REQS) | ||
rm -rf $(model_dir) | ||
- $(VCS) $(VCS_OPTS) $(EXTRA_SIM_SOURCES) -o $@ -Mdir=$(model_dir) | ||
+ $(VCS) $(VCS_OPTS) $(EXTRA_SIM_SOURCES) -o $@ -Mdir=$(model_dir) -lca -cm line+cond+fsm+tgl+path+branch+assert -cm_dir Coverage.vdb | ||
|
||
$(sim_debug): $(sim_common_files) $(dramsim_lib) $(EXTRA_SIM_REQS) | ||
rm -rf $(model_dir_debug) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
fuzzer: | ||
max_testcase_size: 128 | ||
simv: | ||
vcs_args: | ||
plus_args: | ||
cov_enable: true | ||
coverage_metrics: "line+fsm+cond+tgl+branch" | ||
coverage_directory: "Coverage.vdb" | ||
reset_coverage_before_use: false | ||
system_timeout_s: 180 | ||
vcs_timeout: "10us" | ||
multi_seed: 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
|
||
# SPDX-FileCopyrightText: 2022 Intel Corporation | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import os | ||
import re | ||
import time | ||
import copy | ||
from datetime import datetime | ||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
from collections import OrderedDict | ||
import pandas | ||
import seaborn as sns | ||
import json | ||
import argparse | ||
|
||
print(sns.__version__) | ||
|
||
|
||
def is_valid_directory(arg): | ||
if not os.path.isdir(arg): | ||
raise argparse.ArgumentTypeError(f"'{arg}' is not a valid directory") | ||
return arg | ||
|
||
def parse_args(): | ||
parser = argparse.ArgumentParser(description="Parse command line arguments") | ||
parser.add_argument("-d", "--directory", type=is_valid_directory, required=True, help="Input directory") | ||
parser.add_argument("-m", "--metric", choices=["line", "branch", "cond", "tgl", "fsm"], required=True, help="Metric to be provided") | ||
return parser.parse_args() | ||
|
||
args = parse_args() | ||
print("Input directory:", args.directory) | ||
print("Metric:", args.metric) | ||
|
||
stats_directories = [args.directory] | ||
metric = args.metric | ||
|
||
# first, let's load all the data | ||
data = [] | ||
for stats_directory in stats_directories: | ||
|
||
i = 0 | ||
delta = 0 | ||
for stats_filename in os.scandir(stats_directory): | ||
|
||
f = stats_filename | ||
|
||
if "stats" not in f.name: | ||
continue | ||
|
||
|
||
print(f"Analyzing file {f.name}: ") | ||
|
||
if os.path.isfile(f): | ||
f = open(f) | ||
|
||
last_runtime = 0 | ||
last_coverage = 0 | ||
|
||
lines = f.readlines() | ||
for l in lines: | ||
|
||
l = json.loads(l) | ||
|
||
if "coverage_verdi_"+metric in l["UpdateUserStats"]["name"]: | ||
a = l["UpdateUserStats"]["value"]["value"]["Ratio"][0] | ||
b = l["UpdateUserStats"]["value"]["value"]["Ratio"][1] | ||
last_coverage = a/b | ||
|
||
if "time_verdi_"+metric in l["UpdateUserStats"]["name"]: | ||
last_runtime = l["UpdateUserStats"]["value"]["value"]["Number"] | ||
data += [{"runtime": last_runtime, "score": last_coverage}] | ||
i += 1 | ||
|
||
# let's order the timepoint | ||
dataset = [] | ||
runtime = [] | ||
coverage = [] | ||
max_cov = 0.0 | ||
|
||
time_sorted = sorted(data, key=lambda x: x['runtime']) | ||
|
||
delta = time_sorted[0]["runtime"] | ||
|
||
for item in time_sorted: | ||
|
||
runtime += [item["runtime"]] | ||
|
||
if max_cov < item["score"]: | ||
max_cov = item["score"] | ||
|
||
coverage += [max_cov] | ||
|
||
dataset = {"Execution Time": runtime, "Score": coverage} | ||
print(dataset) | ||
ax = sns.lineplot(x=dataset["Execution Time"], y=dataset["Score"], legend="full") | ||
|
||
plt.title(f"{metric} coverage score over time.") | ||
plt.legend(loc='upper center') | ||
plt.savefig(f'{metric}.png') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
��l��(}����#�69����I ��&W[��NÔr0 $绿��9e��x��A�dqJ��y��w |
Oops, something went wrong.