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
34 changes: 31 additions & 3 deletions rs/embedders/src/wasm_utils/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
use wirm::{
DataSegment, DataSegmentKind, DataType, InitInstr, Module,
ir::{
id::TypeID,
id::{FunctionID, TypeID},
module::{
LocalOrImport, module_functions::FuncKind, module_globals::GlobalKind,
module_types::Types,
Expand Down Expand Up @@ -65,6 +65,7 @@ pub const WASM_VALID_SYSTEM_FUNCTIONS: [&str; 7] = [
const WASM_FUNCTION_COMPLEXITY_LIMIT: Complexity = Complexity(1_000_000);
pub const WASM_FUNCTION_SIZE_LIMIT: usize = 1_000_000;
pub const MAX_CODE_SECTION_SIZE_IN_BYTES: u32 = 12 * 1024 * 1024;
pub const MAX_WASM_FUNCTION_NAME_LENGTH: usize = 1024 * 1024;

// Represents the expected function signature for any System APIs the Internet
// Computer provides or any special exported user functions.
Expand Down Expand Up @@ -1241,18 +1242,45 @@ fn validate_global_section(module: &Module, max_globals: usize) -> Result<(), Wa
}

// Checks that no more than `max_functions` are defined in the
// module.
// module and all function names are less than MAX_WASM_FUNCTION_NAME_LENGTH
// bytes.
fn validate_function_section(
module: &Module,
max_functions: usize,
) -> Result<(), WasmValidationError> {
let func_count = module.functions.iter().filter(|f| f.is_local()).count();
let local_indexes = module
.functions
.iter()
.enumerate()
.filter(|(_, f)| f.is_local())
.map(|(i, _)| FunctionID(i as u32))
.collect::<Vec<FunctionID>>();

let func_count = local_indexes.len();
if func_count > max_functions {
return Err(WasmValidationError::TooManyFunctions {
defined: func_count,
allowed: max_functions,
});
}
// We only need to look at local functions, since `validate_import_section`
// already checks and only allows a fixed set of valid imports.
for id in local_indexes {
Comment thread
venkkatesh-sekar marked this conversation as resolved.
if let Some(name) = module.functions.get_name(id)
&& name.len() > MAX_WASM_FUNCTION_NAME_LENGTH
{
let limit = std::cmp::min(name.len(), 100);
let truncated_name: String = name.chars().take(limit).collect();
let truncated_name = format!("{truncated_name}...");
return Err(WasmValidationError::FunctionNameTooLarge {
index: *id as usize,
size: name.len(),
allowed: MAX_WASM_FUNCTION_NAME_LENGTH,
Comment thread
venkkatesh-sekar marked this conversation as resolved.
name: truncated_name,
});
}
}

Ok(())
}

Expand Down
27 changes: 26 additions & 1 deletion rs/embedders/tests/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use ic_embedders::{
WasmtimeEmbedder,
wasm_utils::{
Complexity, WasmImportsDetails, WasmValidationDetails, validate_and_instrument_for_testing,
validation::{RESERVED_SYMBOLS, extract_custom_section_name},
validation::{
MAX_WASM_FUNCTION_NAME_LENGTH, RESERVED_SYMBOLS, extract_custom_section_name,
},
},
};
use ic_interfaces::execution_environment::HypervisorError;
Expand Down Expand Up @@ -1453,3 +1455,26 @@ fn can_validate_table_section_with_mixed_tables() {
Err(WasmValidationError::InvalidTableSection(_))
);
}

#[test]
fn wasm_with_long_func_name_is_invalid() {
let wat = format!(
r#"
(module
(type (;0;) (func))
(func ${} (type 0))
)"#,
"A".repeat(MAX_WASM_FUNCTION_NAME_LENGTH + 10)
);

let wasm = wat2wasm(&wat).unwrap();
assert_eq!(
validate_wasm_binary(&wasm, &EmbeddersConfig::default()),
Err(WasmValidationError::FunctionNameTooLarge {
index: 0,
size: MAX_WASM_FUNCTION_NAME_LENGTH + 10,
allowed: MAX_WASM_FUNCTION_NAME_LENGTH,
name: format!("{}...", "A".repeat(100)),
})
);
}
21 changes: 21 additions & 0 deletions rs/types/wasm_types/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ pub enum WasmValidationError {
size: usize,
allowed: usize,
},
/// A function name was too large.
FunctionNameTooLarge {
index: usize,
size: usize,
allowed: usize,
name: String,
},
/// The code section is too large.
CodeSectionTooLarge {
size: u32,
Expand Down Expand Up @@ -246,6 +253,16 @@ impl std::fmt::Display for WasmValidationError {
"Wasm module contains a function at index {index} \
of size {size} that exceeds the maximum allowed size of {allowed}.",
),
Self::FunctionNameTooLarge {
index,
size,
allowed,
name,
} => write!(
f,
"Wasm module contains a function at index {index} \
with name '{name}' of size {size} bytes that exceeds the maximum allowed size of {allowed} bytes.",
),
Self::CodeSectionTooLarge { size, allowed } => write!(
f,
"Wasm module code section size of {size} \
Expand Down Expand Up @@ -319,6 +336,10 @@ impl AsErrorHelp for WasmValidationError {
.to_string(),
doc_link: doc_ref("wasm-module-function-too-large"),
},
WasmValidationError::FunctionNameTooLarge { .. } => ErrorHelp::UserError {
suggestion: "Try using shorter function names.".to_string(),
doc_link: doc_ref("wasm-module-function-name-too-large"),
},
WasmValidationError::CodeSectionTooLarge { .. } => ErrorHelp::UserError {
suggestion: "Try shrinking the module code section using tools like \
`ic-wasm` or splitting the logic across multiple canisters."
Expand Down
Loading