Skip to content

Commit

Permalink
Merge pull request #145 from rust-secure-code/wasm
Browse files Browse the repository at this point in the history
WASM support
  • Loading branch information
Shnatsel authored May 3, 2024
2 parents 1a53658 + 6bca5e4 commit 33ba39b
Show file tree
Hide file tree
Showing 28 changed files with 807 additions and 32 deletions.
1 change: 1 addition & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
target: "wasm32-unknown-unknown"
profile: minimal
override: true
# multiple additional targets are not supported, so we invoke the action multiple times
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ jobs:
toolchain: ${{ matrix.toolchain }}
profile: minimal
override: true
target: "wasm32-unknown-unknown"
- run: cargo test --all-features --workspace
2 changes: 1 addition & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
toolchain: ${{ matrix.toolchain }}
profile: minimal
override: true
target: "x86_64-pc-windows-msvc"
target: "wasm32-unknown-unknown"
- name: "Test on the native x86_64-pc-windows-mscv"
run: cargo test --all-features --workspace
- name: "Test when cross-compiling to x86_64-pc-windows-gnu"
Expand Down
79 changes: 73 additions & 6 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Know the exact crate versions used to build your Rust executable. Audit binaries

This works by embedding data about the dependency tree in JSON format into a dedicated linker section of the compiled executable.

Linux, Windows and Mac OS are officially supported. All other ELF targets should work, but are not tested on CI. WASM is currently not supported, but patches are welcome.
Linux, Windows and Mac OS are officially supported. [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly) is also supported starting with v0.6.3. All other ELF targets should work, but are not tested on CI.

The end goal is to get Cargo itself to encode this information in binaries. There is an RFC for an implementation within Cargo, for which this project paves the way: https://github.com/rust-lang/rfcs/pull/2801

Expand Down
6 changes: 5 additions & 1 deletion auditable-extract/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "auditable-extract"
version = "0.3.2"
version = "0.3.3"
authors = ["Sergey \"Shnatsel\" Davidoff <[email protected]>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-secure-code/cargo-auditable"
Expand All @@ -12,3 +12,7 @@ edition = "2018"

[dependencies]
binfarce = "0.2"
wasmparser = { version = "0.206.0", optional = true }

[features]
wasm = ["wasmparser"]
4 changes: 4 additions & 0 deletions auditable-extract/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
30 changes: 30 additions & 0 deletions auditable-extract/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "auditable-extract-fuzz"
version = "0.0.0"
publish = false
edition = "2018"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"

[dependencies.auditable-extract]
path = ".."
features = ["wasm"]

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[profile.release]
debug = 1

[[bin]]
name = "parse_wasm"
path = "fuzz_targets/parse_wasm.rs"
test = false
doc = false

# The other format parsers are tested as part of the `binfarce` crate
7 changes: 7 additions & 0 deletions auditable-extract/fuzz/fuzz_targets/parse_wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![no_main]

use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
let _ = auditable_extract::raw_auditable_data_wasm_for_fuzz(data);
});
34 changes: 31 additions & 3 deletions auditable-extract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//!
//! This crate parses platform-specific binary formats ([ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format),
//! [PE](https://en.wikipedia.org/wiki/Portable_Executable),
//! [Mach-O](https://en.wikipedia.org/wiki/Mach-O)) and obtains the compressed audit data.
//! [Mach-O](https://en.wikipedia.org/wiki/Mach-O), [WASM](https://en.wikipedia.org/wiki/WebAssembly)) and obtains the compressed audit data.
//!
//! Unlike other binary parsing crates, it is specifically designed to be resilient to malicious input.
//! It 100% safe Rust (including all dependencies) and performs no heap allocations.
Expand All @@ -15,7 +15,7 @@
//! **Note:** this is a low-level crate that only implements binary parsing. It rarely should be used directly.
//! You probably want the higher-level [`auditable-info`](https://docs.rs/auditable-info) crate instead.
//!
//! The following snippet demonstrates full extraction pipeline, including decompression
//! The following snippet demonstrates full extraction pipeline using this crate, including decompression
//! using the safe-Rust [`miniz_oxide`](http://docs.rs/miniz_oxide/) and optional JSON parsing
//! via [`auditable-serde`](http://docs.rs/auditable-serde/):
//!
Expand All @@ -42,6 +42,22 @@
//! Ok(())
//! }
//! ```
//!
//! ## WebAssembly support
//!
//! We use a third-party crate [`wasmparser`](https://crates.io/crates/wasmparser)
//! created by Bytecode Alliance for parsing WebAssembly.
//! It is a robust and high-quality parser, but its dependencies contain some `unsafe` code,
//! most of which is not actually used in our build configuration.
//!
//! We have manually audited it and found it to be sound.
//! Still, the security guarantees for it are not as ironclad as for other parsers.
//! Because of that WebAssembly support is gated behind the optional `wasm` feature.
//! Be sure to [enable](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features)
//! the `wasm` feature if you want to parse WebAssembly.
#[cfg(feature = "wasm")]
mod wasm;

use binfarce::Format;

Expand Down Expand Up @@ -75,10 +91,22 @@ pub fn raw_auditable_data(data: &[u8]) -> Result<&[u8], Error> {
.ok_or(Error::NoAuditData)?;
Ok(data.get(section.range()?).ok_or(Error::UnexpectedEof)?)
}
_ => Err(Error::NotAnExecutable),
Format::Unknown => {
#[cfg(feature = "wasm")]
if data.starts_with(b"\0asm") {
return wasm::raw_auditable_data_wasm(data);
}

Err(Error::NotAnExecutable)
}
}
}

#[cfg(all(fuzzing, feature = "wasm"))]
pub fn raw_auditable_data_wasm_for_fuzz(input: &[u8]) -> Result<&[u8], Error> {
wasm::raw_auditable_data_wasm(input)
}

#[derive(Debug, Copy, Clone)]
pub enum Error {
NoAuditData,
Expand Down
52 changes: 52 additions & 0 deletions auditable-extract/src/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! Implements WASM parsing
use crate::Error;

use wasmparser::{self, Chunk, Payload};

pub(crate) fn raw_auditable_data_wasm(mut input: &[u8]) -> Result<&[u8], Error> {
let mut parser = wasmparser::Parser::new(0);

// `wasmparser` relies on manually advancing the offset,
// which potentially allows infinite loops if the logic is wrong somewhere.
// Therefore, limit the maximum number of iterations to 10,000.
// This is way more than any sane WASM blob will have,
// and prevents infinite loops in case of such logic errors.
for _i in 0..10_000 {
// wasmparser errors are strings, so we can't reasonably convert them
let payload = match parser
.parse(input, true)
.map_err(|_| Error::MalformedFile)?
{
// This shouldn't be possible because `eof` is always true.
Chunk::NeedMoreData(_) => return Err(Error::MalformedFile),

Chunk::Parsed { payload, consumed } => {
// Guard against made-up "consumed" values that would cause a panic
input = match input.get(consumed..) {
Some(input) => input,
None => return Err(Error::MalformedFile),
};
payload
}
};

match payload {
Payload::CustomSection(reader) => {
if reader.name() == ".dep-v0" {
return Ok(reader.data());
}
}
// We reached the end without seeing ".dep-v0" custom section
Payload::End(_) => return Err(Error::NoAuditData),
// ignore everything that's not a custom section
_ => {}
}
}

if cfg!(debug_assertions) {
panic!("The parser has been running for more than 10k sections! Is it stuck?");
} else {
Err(Error::MalformedFile)
}
}
6 changes: 6 additions & 0 deletions auditable-info/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.1] - 2024-05-03

### Added

- Added WebAssembly support, gated behind the non-default `wasm` feature

## [0.7.0] - 2023-04-27

### Changed
Expand Down
Loading

0 comments on commit 33ba39b

Please sign in to comment.