Skip to content

Commit 07f2601

Browse files
committed
Replace bincode with postcard. Inline fuzz_mutate. Address other small comments
1 parent f3d6420 commit 07f2601

File tree

6 files changed

+39
-74
lines changed

6 files changed

+39
-74
lines changed

Cargo.lock

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

crates/fuzzing/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@ workspace = true
1515
wasmtime-test-util = { workspace = true, features = ['wast'] }
1616

1717
[dependencies]
18-
bincode = "2.0.1"
1918
anyhow = { workspace = true }
2019
arbitrary = { workspace = true, features = ["derive"] }
2120
env_logger = { workspace = true }
2221
log = { workspace = true }
2322
mutatis = { workspace = true }
24-
rand = { workspace = true }
2523
rayon = "1.2.1"
2624
smallvec = { workspace = true }
2725
target-lexicon = { workspace = true }
@@ -38,6 +36,7 @@ wasmi = { version = "0.43.1", default-features = false, features = ["std", "simd
3836
futures = { workspace = true }
3937
wasmtime-test-util = { workspace = true, features = ['wast', 'component-fuzz', 'component'] }
4038
serde_json = { workspace = true }
39+
serde = { workspace = true }
4140

4241
[dependencies.wasmtime-cli-flags]
4342
workspace = true

crates/fuzzing/src/generators/table_ops.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Generating series of `table.get` and `table.set` operations.
2-
use bincode::{Decode, Encode};
32
use mutatis::mutators as m;
43
use mutatis::{Candidates, Context, DefaultMutate, Generate, Mutate, Result as MutResult};
4+
use serde::{Deserialize, Serialize};
55
use smallvec::SmallVec;
66
use std::ops::RangeInclusive;
77
use wasm_encoder::{
@@ -12,7 +12,7 @@ use wasm_encoder::{
1212

1313
/// A description of a Wasm module that makes a series of `externref` table
1414
/// operations.
15-
#[derive(Debug, Default, Encode, Decode)]
15+
#[derive(Debug, Default, Serialize, Deserialize)]
1616
pub struct TableOps {
1717
pub(crate) num_params: u32,
1818
pub(crate) num_globals: u32,
@@ -183,7 +183,10 @@ impl TableOps {
183183

184184
self.ops = new_ops;
185185
}
186-
/// Pops from the vector of the opcodes and returns bool
186+
187+
/// Attempts to remove the last opcode from the sequence.
188+
///
189+
/// Returns `true` if an opcode was successfully removed, or `false` if the list was already empty.
187190
pub fn pop(&mut self) -> bool {
188191
self.ops.pop().is_some()
189192
}
@@ -288,7 +291,7 @@ macro_rules! define_table_ops {
288291
$op:ident $( ( $($limit:expr => $ty:ty),* ) )? : $params:expr => $results:expr ,
289292
)*
290293
) => {
291-
#[derive(Copy, Clone, Debug, Encode, Decode)]
294+
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
292295
pub(crate) enum TableOp {
293296
$(
294297
$op ( $( $($ty),* )? ),

crates/fuzzing/src/oracles.rs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ use self::diff_wasmtime::WasmtimeInstance;
2323
use self::engine::{DiffEngine, DiffInstance};
2424
use crate::generators::{self, CompilerStrategy, DiffValue, DiffValueType};
2525
use crate::single_module_fuzzer::KnownValid;
26-
use arbitrary::{Arbitrary, Unstructured};
27-
use rand::{Rng, SeedableRng};
26+
use arbitrary::Arbitrary;
2827
pub use stacks::check_stacks;
2928
use std::future::Future;
3029
use std::pin::Pin;
@@ -1332,22 +1331,6 @@ pub fn call_async(wasm: &[u8], config: &generators::Config, mut poll_amts: &[u32
13321331
}
13331332
}
13341333

1335-
/// Fuzz target for table operations that reconstructs config from seed.
1336-
pub fn fuzz_table_ops((config_seed, ops): (u64, generators::table_ops::TableOps)) {
1337-
let mut buf = [0u8; 1024];
1338-
let mut rng = rand::rngs::StdRng::seed_from_u64(config_seed);
1339-
1340-
for b in buf.iter_mut() {
1341-
*b = rng.r#gen()
1342-
}
1343-
1344-
let u = Unstructured::new(&buf);
1345-
let config = generators::Config::arbitrary_take_rest(u)
1346-
.expect("should be able to generate config from seed");
1347-
1348-
let _ = crate::oracles::table_ops(config, ops);
1349-
}
1350-
13511334
#[cfg(test)]
13521335
mod tests {
13531336
use super::*;

fuzz/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ cargo-fuzz = true
1313

1414
[dependencies]
1515
mutatis = { workspace = true }
16-
bincode = "2.0.1"
16+
rand = { workspace = true }
17+
postcard = { workspace = true }
1718
anyhow = { workspace = true }
1819
env_logger = { workspace = true }
1920
cranelift-assembler-x64 = { workspace = true, features = ["fuzz"] }

fuzz/fuzz_targets/table_ops.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,56 @@
11
#![no_main]
22

3+
use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured};
34
use libfuzzer_sys::{fuzz_mutator, fuzz_target, fuzzer_mutate};
45
use mutatis::Session;
6+
use postcard::{from_bytes, to_slice};
7+
use rand::{Rng, SeedableRng};
58
use wasmtime_fuzzing::generators::table_ops::TableOps;
6-
use wasmtime_fuzzing::oracles::fuzz_table_ops;
9+
use wasmtime_fuzzing::oracles::table_ops;
710

811
fuzz_target!(|input: (u64, TableOps)| {
9-
fuzz_table_ops(input);
12+
let (seed, ops) = input;
13+
let mut buf = [0u8; 1024];
14+
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
15+
for b in buf.iter_mut() {
16+
*b = rng.r#gen();
17+
}
18+
19+
let u = Unstructured::new(&buf);
20+
let config = wasmtime_fuzzing::generators::Config::arbitrary_take_rest(u)
21+
.expect("should be able to generate config from seed");
22+
23+
let _ = table_ops(config, ops);
1024
});
1125

1226
fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| {
1327
let _ = env_logger::try_init();
1428

15-
// With probability of about 1/8, just use the default mutator.
29+
// With probability of about 1/8, use default mutator
1630
if seed.count_ones() % 8 == 0 {
1731
return fuzzer_mutate(data, size, max_size);
1832
}
1933

20-
// Fallback to default on decode failure
21-
let mut tuple =
22-
bincode::decode_from_slice::<(u64, TableOps), _>(data, bincode::config::standard())
23-
.map_or_else(|_err| (0, TableOps::default()), |(tuple, _)| tuple);
34+
// Try to decode using postcard; fallback to default input on failure
35+
let mut tuple: (u64, TableOps) = from_bytes(&data[..size]).ok().unwrap_or_default();
2436

2537
let mut session = Session::new().seed(seed.into()).shrink(max_size < size);
2638

2739
if session.mutate(&mut tuple).is_ok() {
28-
// Re-encode the mutated ops back into `data`.
2940
loop {
30-
if let Ok(new_size) =
31-
bincode::encode_into_slice(&tuple, data, bincode::config::standard())
32-
{
33-
return new_size;
41+
if let Ok(encoded) = to_slice(&tuple, data) {
42+
return encoded.len();
3443
}
35-
// When re-encoding fails (presumably because `data` is not
36-
// large enough) then pop an op off the end and try again.
44+
45+
// Attempt to shrink ops if encoding fails (e.g., buffer too small)
3746
if tuple.1.pop() {
3847
continue;
3948
}
49+
4050
break;
4151
}
4252
}
43-
// Fall back to the fuzzer's default mutation strategies.
53+
54+
// Fallback to default libfuzzer mutator
4455
fuzzer_mutate(data, size, max_size)
4556
});

0 commit comments

Comments
 (0)