Skip to content

Commit

Permalink
Arbitrary-compatible expr generation and host fuzzing (#1171)
Browse files Browse the repository at this point in the history
### What

#1136 

### Why

[TODO: Why this change is being made. Include any context required to
understand the why.]

### Known limitations

[TODO or N/A]

---------

Co-authored-by: Graydon Hoare <graydon@pobox.com>
  • Loading branch information
jayz22 and graydon authored Nov 4, 2023
1 parent dce34f9 commit 4a7a6ad
Show file tree
Hide file tree
Showing 23 changed files with 2,019 additions and 79 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions cackle.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@ allow_apis = [
]

[pkg.rand]
allow_apis = [
"thread",
]
allow_unsafe = true

[pkg.backtrace]
Expand Down Expand Up @@ -384,3 +381,9 @@ allow_unsafe = true

[pkg.soroban-bench-utils]
allow_unsafe = true

[pkg.derive_arbitrary]
allow_proc_macro = true

[pkg.arbitrary]
allow_unsafe = true
49 changes: 46 additions & 3 deletions soroban-env-common/src/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,57 @@
extern crate alloc;

use crate::xdr::ScError;
use crate::Error;
use crate::symbol::MAX_SMALL_CHARS;
use crate::xdr::{ScError, ScErrorCode};
use crate::{Error, StorageType, Symbol, SymbolSmall, Val, Void};
use arbitrary::{Arbitrary, Unstructured};

impl<'a> Arbitrary<'a> for Error {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let scerror = ScError::arbitrary(u)?;
let error = Error::from(scerror);
Ok(error)
// FIXME: fuzzer discovered that it can just return "InternalError" from
// a contract to make the host think it had an InternalError. See
// https://github.com/stellar/rs-soroban-env/issues/1175
if error.is_code(ScErrorCode::InternalError) {
Err(arbitrary::Error::IncorrectFormat)
} else {
Ok(error)
}
}
}

impl<'a> Arbitrary<'a> for Void {
fn arbitrary(_u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Val::VOID)
}
}

impl<'a> Arbitrary<'a> for Symbol {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let len: usize = u.int_in_range(0..=MAX_SMALL_CHARS)?;
let mut buf = [0u8; MAX_SMALL_CHARS];
for i in 0..len {
buf[i] = (*u.choose(&[
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_',
])?) as u8;
}
let small =
SymbolSmall::try_from(&buf[0..len]).map_err(|_| arbitrary::Error::IncorrectFormat)?;
Ok(small.into())
}
}

impl<'a> Arbitrary<'a> for StorageType {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
u.choose(&[
StorageType::Instance,
StorageType::Persistent,
StorageType::Temporary,
])
.map(|x| *x)
}
}
2 changes: 1 addition & 1 deletion soroban-env-common/src/storage_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use num_derive::FromPrimitive;
/// an argument to storage functions. It doesn't correspond to any [`Val`] types,
/// and is passed by direct marshalling as a u64.
#[repr(u64)]
#[derive(Debug, FromPrimitive, PartialEq, Eq, Clone)]
#[derive(Debug, FromPrimitive, PartialEq, Eq, Clone, Copy)]
pub enum StorageType {
Temporary = 0,
Persistent = 1,
Expand Down
2 changes: 1 addition & 1 deletion soroban-env-host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ linregress = "0.5.1"
pretty_assertions = "1.4.0"

[features]
testutils = ["soroban-env-common/testutils", "recording_auth"]
testutils = ["soroban-env-common/testutils", "soroban-synth-wasm/testutils", "recording_auth"]
next = ["soroban-env-common/next", "soroban-test-wasms/next", "soroban-synth-wasm/next", "soroban-bench-utils/next"]
tracy = ["dep:tracy-client", "soroban-env-common/tracy"]
recording_auth = []
Expand Down
6 changes: 3 additions & 3 deletions soroban-env-host/benches/common/cost_types/wasm_insn_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ fn br_table_nested(n: u64, _rng: &mut StdRng) -> WasmModule {

fn local_get(n: u64, _rng: &mut StdRng) -> WasmModule {
let mut fe = ModEmitter::new().func(Arity(0), 1);
let s = fe.locals[0];
let s = fe.locals[0].0;
for _i in 0..n {
fe.local_get(s);
}
Expand All @@ -164,7 +164,7 @@ fn local_get(n: u64, _rng: &mut StdRng) -> WasmModule {

fn local_set(n: u64, _rng: &mut StdRng) -> WasmModule {
let mut fe = ModEmitter::new().func(Arity(0), 1);
let s = fe.locals[0];
let s = fe.locals[0].0;
for i in 0..n {
fe.i64_const(i as i64);
fe.local_set(s);
Expand All @@ -177,7 +177,7 @@ fn local_set(n: u64, _rng: &mut StdRng) -> WasmModule {

fn local_tee(n: u64, _rng: &mut StdRng) -> WasmModule {
let mut fe = ModEmitter::new().func(Arity(0), 1);
let s = fe.locals[0];
let s = fe.locals[0].0;
for i in 0..n {
fe.i64_const(i as i64);
fe.local_tee(s);
Expand Down
4 changes: 4 additions & 0 deletions soroban-env-host/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
Loading

0 comments on commit 4a7a6ad

Please sign in to comment.