mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-13 23:11:53 +00:00
added memory/table import limits validation
This commit is contained in:
@ -15,8 +15,7 @@ fn main() {
|
|||||||
let program = parity_wasm::DefaultProgramInstance::with_env_params(
|
let program = parity_wasm::DefaultProgramInstance::with_env_params(
|
||||||
interpreter::EnvParams {
|
interpreter::EnvParams {
|
||||||
total_stack: 128*1024,
|
total_stack: 128*1024,
|
||||||
total_memory: 2*1024*1024,
|
..Default::default()
|
||||||
allow_memory_growth: false,
|
|
||||||
}
|
}
|
||||||
).expect("Failed to load program");
|
).expect("Failed to load program");
|
||||||
let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module");
|
let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module");
|
||||||
|
@ -22,8 +22,7 @@ fn main() {
|
|||||||
let program = parity_wasm::DefaultProgramInstance::with_env_params(
|
let program = parity_wasm::DefaultProgramInstance::with_env_params(
|
||||||
interpreter::EnvParams {
|
interpreter::EnvParams {
|
||||||
total_stack: 128*1024,
|
total_stack: 128*1024,
|
||||||
total_memory: 2*1024*1024,
|
..Default::default()
|
||||||
allow_memory_growth: false,
|
|
||||||
}
|
}
|
||||||
).expect("Program instance to load");
|
).expect("Program instance to load");
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use elements;
|
use elements;
|
||||||
use super::invoke::{Invoke, Identity};
|
use super::invoke::{Invoke, Identity};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MemoryDefinition {
|
pub struct MemoryDefinition {
|
||||||
pub min: u32,
|
pub min: u32,
|
||||||
pub max: Option<u32>,
|
pub max: Option<u32>,
|
||||||
pub data: Vec<MemoryDataDefinition>,
|
pub data: Vec<MemoryDataDefinition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MemoryDataDefinition {
|
pub struct MemoryDataDefinition {
|
||||||
pub offset: elements::InitExpr,
|
pub offset: elements::InitExpr,
|
||||||
pub values: Vec<u8>,
|
pub values: Vec<u8>,
|
||||||
|
@ -217,7 +217,7 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
|
|||||||
/// Push table
|
/// Push table
|
||||||
pub fn push_table(&mut self, mut table: table::TableDefinition) -> u32 {
|
pub fn push_table(&mut self, mut table: table::TableDefinition) -> u32 {
|
||||||
let entries = self.module.table.entries_mut();
|
let entries = self.module.table.entries_mut();
|
||||||
entries.push(elements::TableType::new(table.min, Some(table.min)));
|
entries.push(elements::TableType::new(table.min, table.max));
|
||||||
let table_index = (entries.len() - 1) as u32;
|
let table_index = (entries.len() - 1) as u32;
|
||||||
for entry in table.elements.drain(..) {
|
for entry in table.elements.drain(..) {
|
||||||
self.module.element.entries_mut()
|
self.module.element.entries_mut()
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
use elements;
|
use elements;
|
||||||
use super::invoke::{Invoke, Identity};
|
use super::invoke::{Invoke, Identity};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct TableDefinition {
|
pub struct TableDefinition {
|
||||||
pub min: u32,
|
pub min: u32,
|
||||||
|
pub max: Option<u32>,
|
||||||
pub elements: Vec<TableEntryDefinition>,
|
pub elements: Vec<TableEntryDefinition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct TableEntryDefinition {
|
pub struct TableEntryDefinition {
|
||||||
pub offset: elements::InitExpr,
|
pub offset: elements::InitExpr,
|
||||||
pub values: Vec<u32>,
|
pub values: Vec<u32>,
|
||||||
@ -35,6 +38,11 @@ impl<F> TableBuilder<F> where F: Invoke<TableDefinition> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_max(mut self, max: Option<u32>) -> Self {
|
||||||
|
self.table.max = max;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_element(mut self, index: u32, values: Vec<u32>) -> Self {
|
pub fn with_element(mut self, index: u32, values: Vec<u32>) -> Self {
|
||||||
self.table.elements.push(TableEntryDefinition {
|
self.table.elements.push(TableEntryDefinition {
|
||||||
offset: elements::InitExpr::new(vec![elements::Opcode::I32Const(index as i32)]),
|
offset: elements::InitExpr::new(vec![elements::Opcode::I32Const(index as i32)]),
|
||||||
@ -52,6 +60,7 @@ impl Default for TableDefinition {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TableDefinition {
|
TableDefinition {
|
||||||
min: 0,
|
min: 0,
|
||||||
|
max: None,
|
||||||
elements: Vec::new(),
|
elements: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ impl Serialize for TableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Memory limits
|
/// Memory limits
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ResizableLimits {
|
pub struct ResizableLimits {
|
||||||
initial: u32,
|
initial: u32,
|
||||||
maximum: Option<u32>,
|
maximum: Option<u32>,
|
||||||
|
@ -19,7 +19,7 @@ const DEFAULT_TOTAL_STACK: u32 = 5 * 1024 * 1024;
|
|||||||
/// Total memory, allocated by default.
|
/// Total memory, allocated by default.
|
||||||
const DEFAULT_TOTAL_MEMORY: u32 = 16 * 1024 * 1024;
|
const DEFAULT_TOTAL_MEMORY: u32 = 16 * 1024 * 1024;
|
||||||
/// Whether memory can be enlarged, or not.
|
/// Whether memory can be enlarged, or not.
|
||||||
const DEFAULT_ALLOW_MEMORY_GROWTH: bool = false;
|
const DEFAULT_ALLOW_MEMORY_GROWTH: bool = true;
|
||||||
/// Default tableBase variable value.
|
/// Default tableBase variable value.
|
||||||
const DEFAULT_TABLE_BASE: u32 = 0;
|
const DEFAULT_TABLE_BASE: u32 = 0;
|
||||||
/// Default tableBase variable value.
|
/// Default tableBase variable value.
|
||||||
@ -76,6 +76,8 @@ pub struct EnvParams {
|
|||||||
pub total_memory: u32,
|
pub total_memory: u32,
|
||||||
/// Allow memory growth.
|
/// Allow memory growth.
|
||||||
pub allow_memory_growth: bool,
|
pub allow_memory_growth: bool,
|
||||||
|
/// Table size.
|
||||||
|
pub table_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EnvModuleInstance<E: UserError> {
|
pub struct EnvModuleInstance<E: UserError> {
|
||||||
@ -180,7 +182,7 @@ pub fn env_module<E: UserError>(params: EnvParams) -> Result<EnvModuleInstance<E
|
|||||||
.with_export(ExportEntry::new("memory".into(), Internal::Memory(INDEX_MEMORY)))
|
.with_export(ExportEntry::new("memory".into(), Internal::Memory(INDEX_MEMORY)))
|
||||||
// tables
|
// tables
|
||||||
.table()
|
.table()
|
||||||
.with_min(DEFAULT_TABLE_SIZE)
|
.with_min(params.table_size)
|
||||||
.build()
|
.build()
|
||||||
.with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE)))
|
.with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE)))
|
||||||
// globals
|
// globals
|
||||||
@ -194,7 +196,7 @@ pub fn env_module<E: UserError>(params: EnvParams) -> Result<EnvModuleInstance<E
|
|||||||
.with_export(ExportEntry::new("DYNAMIC_BASE".into(), Internal::Global(INDEX_GLOBAL_DYNAMIC_BASE)))
|
.with_export(ExportEntry::new("DYNAMIC_BASE".into(), Internal::Global(INDEX_GLOBAL_DYNAMIC_BASE)))
|
||||||
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)])))
|
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)])))
|
||||||
.with_export(ExportEntry::new("DYNAMICTOP_PTR".into(), Internal::Global(INDEX_GLOBAL_DYNAMICTOP_PTR)))
|
.with_export(ExportEntry::new("DYNAMICTOP_PTR".into(), Internal::Global(INDEX_GLOBAL_DYNAMICTOP_PTR)))
|
||||||
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory as i32)])))
|
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(params.total_memory as i32)])))
|
||||||
.with_export(ExportEntry::new("TOTAL_MEMORY".into(), Internal::Global(INDEX_GLOBAL_TOTAL_MEMORY)))
|
.with_export(ExportEntry::new("TOTAL_MEMORY".into(), Internal::Global(INDEX_GLOBAL_TOTAL_MEMORY)))
|
||||||
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)])))
|
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)])))
|
||||||
.with_export(ExportEntry::new("ABORT".into(), Internal::Global(INDEX_GLOBAL_ABORT)))
|
.with_export(ExportEntry::new("ABORT".into(), Internal::Global(INDEX_GLOBAL_ABORT)))
|
||||||
@ -235,6 +237,7 @@ impl Default for EnvParams {
|
|||||||
total_stack: DEFAULT_TOTAL_STACK,
|
total_stack: DEFAULT_TOTAL_STACK,
|
||||||
total_memory: DEFAULT_TOTAL_MEMORY,
|
total_memory: DEFAULT_TOTAL_MEMORY,
|
||||||
allow_memory_growth: DEFAULT_ALLOW_MEMORY_GROWTH,
|
allow_memory_growth: DEFAULT_ALLOW_MEMORY_GROWTH,
|
||||||
|
table_size: DEFAULT_TABLE_SIZE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::u32;
|
use std::u32;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use elements::MemoryType;
|
use elements::{MemoryType, ResizableLimits};
|
||||||
use interpreter::{Error, UserError};
|
use interpreter::{Error, UserError};
|
||||||
use interpreter::module::check_limits;
|
use interpreter::module::check_limits;
|
||||||
|
|
||||||
@ -12,6 +12,8 @@ const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;
|
|||||||
|
|
||||||
/// Linear memory instance.
|
/// Linear memory instance.
|
||||||
pub struct MemoryInstance<E: UserError> {
|
pub struct MemoryInstance<E: UserError> {
|
||||||
|
/// Memofy limits.
|
||||||
|
limits: ResizableLimits,
|
||||||
/// Linear memory buffer.
|
/// Linear memory buffer.
|
||||||
buffer: RwLock<Vec<u8>>,
|
buffer: RwLock<Vec<u8>>,
|
||||||
/// Maximum buffer size.
|
/// Maximum buffer size.
|
||||||
@ -51,6 +53,7 @@ impl<E> MemoryInstance<E> where E: UserError {
|
|||||||
.ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?;
|
.ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?;
|
||||||
|
|
||||||
let memory = MemoryInstance {
|
let memory = MemoryInstance {
|
||||||
|
limits: memory_type.limits().clone(),
|
||||||
buffer: RwLock::new(vec![0; initial_size as usize]),
|
buffer: RwLock::new(vec![0; initial_size as usize]),
|
||||||
maximum_size: maximum_size,
|
maximum_size: maximum_size,
|
||||||
_dummy: Default::default(),
|
_dummy: Default::default(),
|
||||||
@ -59,6 +62,11 @@ impl<E> MemoryInstance<E> where E: UserError {
|
|||||||
Ok(Arc::new(memory))
|
Ok(Arc::new(memory))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return linear memory limits.
|
||||||
|
pub fn limits(&self) -> &ResizableLimits {
|
||||||
|
&self.limits
|
||||||
|
}
|
||||||
|
|
||||||
/// Return linear memory size (in pages).
|
/// Return linear memory size (in pages).
|
||||||
pub fn size(&self) -> u32 {
|
pub fn size(&self) -> u32 {
|
||||||
self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
|
self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
|
||||||
|
@ -304,12 +304,48 @@ impl<E> ModuleInstance<E> where E: UserError {
|
|||||||
self.imports.global(externals, import, Some(global_type.content_type().into()))?;
|
self.imports.global(externals, import, Some(global_type.content_type().into()))?;
|
||||||
},
|
},
|
||||||
&External::Memory(ref memory_type) => {
|
&External::Memory(ref memory_type) => {
|
||||||
check_limits(memory_type.limits())?;
|
let import_limits = memory_type.limits();
|
||||||
self.imports.memory(externals, import)?;
|
check_limits(import_limits)?;
|
||||||
|
|
||||||
|
let memory = self.imports.memory(externals, import)?;
|
||||||
|
let memory_limits = memory.limits();
|
||||||
|
|
||||||
|
// a linear-memory import's minimum length is required to be at most the imported linear memory's minimum length.
|
||||||
|
if import_limits.initial() > memory_limits.initial() {
|
||||||
|
return Err(Error::Validation(format!("trying to import memory with initial={} and import.initial={}", memory_limits.initial(), import_limits.initial())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// a linear-memory import is required to have a maximum length if the imported linear memory has a maximum length.
|
||||||
|
// if present, a linear-memory import's maximum length is required to be at least the imported linear memory's maximum length.
|
||||||
|
match (memory_limits.maximum(), import_limits.maximum()) {
|
||||||
|
(Some(_), None) | (None, Some(_)) =>
|
||||||
|
return Err(Error::Validation("trying to import memory with maximum absence mismatch".into())),
|
||||||
|
(Some(ml), Some(il)) if il < ml =>
|
||||||
|
return Err(Error::Validation(format!("trying to import memory with maximum={} and import.maximum={}", ml, il))),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
&External::Table(ref table_type) => {
|
&External::Table(ref table_type) => {
|
||||||
check_limits(table_type.limits())?;
|
let import_limits = table_type.limits();
|
||||||
self.imports.table(externals, import)?;
|
check_limits(import_limits)?;
|
||||||
|
|
||||||
|
let table = self.imports.table(externals, import)?;
|
||||||
|
let table_limits = table.limits();
|
||||||
|
|
||||||
|
// a table import's minimum length is required to be at most the imported table's minimum length.
|
||||||
|
if import_limits.initial() > table_limits.initial() {
|
||||||
|
return Err(Error::Validation(format!("trying to import table with initial={} and import.initial={}", table_limits.initial(), import_limits.initial())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// a table import is required to have a maximum length if the imported table has a maximum length.
|
||||||
|
// if present, a table import's maximum length is required to be at least the imported table's maximum length.
|
||||||
|
match (table_limits.maximum(), import_limits.maximum()) {
|
||||||
|
(Some(_), None) | (None, Some(_)) =>
|
||||||
|
return Err(Error::Validation("trying to import table with maximum absence mismatch".into())),
|
||||||
|
(Some(ml), Some(il)) if il < ml =>
|
||||||
|
return Err(Error::Validation(format!("trying to import table with maximum={} and import.maximum={}", ml, il))),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -600,7 +636,7 @@ impl<E> ModuleInstanceInterface<E> for ModuleInstance<E> where E: UserError {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_internal_function(&self, mut outer: CallerContext<E>, index: u32) -> Result<Option<RuntimeValue>, Error<E>> {
|
fn call_internal_function(&self, outer: CallerContext<E>, index: u32) -> Result<Option<RuntimeValue>, Error<E>> {
|
||||||
let function_type = self.function_type(ItemIndex::Internal(index))?;
|
let function_type = self.function_type(ItemIndex::Internal(index))?;
|
||||||
let args = prepare_function_args(&function_type, outer.value_stack)?;
|
let args = prepare_function_args(&function_type, outer.value_stack)?;
|
||||||
let function_ref = InternalFunctionReference { module: self.self_ref(Some(outer.externals))?, internal_index: index };
|
let function_ref = InternalFunctionReference { module: self.self_ref(Some(outer.externals))?, internal_index: index };
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::u32;
|
use std::u32;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use elements::TableType;
|
use elements::{TableType, ResizableLimits};
|
||||||
use interpreter::{Error, UserError};
|
use interpreter::{Error, UserError};
|
||||||
use interpreter::module::check_limits;
|
use interpreter::module::check_limits;
|
||||||
use interpreter::variable::{VariableInstance, VariableType};
|
use interpreter::variable::{VariableInstance, VariableType};
|
||||||
@ -9,6 +9,8 @@ use interpreter::value::RuntimeValue;
|
|||||||
|
|
||||||
/// Table instance.
|
/// Table instance.
|
||||||
pub struct TableInstance<E: UserError> {
|
pub struct TableInstance<E: UserError> {
|
||||||
|
/// Table limits.
|
||||||
|
limits: ResizableLimits,
|
||||||
/// Table variables type.
|
/// Table variables type.
|
||||||
variable_type: VariableType,
|
variable_type: VariableType,
|
||||||
/// Table memory buffer.
|
/// Table memory buffer.
|
||||||
@ -27,6 +29,7 @@ impl<E> TableInstance<E> where E: UserError {
|
|||||||
|
|
||||||
let variable_type = table_type.elem_type().into();
|
let variable_type = table_type.elem_type().into();
|
||||||
Ok(Arc::new(TableInstance {
|
Ok(Arc::new(TableInstance {
|
||||||
|
limits: table_type.limits().clone(),
|
||||||
variable_type: variable_type,
|
variable_type: variable_type,
|
||||||
buffer: RwLock::new(
|
buffer: RwLock::new(
|
||||||
vec![TableElement::new(VariableInstance::new(true, variable_type, RuntimeValue::Null)?); table_type.limits().initial() as usize]
|
vec![TableElement::new(VariableInstance::new(true, variable_type, RuntimeValue::Null)?); table_type.limits().initial() as usize]
|
||||||
@ -34,6 +37,11 @@ impl<E> TableInstance<E> where E: UserError {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return table limits.
|
||||||
|
pub fn limits(&self) -> &ResizableLimits {
|
||||||
|
&self.limits
|
||||||
|
}
|
||||||
|
|
||||||
/// Get variable type for this table.
|
/// Get variable type for this table.
|
||||||
pub fn variable_type(&self) -> VariableType {
|
pub fn variable_type(&self) -> VariableType {
|
||||||
self.variable_type
|
self.variable_type
|
||||||
|
@ -5,7 +5,7 @@ use std::cell::RefCell;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use builder::module;
|
use builder::module;
|
||||||
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
|
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
|
||||||
InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType};
|
InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType, TableType, MemoryType};
|
||||||
use interpreter::{Error, UserError, ProgramInstance, DefaultProgramInstance, DefaultModuleInstance};
|
use interpreter::{Error, UserError, ProgramInstance, DefaultProgramInstance, DefaultModuleInstance};
|
||||||
use interpreter::env_native::{env_native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor};
|
use interpreter::env_native::{env_native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor};
|
||||||
use interpreter::memory::MemoryInstance;
|
use interpreter::memory::MemoryInstance;
|
||||||
@ -456,3 +456,143 @@ fn if_else_with_return_type_validation() {
|
|||||||
Opcode::End,
|
Opcode::End,
|
||||||
]).unwrap();
|
]).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn memory_import_limits_initial() {
|
||||||
|
let core_module = module()
|
||||||
|
.memory().with_min(10).build()
|
||||||
|
.with_export(ExportEntry::new("memory".into(), Internal::Memory(0)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let program = DefaultProgramInstance::new().unwrap();
|
||||||
|
program.add_module("core", core_module, None).unwrap();
|
||||||
|
|
||||||
|
let test_cases = vec![
|
||||||
|
(9, false),
|
||||||
|
(10, false),
|
||||||
|
(11, true),
|
||||||
|
];
|
||||||
|
|
||||||
|
for test_case in test_cases {
|
||||||
|
let (import_initial, is_error) = test_case;
|
||||||
|
let client_module = module()
|
||||||
|
.with_import(ImportEntry::new("core".into(), "memory".into(), External::Memory(MemoryType::new(import_initial, None))))
|
||||||
|
.build();
|
||||||
|
match program.add_module("client", client_module, None).map(|_| ()) {
|
||||||
|
Ok(_) if !is_error => (),
|
||||||
|
Err(Error::Validation(ref actual_error))
|
||||||
|
if is_error && actual_error == &format!("trying to import memory with initial=10 and import.initial={}", import_initial) => (),
|
||||||
|
x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn memory_import_limits_maximum() {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum MaximumError { AbsenceMismatch, ValueMismatch, Ok };
|
||||||
|
|
||||||
|
let test_cases = vec![
|
||||||
|
(None, Some(100), MaximumError::AbsenceMismatch),
|
||||||
|
(Some(100), None, MaximumError::AbsenceMismatch),
|
||||||
|
(Some(100), Some(98), MaximumError::ValueMismatch),
|
||||||
|
(Some(100), Some(100), MaximumError::Ok),
|
||||||
|
(Some(100), Some(101), MaximumError::Ok),
|
||||||
|
(None, None, MaximumError::Ok),
|
||||||
|
];
|
||||||
|
|
||||||
|
let program = DefaultProgramInstance::new().unwrap();
|
||||||
|
for test_case in test_cases {
|
||||||
|
let (core_maximum, client_maximum, expected_err) = test_case;
|
||||||
|
let core_module = module()
|
||||||
|
.memory().with_min(10).with_max(core_maximum).build()
|
||||||
|
.with_export(ExportEntry::new("memory".into(), Internal::Memory(0)))
|
||||||
|
.build();
|
||||||
|
let client_module = module()
|
||||||
|
.with_import(ImportEntry::new("core".into(), "memory".into(), External::Memory(MemoryType::new(10, client_maximum))))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
program.add_module("core", core_module, None).unwrap();
|
||||||
|
match program.add_module("client", client_module, None).map(|_| ()) {
|
||||||
|
Err(Error::Validation(actual_err)) => match expected_err {
|
||||||
|
MaximumError::AbsenceMismatch
|
||||||
|
if actual_err == "trying to import memory with maximum absence mismatch".to_owned() => (),
|
||||||
|
MaximumError::ValueMismatch
|
||||||
|
if actual_err == format!("trying to import memory with maximum={} and import.maximum={}", core_maximum.unwrap_or_default(), client_maximum.unwrap_or_default()) => (),
|
||||||
|
_ => panic!("unexpected validation error for test_case {:?}: {}", test_case, actual_err),
|
||||||
|
},
|
||||||
|
Ok(_) if expected_err == MaximumError::Ok => (),
|
||||||
|
x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_import_limits_initial() {
|
||||||
|
let core_module = module()
|
||||||
|
.table().with_min(10).build()
|
||||||
|
.with_export(ExportEntry::new("table".into(), Internal::Table(0)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let program = DefaultProgramInstance::new().unwrap();
|
||||||
|
program.add_module("core", core_module, None).unwrap();
|
||||||
|
|
||||||
|
let test_cases = vec![
|
||||||
|
(9, false),
|
||||||
|
(10, false),
|
||||||
|
(11, true),
|
||||||
|
];
|
||||||
|
|
||||||
|
for test_case in test_cases {
|
||||||
|
let (import_initial, is_error) = test_case;
|
||||||
|
let client_module = module()
|
||||||
|
.with_import(ImportEntry::new("core".into(), "table".into(), External::Table(TableType::new(import_initial, None))))
|
||||||
|
.build();
|
||||||
|
match program.add_module("client", client_module, None).map(|_| ()) {
|
||||||
|
Ok(_) if !is_error => (),
|
||||||
|
Err(Error::Validation(ref actual_error))
|
||||||
|
if is_error && actual_error == &format!("trying to import table with initial=10 and import.initial={}", import_initial) => (),
|
||||||
|
x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_import_limits_maximum() {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum MaximumError { AbsenceMismatch, ValueMismatch, Ok };
|
||||||
|
|
||||||
|
let test_cases = vec![
|
||||||
|
(None, Some(100), MaximumError::AbsenceMismatch),
|
||||||
|
(Some(100), None, MaximumError::AbsenceMismatch),
|
||||||
|
(Some(100), Some(98), MaximumError::ValueMismatch),
|
||||||
|
(Some(100), Some(100), MaximumError::Ok),
|
||||||
|
(Some(100), Some(101), MaximumError::Ok),
|
||||||
|
(None, None, MaximumError::Ok),
|
||||||
|
];
|
||||||
|
|
||||||
|
let program = DefaultProgramInstance::new().unwrap();
|
||||||
|
for test_case in test_cases {
|
||||||
|
let (core_maximum, client_maximum, expected_err) = test_case;
|
||||||
|
let core_module = module()
|
||||||
|
.table().with_min(10).with_max(core_maximum).build()
|
||||||
|
.with_export(ExportEntry::new("table".into(), Internal::Table(0)))
|
||||||
|
.build();
|
||||||
|
let client_module = module()
|
||||||
|
.with_import(ImportEntry::new("core".into(), "table".into(), External::Table(TableType::new(10, client_maximum))))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
program.add_module("core", core_module, None).unwrap();
|
||||||
|
match program.add_module("client", client_module, None).map(|_| ()) {
|
||||||
|
Err(Error::Validation(actual_err)) => match expected_err {
|
||||||
|
MaximumError::AbsenceMismatch
|
||||||
|
if actual_err == "trying to import table with maximum absence mismatch".to_owned() => (),
|
||||||
|
MaximumError::ValueMismatch
|
||||||
|
if actual_err == format!("trying to import table with maximum={} and import.maximum={}", core_maximum.unwrap_or_default(), client_maximum.unwrap_or_default()) => (),
|
||||||
|
_ => panic!("unexpected validation error for test_case {:?}: {}", test_case, actual_err),
|
||||||
|
},
|
||||||
|
Ok(_) if expected_err == MaximumError::Ok => (),
|
||||||
|
x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use elements::deserialize_file;
|
use elements::deserialize_file;
|
||||||
use elements::Module;
|
use elements::Module;
|
||||||
use interpreter::{EnvParams, ExecutionParams, DefaultProgramInstance};
|
use interpreter::{ExecutionParams, DefaultProgramInstance};
|
||||||
use interpreter::value::RuntimeValue;
|
use interpreter::value::RuntimeValue;
|
||||||
use interpreter::module::{ModuleInstanceInterface, ItemIndex};
|
use interpreter::module::{ModuleInstanceInterface, ItemIndex};
|
||||||
|
|
||||||
@ -11,12 +11,7 @@ fn interpreter_inc_i32() {
|
|||||||
// The WASM file containing the module and function
|
// The WASM file containing the module and function
|
||||||
const WASM_FILE: &str = &"res/cases/v1/inc_i32.wasm";
|
const WASM_FILE: &str = &"res/cases/v1/inc_i32.wasm";
|
||||||
|
|
||||||
let program = DefaultProgramInstance::with_env_params(
|
let program = DefaultProgramInstance::new().expect("Failed to instanciate program");
|
||||||
EnvParams {
|
|
||||||
total_stack: 128 * 1024,
|
|
||||||
total_memory: 2 * 1024 * 1024,
|
|
||||||
allow_memory_growth: false,
|
|
||||||
}).expect("Failed to instanciate program");
|
|
||||||
|
|
||||||
let module: Module =
|
let module: Module =
|
||||||
deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer");
|
deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer");
|
||||||
@ -48,11 +43,7 @@ fn interpreter_accumulate_u8() {
|
|||||||
const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1];
|
const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1];
|
||||||
|
|
||||||
// Declare the memory limits of the runtime-environment
|
// Declare the memory limits of the runtime-environment
|
||||||
let program = DefaultProgramInstance::with_env_params(EnvParams {
|
let program = DefaultProgramInstance::new().expect("Failed to instanciate program");
|
||||||
total_stack: 128 * 1024,
|
|
||||||
total_memory: 2 * 1024 * 1024,
|
|
||||||
allow_memory_growth: false,
|
|
||||||
}).expect("Failed to instanciate program");
|
|
||||||
|
|
||||||
// Load the module-structure from wasm-file and add to program
|
// Load the module-structure from wasm-file and add to program
|
||||||
let module: Module =
|
let module: Module =
|
||||||
|
Reference in New Issue
Block a user