Skip to content

Commit

Permalink
Merge pull request #1991 from CosmWasm/area-under-curve
Browse files Browse the repository at this point in the history
Count sum of all params over all functions
  • Loading branch information
webmaster128 authored Jan 24, 2024
2 parents 226add5 + 2d8c0e2 commit f69ffc7
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 6 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ and this project adheres to

## [Unreleased]

### Changed

- cosmwasm-vm: Limit total number of function parameters in
`check_wasm_functions` and increase max function count and max parameter
count. ([#1991])

[#1991]: https://github.com/CosmWasm/cosmwasm/pull/1991

## [2.0.0-beta.1] - 2023-01-22

### Fixed
Expand Down
19 changes: 15 additions & 4 deletions packages/vm/src/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ const TABLE_SIZE_LIMIT: u32 = 2500; // entries
/// when a user accidentally includes wasm-bindgen, they get a bunch of unsupported imports.
const MAX_IMPORTS: usize = 100;

const MAX_FUNCTIONS: usize = 10000;
const MAX_FUNCTIONS: usize = 20_000;

const MAX_FUNCTION_PARAMS: usize = 50;
const MAX_FUNCTION_PARAMS: usize = 100;

const MAX_TOTAL_FUNCTION_PARAMS: usize = 10_000;

const MAX_FUNCTION_RESULTS: usize = 1;

Expand Down Expand Up @@ -251,29 +253,38 @@ fn check_wasm_functions(module: &ParsedWasm) -> VmResult<()> {
"Wasm contract contains function with more than {MAX_FUNCTION_RESULTS} results"
)));
}

if module.total_func_params > MAX_TOTAL_FUNCTION_PARAMS {
return Err(VmError::static_validation_err(format!(
"Wasm contract contains more than {MAX_TOTAL_FUNCTION_PARAMS} function parameters in total"
)));
}

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use crate::errors::VmError;
use crate::{capabilities_from_csv, errors::VmError};

static CONTRACT_0_7: &[u8] = include_bytes!("../testdata/hackatom_0.7.wasm");
static CONTRACT_0_12: &[u8] = include_bytes!("../testdata/hackatom_0.12.wasm");
static CONTRACT_0_14: &[u8] = include_bytes!("../testdata/hackatom_0.14.wasm");
static CONTRACT_0_15: &[u8] = include_bytes!("../testdata/hackatom_0.15.wasm");
static CONTRACT: &[u8] = include_bytes!("../testdata/hackatom.wasm");
static CYBERPUNK: &[u8] = include_bytes!("../testdata/cyberpunk.wasm");
static CONTRACT_RUST_170: &[u8] = include_bytes!("../testdata/cyberpunk_rust170.wasm");

fn default_capabilities() -> HashSet<String> {
["staking".to_string()].into_iter().collect()
capabilities_from_csv("cosmwasm_1_1,cosmwasm_1_2,iterator,staking,stargate")
}

#[test]
fn check_wasm_passes_for_latest_contract() {
// this is our reference check, must pass
check_wasm(CONTRACT, &default_capabilities()).unwrap();
check_wasm(CYBERPUNK, &default_capabilities()).unwrap();
}

#[test]
Expand Down
32 changes: 30 additions & 2 deletions packages/vm/src/parsed_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use wasmer::wasmparser::{
WasmFeatures,
};

use crate::VmResult;
use crate::{VmError, VmResult};

/// A parsed and validated wasm module.
/// It keeps track of the parts that are important for our static analysis and compatibility checks.
Expand All @@ -16,8 +16,15 @@ pub struct ParsedWasm<'a> {
pub memories: Vec<MemoryType>,
pub function_count: usize,
pub type_count: u32,
/// How many parameters a type has.
/// The index is the type id
pub type_params: Vec<usize>,
/// How many parameters the function with the most parameters has
pub max_func_params: usize,
/// How many results the function with the most results has
pub max_func_results: usize,
/// How many function parameters are used in the module
pub total_func_params: usize,
}

impl<'a> ParsedWasm<'a> {
Expand All @@ -41,8 +48,10 @@ impl<'a> ParsedWasm<'a> {
memories: vec![],
function_count: 0,
type_count: 0,
type_params: Vec::new(),
max_func_params: 0,
max_func_results: 0,
total_func_params: 0,
};

let mut fun_allocations = Default::default();
Expand All @@ -61,10 +70,13 @@ impl<'a> ParsedWasm<'a> {
match p {
Payload::TypeSection(t) => {
this.type_count = t.get_count();
for t_res in t {
this.type_params = Vec::with_capacity(t.get_count() as usize);
for t_res in t.into_iter() {
let ty: Type = t_res?;
match ty {
Type::Func(ft) => {
this.type_params.push(ft.params().len());

this.max_func_params =
core::cmp::max(ft.params().len(), this.max_func_params);
this.max_func_results =
Expand All @@ -73,6 +85,22 @@ impl<'a> ParsedWasm<'a> {
}
}
}
Payload::FunctionSection(section) => {
// In valid Wasm, the function section always has to come after the type section
// (see https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#modules%E2%91%A0%E2%93%AA),
// so we can assume that the type_params map is already filled at this point

for a in section {
let type_index = a? as usize;
this.total_func_params +=
this.type_params.get(type_index).ok_or_else(|| {
// this will also be thrown if the wasm section order is invalid
VmError::static_validation_err(
"Wasm bytecode error: function uses unknown type index",
)
})?
}
}
Payload::Version { num, .. } => this.version = num,
Payload::ImportSection(i) => {
this.imports = i.into_iter().collect::<Result<Vec<_>, _>>()?;
Expand Down

0 comments on commit f69ffc7

Please sign in to comment.