Browse Source

PCC: fuzz static (no dynamic checks) case in Wasmtime fuzzers. (#8142)

* Update PCC test to expose failure.

* Reduce test coverage only to fully-static (bounds-check-elided) case.

* Configure PCC when fuzzing

* Ensure that we panic for pcc errors in the instantiate fuzz target

* Adjust Wasmtime configuration generation: PCC forces static memory configuration.

* Properly force memory config when fuzzing PCC.

---------

Co-authored-by: Trevor Elliott <telliott@fastly.com>
pull/8146/head
Chris Fallin 8 months ago
committed by GitHub
parent
commit
acbbf342b7
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 3
      cranelift/codegen/src/result.rs
  2. 23
      crates/fuzzing/src/generators/config.rs
  3. 10
      crates/fuzzing/src/oracles.rs
  4. 127
      tests/pcc_memory.rs

3
cranelift/codegen/src/result.rs

@ -77,6 +77,9 @@ impl std::fmt::Display for CodegenError {
#[cfg(feature = "unwind")]
CodegenError::RegisterMappingError(_0) => write!(f, "Register mapping error"),
CodegenError::Regalloc(errors) => write!(f, "Regalloc validation errors: {:?}", errors),
// NOTE: if this is changed, please update the `is_pcc_error` function defined in
// `wasmtime/crates/fuzzing/src/oracles.rs`
CodegenError::Pcc(e) => write!(f, "Proof-carrying-code validation error: {:?}", e),
}
}

23
crates/fuzzing/src/generators/config.rs

@ -1,7 +1,8 @@
//! Generate a configuration for both Wasmtime and the Wasm module to execute.
use super::{
CodegenSettings, InstanceAllocationStrategy, MemoryConfig, ModuleConfig, UnalignedMemoryCreator,
CodegenSettings, InstanceAllocationStrategy, MemoryConfig, ModuleConfig, NormalMemoryConfig,
UnalignedMemoryCreator,
};
use crate::oracles::{StoreLimits, Timeout};
use anyhow::Result;
@ -215,6 +216,8 @@ impl Config {
);
}
}
cfg.cranelift_pcc(self.wasmtime.pcc);
}
// Vary the memory configuration, but only if threads are not enabled.
@ -228,7 +231,20 @@ impl Config {
// `CustomUnaligned` variant isn't actually safe to use with a shared
// memory.
if !self.module_config.config.threads_enabled {
match &self.wasmtime.memory_config {
// If PCC is enabled, force other options to be compatible: PCC is currently only
// supported when bounds checks are elided.
let memory_config = if self.wasmtime.pcc {
MemoryConfig::Normal(NormalMemoryConfig {
static_memory_maximum_size: Some(4 << 30), // 4 GiB
static_memory_guard_size: Some(2 << 30), // 2 GiB
dynamic_memory_guard_size: Some(0),
guard_before_linear_memory: false,
})
} else {
self.wasmtime.memory_config.clone()
};
match &memory_config {
MemoryConfig::Normal(memory_config) => {
cfg.static_memory_maximum_size(
memory_config.static_memory_maximum_size.unwrap_or(0),
@ -429,6 +445,9 @@ pub struct WasmtimeConfig {
native_unwind_info: bool,
/// Configuration for the compiler to use.
pub compiler_strategy: CompilerStrategy,
/// Whether or not fuzzing should enable PCC.
pcc: bool,
}
impl WasmtimeConfig {

10
crates/fuzzing/src/oracles.rs

@ -256,8 +256,18 @@ fn compile_module(
config: &generators::Config,
) -> Option<Module> {
log_wasm(bytes);
fn is_pcc_error(e: &anyhow::Error) -> bool {
// NOTE: please keep this predicate in sync with the display format of CodegenError,
// defined in `wasmtime/cranelift/codegen/src/result.rs`
e.to_string().to_lowercase().contains("proof-carrying-code")
}
match config.compile(engine, bytes) {
Ok(module) => Some(module),
Err(e) if is_pcc_error(&e) => {
panic!("pcc error in input: {e:#?}");
}
Err(_) if known_valid == KnownValid::No => None,
Err(e) => {
if let generators::InstanceAllocationStrategy::Pooling(c) = &config.wasmtime.strategy {

127
tests/pcc_memory.rs

@ -8,132 +8,44 @@ mod pcc_memory_tests {
const TESTS: &'static [&'static str] = &[
r#"
(module
(memory 1 1)
(func (param i32)
local.get 0
i32.load8_u
drop))
drop
"#,
r#"
(module
(memory 1 1)
(func (param i32)
local.get 0
i32.load8_u offset=0x10000
drop))
drop
"#,
r#"
(module
(memory 1 1)
(func (param i32)
local.get 0
i32.load16_u
drop))
drop
"#,
r#"
(module
(memory 1 1)
(func (param i32)
local.get 0
i32.load16_u offset=0x10000
drop))
drop
"#,
r#"
(module
(memory 1 1)
(func (param i32)
local.get 0
i32.load
drop))
drop
"#,
r#"
(module
(memory 1 1)
(func (param i32)
local.get 0
i32.load offset=0x10000
drop))
drop
"#,
r#"
(module
(memory 1 1)
(func (param i32)
local.get 0
i64.load
drop))
drop
"#,
r#"
(module
(memory 1 1)
(func (param i32)
local.get 0
i64.load offset=0x10000
drop))
"#,
r#"
(module
(memory 10 20)
(func (param i32)
local.get 0
i32.load8_u
drop))
"#,
r#"
(module
(memory 10 20)
(func (param i32)
local.get 0
i32.load8_u offset=0x10000
drop))
"#,
r#"
(module
(memory 10 20)
(func (param i32)
local.get 0
i32.load16_u
drop))
"#,
r#"
(module
(memory 10 20)
(func (param i32)
local.get 0
i32.load16_u offset=0x10000
drop))
"#,
r#"
(module
(memory 10 20)
(func (param i32)
local.get 0
i32.load
drop))
"#,
r#"
(module
(memory 10 20)
(func (param i32)
local.get 0
i32.load offset=0x10000
drop))
"#,
r#"
(module
(memory 10 20)
(func (param i32)
local.get 0
i64.load
drop))
"#,
r#"
(module
(memory 10 20)
(func (param i32)
local.get 0
i64.load offset=0x10000
drop))
drop
"#,
];
@ -145,9 +57,26 @@ mod pcc_memory_tests {
const MIB: u64 = 1024 * KIB;
const GIB: u64 = 1024 * MIB;
for &test in TESTS {
for static_memory_maximum_size in [0, 64 * KIB, 1 * MIB, 4 * GIB, 6 * GIB] {
for guard_size in [0, 64 * KIB, 2 * GIB] {
let mut bodies = vec![];
for (mem_min, mem_max) in [(1, 1), (10, 20)] {
for &snippet in TESTS {
bodies.push(format!(
"(module (memory {mem_min} {mem_max}) (func (param i32) {snippet}))"
));
}
let all_snippets = TESTS
.iter()
.map(|s| s.to_owned())
.collect::<Vec<_>>()
.join("\n");
bodies.push(format!(
"(module (memory {mem_min} {mem_max}) (func (param i32) {all_snippets}))"
));
}
for test in &bodies {
for static_memory_maximum_size in [4 * GIB] {
for guard_size in [2 * GIB] {
for enable_spectre in [true /* not yet supported by PCC: false */] {
for _memory_bits in [32 /* not yet supported by PCC: 64 */] {
log::trace!("test:\n{}\n", test);

Loading…
Cancel
Save