Skip to content

Commit d8fe32e

Browse files
svm: integrate Firedancer's fuzz harness for instructions (solana-labs#8047)
* init fixture lib * init instr lib * hook up bin * fix ci * expose entrypoints into single .so file (#33) * expose entrypoints into single .so file * test * fix * fix submodule * fix submodule path * remove bad file * whitespace * update manifests * clean up directories * remove outdated submodule (#34) * clean up directories * update submodules * update script * cleaned up feature comment * cleanups to fuzz harness instrcontext and sysvar cache setup (solana-labs#35) * remove `rlib` target * Revert "remove `rlib` target" This reverts commit 1543c01. * fix sysvars in test * configure instruction changes from rebase * rebase updates --------- Co-authored-by: mjain-jump <[email protected]>
1 parent e5dabe6 commit d8fe32e

File tree

22 files changed

+1072
-1
lines changed

22 files changed

+1072
-1
lines changed

.github/workflows/cargo.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ jobs:
5555
apk add bash git
5656
5757
- uses: actions/checkout@v5
58+
with:
59+
submodules: recursive
5860

5961
- uses: mozilla-actions/[email protected]
6062
with:

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "svm-fuzz-harness/protosol"]
2+
path = svm-fuzz-harness/protosol
3+
url = https://github.com/firedancer-io/protosol.git

Cargo.lock

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ members = [
104104
"svm",
105105
"svm-callback",
106106
"svm-feature-set",
107+
"svm-fuzz-harness",
107108
"svm-log-collector",
108109
"svm-measure",
109110
"svm-timings",
@@ -529,6 +530,7 @@ solana-streamer = { path = "streamer", version = "=3.1.0" }
529530
solana-svm = { path = "svm", version = "=3.1.0" }
530531
solana-svm-callback = { path = "svm-callback", version = "=3.1.0" }
531532
solana-svm-feature-set = { path = "svm-feature-set", version = "=3.1.0" }
533+
solana-svm-fuzz-harness = { path = "svm-fuzz-harness", version = "=3.1.0" }
532534
solana-svm-log-collector = { path = "svm-log-collector", version = "=3.1.0" }
533535
solana-svm-measure = { path = "svm-measure", version = "=3.1.0" }
534536
solana-svm-timings = { path = "svm-timings", version = "=3.1.0" }

programs/bpf_loader/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1550,7 +1550,14 @@ fn execute<'a, 'b: 'a>(
15501550
Err(Box::new(error) as Box<dyn std::error::Error>)
15511551
}
15521552
ProgramResult::Err(mut error) => {
1553-
if !matches!(error, EbpfError::SyscallError(_)) {
1553+
// Don't clean me up!!
1554+
// This feature is active on all networks, but we still toggle
1555+
// it off during fuzzing.
1556+
if invoke_context
1557+
.get_feature_set()
1558+
.deplete_cu_meter_on_vm_failure
1559+
&& !matches!(error, EbpfError::SyscallError(_))
1560+
{
15541561
// when an exception is thrown during the execution of a
15551562
// Basic Block (e.g., a null memory dereference or other
15561563
// faults), determining the exact number of CUs consumed

svm-fuzz-harness/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dump

svm-fuzz-harness/Cargo.toml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
[package]
2+
name = "solana-svm-fuzz-harness"
3+
description = "Solana SVM fuzzing harnesses."
4+
documentation = "https://docs.rs/solana-svm-fuzz-harness"
5+
version = { workspace = true }
6+
authors = { workspace = true }
7+
repository = { workspace = true }
8+
homepage = { workspace = true }
9+
license = { workspace = true }
10+
edition = { workspace = true }
11+
publish = false
12+
13+
[lib]
14+
crate-type = ["cdylib", "rlib"]
15+
16+
[[bin]]
17+
name = "test_exec_instr"
18+
path = "bin/test_exec_instr.rs"
19+
20+
[dependencies]
21+
agave-feature-set = { workspace = true }
22+
agave-precompiles = { workspace = true }
23+
agave-syscalls = { workspace = true }
24+
bincode = { workspace = true }
25+
clap = { version = "4.5.2", features = ["derive"] }
26+
prost = { workspace = true }
27+
solana-account = { workspace = true }
28+
solana-builtins = { workspace = true }
29+
solana-clock = { workspace = true, features = ["sysvar"] }
30+
solana-compute-budget = { workspace = true }
31+
solana-epoch-schedule = { workspace = true, features = ["sysvar"] }
32+
solana-hash = { workspace = true }
33+
solana-instruction = { workspace = true }
34+
solana-instruction-error = { workspace = true, features = ["serde"] }
35+
solana-last-restart-slot = { workspace = true, features = ["sysvar"] }
36+
solana-logger = { workspace = true }
37+
solana-precompile-error = { workspace = true }
38+
solana-program-runtime = { workspace = true }
39+
solana-pubkey = { workspace = true }
40+
solana-rent = { workspace = true, features = ["sysvar"] }
41+
solana-sdk-ids = { workspace = true }
42+
solana-stable-layout = { workspace = true }
43+
solana-svm = { workspace = true }
44+
solana-svm-callback = { workspace = true }
45+
solana-svm-log-collector = { workspace = true }
46+
solana-svm-timings = { workspace = true }
47+
solana-sysvar-id = { workspace = true }
48+
solana-transaction-context = { workspace = true }
49+
thiserror = { workspace = true }
50+
51+
[build-dependencies]
52+
prost-build = { workspace = true }
53+
54+
[lints]
55+
workspace = true

svm-fuzz-harness/Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
CARGO?=cargo
3+
4+
test: | binaries tests/run_test_vectors
5+
6+
binaries:
7+
$(CARGO) build --manifest-path ./Cargo.toml --bins --release
8+
9+
tests/run_test_vectors:
10+
scripts/run_test_vectors.sh
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use {
2+
clap::Parser,
3+
prost::Message,
4+
solana_svm_fuzz_harness::{
5+
fixture::proto::InstrFixture as ProtoInstrFixture, instr::execute_instr_proto,
6+
},
7+
std::path::PathBuf,
8+
};
9+
10+
#[derive(Parser)]
11+
#[command(version, about, long_about = None)]
12+
struct Cli {
13+
inputs: Vec<PathBuf>,
14+
}
15+
16+
fn exec(input: &PathBuf) -> bool {
17+
let blob = std::fs::read(input).unwrap();
18+
let fixture = ProtoInstrFixture::decode(&blob[..]).unwrap();
19+
let Some(context) = fixture.input else {
20+
println!("No context found.");
21+
return false;
22+
};
23+
24+
let Some(expected) = fixture.output else {
25+
println!("No fixture found.");
26+
return false;
27+
};
28+
let Some(effects) = execute_instr_proto(context) else {
29+
println!("FAIL: No instruction effects returned for input: {input:?}",);
30+
return false;
31+
};
32+
33+
let ok = effects == expected;
34+
35+
if ok {
36+
println!("OK: {input:?}");
37+
} else {
38+
println!("FAIL: {input:?}");
39+
}
40+
ok
41+
}
42+
43+
fn main() {
44+
let cli = Cli::parse();
45+
let mut fail_cnt: i32 = 0;
46+
for input in cli.inputs {
47+
if !exec(&input) {
48+
fail_cnt = fail_cnt.saturating_add(1);
49+
}
50+
}
51+
std::process::exit(fail_cnt);
52+
}

svm-fuzz-harness/build.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use std::io::Result;
2+
3+
fn main() -> Result<()> {
4+
let proto_base_path = std::path::PathBuf::from("protosol/proto");
5+
6+
let protos = &[
7+
proto_base_path.join("context.proto"),
8+
proto_base_path.join("invoke.proto"),
9+
];
10+
11+
protos
12+
.iter()
13+
.for_each(|proto| println!("cargo:rerun-if-changed={}", proto.display()));
14+
15+
prost_build::Config::new().compile_protos(protos, &[proto_base_path])?;
16+
17+
Ok(())
18+
}

0 commit comments

Comments
 (0)