Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/install-rust/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ runs:
EOF

# Deny warnings on CI to keep our code warning-free as it lands in-tree.
echo RUSTFLAGS="-D warnings" >> "$GITHUB_ENV"
echo RUSTFLAGS="-D warnings $RUSTFLAGS" >> "$GITHUB_ENV"

- run: rustup target add wasm32-wasip2
if: inputs.toolchain != 'msrv'
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ jobs:
rust: stable
env:
RUST_BACKTRACE: 1
# test the 'try_op' feature
- os: ubuntu-latest
rust: stable
env:
RUSTFLAGS: --cfg=debug_check_try_op
flags: -F wasmparser/try-op
env: ${{ matrix.env || fromJSON('{}') }}
steps:
- uses: actions/checkout@v6
Expand All @@ -154,6 +160,7 @@ jobs:
- run: cargo test --locked -p wasm-encoder --all-features
- run: cargo test -p wasm-smith --features wasmparser
- run: cargo test -p wasm-smith --features component-model
- run: cargo test --locked -p wasmparser --features try-op

test_capi:
name: Test the C API
Expand Down Expand Up @@ -276,6 +283,7 @@ jobs:
- run: cargo check --no-default-features -p wasmparser --target x86_64-unknown-none --features validate,serde,prefer-btree-collections
- run: cargo check --no-default-features -p wasmparser --features std
- run: cargo check --no-default-features -p wasmparser --features validate
- run: cargo check --no-default-features -p wasmparser --features validate,try-op
- run: cargo check --no-default-features -p wasmparser --features features
- run: cargo check --no-default-features -p wasmparser --features features,validate
- run: cargo check --no-default-features -p wasmparser --features prefer-btree-collections
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ rust-2024-incompatible-pat = 'warn'
missing-unsafe-on-extern = 'warn'
unsafe-op-in-unsafe-fn = 'warn'

unexpected_cfgs = { level = 'warn', check-cfg = ['cfg(fuzzing)'] }
unexpected_cfgs = { level = 'warn', check-cfg = ['cfg(fuzzing)', 'cfg(debug_check_try_op)'] }

[workspace.lints.clippy]
# The default set of lints in Clippy is viewed as "too noisy" right now so
Expand Down
4 changes: 4 additions & 0 deletions crates/wasmparser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,7 @@ component-model = ['dep:semver']
# proposals for WebAssembly. This is enabled by default but if your use case is
# only interested in working on non-SIMD code then this feature can be disabled.
simd = []

# A feature that allows validating an operator atomically, leaving the validator
# unchanged if the operator is invalid.
try-op = []
2 changes: 1 addition & 1 deletion crates/wasmparser/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ macro_rules! define_wasm_features {
/// This is the disabled zero-size version of this structure because the
/// `features` feature was disabled at compile time of this crate.
#[cfg(not(feature = "features"))]
#[derive(Clone, Debug, Default, Hash, Copy)]
#[derive(Clone, Debug, Default, Hash, Copy, PartialEq)]
pub struct WasmFeatures {
_priv: (),
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmparser/src/validator/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,7 @@ impl WasmModuleResources for OperatorValidatorResources<'_> {

/// The implementation of [`WasmModuleResources`] used by
/// [`Validator`](crate::Validator).
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ValidatorResources(pub(crate) Arc<Module>);

impl WasmModuleResources for ValidatorResources {
Expand Down
34 changes: 32 additions & 2 deletions crates/wasmparser/src/validator/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl<T: WasmModuleResources> FuncToValidate<T> {
///
/// This is a finalized validator which is ready to process a [`FunctionBody`].
/// This is created from the [`FuncToValidate::into_validator`] method.
#[derive(Clone)]
pub struct FuncValidator<T> {
validator: OperatorValidator,
resources: T,
Expand Down Expand Up @@ -121,6 +122,19 @@ impl<T: WasmModuleResources> FuncValidator<T> {
reader.set_features(self.validator.features);
}
while !reader.eof() {
// In a `debug_check_try_op` build, verify that `rollback` successfully returns the
// validator to its previous state after each (valid or invalid) operator.
#[cfg(all(debug_check_try_op, feature = "try-op"))]
{
let snapshot = self.validator.clone();
let op = reader.peek_operator(&self.visitor(reader.original_position()))?;
self.validator.begin_try_op();
let _ = self.op(reader.original_position(), &op);
self.validator.rollback();
self.validator.pop_push_log.clear();
assert!(self.validator == snapshot);
}

// In a debug build, verify that the validator's pops and pushes to and from
// the operand stack match the operator's arity.
#[cfg(debug_assertions)]
Expand Down Expand Up @@ -194,14 +208,30 @@ arity mismatch in validation

/// Validates the next operator in a function.
///
/// This functions is expected to be called once-per-operator in a
/// This function is expected to be called once-per-operator in a
/// WebAssembly function. Each operator's offset in the original binary and
/// the operator itself are passed to this function to provide more useful
/// error messages.
/// error messages. On error, the validator may be left in an undefined
/// state and should not be reused.
pub fn op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> {
self.visitor(offset).visit_operator(operator)
}

/// Validates the next operator in a function, rolling back the validator
/// to its previous state if this is unsuccesful. The validator may be reused
/// even after an error.
#[cfg(feature = "try-op")]
pub fn try_op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> {
self.validator.begin_try_op();
let res = self.op(offset, operator);
if res.is_ok() {
self.validator.commit();
} else {
self.validator.rollback();
}
res
}

/// Get the operator visitor for the next operator in the function.
///
/// The returned visitor is intended to visit just one instruction at the `offset`.
Expand Down
Loading