mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-22 11:11:54 +00:00
Implement functions checking
This commit is contained in:
@ -366,41 +366,7 @@ impl ModuleInstance {
|
||||
return Err(Error::Validation(format!("length of function section is {}, while len of code section is {}", function_section_len, code_section_len)));
|
||||
}
|
||||
|
||||
// TODO: pepyakin reimplement
|
||||
// // validate every function body in user modules
|
||||
// if function_section_len != 0 { // tests use invalid code
|
||||
// let function_section = self.module.function_section().expect("function_section_len != 0; qed");
|
||||
// let code_section = self.module.code_section().expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
||||
// // check every function body
|
||||
// for (index, function) in function_section.entries().iter().enumerate() {
|
||||
// let function_labels = {
|
||||
// let function_type = self.function_type_by_index(function.type_ref())?;
|
||||
// let function_body = code_section.bodies().get(index as usize).ok_or(Error::Validation(format!("Missing body for function {}", index)))?;
|
||||
// let mut locals = function_type.params().to_vec();
|
||||
// locals.extend(function_body.locals().iter().flat_map(|l| repeat(l.value_type()).take(l.count() as usize)));
|
||||
|
||||
// let mut context = FunctionValidationContext::new(
|
||||
// self,
|
||||
// externals,
|
||||
// &locals,
|
||||
// DEFAULT_VALUE_STACK_LIMIT,
|
||||
// DEFAULT_FRAME_STACK_LIMIT,
|
||||
// function_type.clone());
|
||||
|
||||
// let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult);
|
||||
// Validator::validate_function(&mut context, block_type, function_body.code().elements())
|
||||
// .map_err(|e| {
|
||||
// if let Error::Validation(msg) = e {
|
||||
// Error::Validation(format!("Function #{} validation error: {}", index, msg))
|
||||
// } else {
|
||||
// e
|
||||
// }
|
||||
// })?;
|
||||
// context.function_labels()
|
||||
// };
|
||||
// self.functions_labels.insert(index as u32, function_labels);
|
||||
// }
|
||||
// }
|
||||
|
||||
// use data section to initialize linear memory regions
|
||||
if let Some(data_section) = self.module.data_section() {
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::u32;
|
||||
use std::sync::Arc;
|
||||
use std::iter::repeat;
|
||||
use std::collections::HashMap;
|
||||
use elements::{Opcode, BlockType, ValueType, TableElementType};
|
||||
use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody};
|
||||
use elements::{FunctionType, Type};
|
||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||
use validation::module::ModuleContext;
|
||||
@ -14,6 +15,11 @@ use common::{BlockFrame, BlockFrameType};
|
||||
/// Constant from wabt' validator.cc to skip alignment validation (not a part of spec).
|
||||
const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
|
||||
|
||||
/// Maximum number of entries in value stack.
|
||||
const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
|
||||
/// Maximum number of entries in frame stack.
|
||||
const DEFAULT_FRAME_STACK_LIMIT: usize = 1024;
|
||||
|
||||
/// Function validation context.
|
||||
pub struct FunctionValidationContext<'a> {
|
||||
/// Wasm module
|
||||
@ -56,9 +62,31 @@ pub enum InstructionOutcome {
|
||||
}
|
||||
|
||||
impl Validator {
|
||||
pub fn validate_function(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result<(), Error> {
|
||||
context.push_label(BlockFrameType::Function, block_type)?;
|
||||
Validator::validate_function_block(context, body)?;
|
||||
pub fn validate_function(
|
||||
module: &ModuleContext,
|
||||
func: &Func,
|
||||
body: &FuncBody,
|
||||
) -> Result<(), Error> {
|
||||
let (params, result_ty) = module.require_function(func.type_ref())?;
|
||||
|
||||
// locals = (params + vars)
|
||||
let mut locals = params;
|
||||
locals.extend(
|
||||
body.locals()
|
||||
.iter()
|
||||
.flat_map(|l| repeat(l.value_type()).take(l.count() as usize)),
|
||||
);
|
||||
|
||||
let mut context = FunctionValidationContext::new(
|
||||
&module,
|
||||
&locals,
|
||||
DEFAULT_VALUE_STACK_LIMIT,
|
||||
DEFAULT_FRAME_STACK_LIMIT,
|
||||
result_ty,
|
||||
);
|
||||
|
||||
context.push_label(BlockFrameType::Function, result_ty)?;
|
||||
Validator::validate_function_block(&mut context, body.code().elements())?;
|
||||
while !context.frame_stack.is_empty() {
|
||||
context.pop_label()?;
|
||||
}
|
||||
@ -378,7 +406,7 @@ impl Validator {
|
||||
}
|
||||
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.push_value(value_type)?;
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
@ -390,7 +418,7 @@ impl Validator {
|
||||
}
|
||||
}
|
||||
|
||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.pop_value(value_type)?;
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
@ -506,7 +534,7 @@ impl Validator {
|
||||
}
|
||||
|
||||
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
let (argument_types, return_type) = context.require_function(idx)?;
|
||||
let (argument_types, return_type) = context.module.require_function(idx)?;
|
||||
for argument_type in argument_types.iter().rev() {
|
||||
context.pop_value((*argument_type).into())?;
|
||||
}
|
||||
@ -517,10 +545,10 @@ impl Validator {
|
||||
}
|
||||
|
||||
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
context.require_table(DEFAULT_TABLE_INDEX, TableElementType::AnyFunc)?;
|
||||
context.module.require_table(DEFAULT_TABLE_INDEX, TableElementType::AnyFunc)?;
|
||||
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
let (argument_types, return_type) = context.require_function_type(idx)?;
|
||||
let (argument_types, return_type) = context.module.require_function_type(idx)?;
|
||||
for argument_type in argument_types.iter().rev() {
|
||||
context.pop_value((*argument_type).into())?;
|
||||
}
|
||||
@ -531,13 +559,13 @@ impl Validator {
|
||||
}
|
||||
|
||||
fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.push_value(ValueType::I32.into())?;
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
context.push_value(ValueType::I32.into())?;
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
@ -550,7 +578,7 @@ impl<'a> FunctionValidationContext<'a> {
|
||||
locals: &'a [ValueType],
|
||||
value_stack_limit: usize,
|
||||
frame_stack_limit: usize,
|
||||
func_type: &'a FunctionType,
|
||||
return_type: BlockType,
|
||||
) -> Self {
|
||||
FunctionValidationContext {
|
||||
module: module,
|
||||
@ -558,7 +586,7 @@ impl<'a> FunctionValidationContext<'a> {
|
||||
locals: locals,
|
||||
value_stack: StackWithLimit::with_limit(value_stack_limit),
|
||||
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
|
||||
return_type: Some(func_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)),
|
||||
return_type: Some(return_type),
|
||||
labels: HashMap::new(),
|
||||
}
|
||||
}
|
||||
@ -693,62 +721,6 @@ impl<'a> FunctionValidationContext<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
|
||||
if self.module.memories().get(idx as usize).is_none() {
|
||||
return Err(Error(format!("Memory at index {} doesn't exists", idx)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn require_table(&self, idx: u32, expected_type: TableElementType) -> Result<(), Error> {
|
||||
let table = match self.module.tables().get(idx as usize) {
|
||||
Some(table) => table,
|
||||
None => {
|
||||
return Err(Error(format!("Table at index {} doesn't exists", idx)));
|
||||
}
|
||||
};
|
||||
|
||||
if table.elem_type() != expected_type {
|
||||
return Err(Error(format!(
|
||||
"Table {} has element type {:?} while {:?} expected",
|
||||
idx,
|
||||
table.elem_type(),
|
||||
expected_type
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
|
||||
let ty_idx = match self.module.func_type_indexes().get(idx as usize) {
|
||||
Some(ty_idx) => *ty_idx,
|
||||
None => {
|
||||
return Err(Error(
|
||||
format!("Function at index {} doesn't exists", idx),
|
||||
));
|
||||
}
|
||||
};
|
||||
self.require_function_type(ty_idx)
|
||||
}
|
||||
|
||||
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
|
||||
let ty = match self.module.types().get(idx as usize) {
|
||||
Some(&Type::Function(ref func_ty)) => func_ty,
|
||||
None => {
|
||||
return Err(Error(
|
||||
format!("Type at index {} doesn't exists", idx),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let params = ty.params().to_vec();
|
||||
let return_ty = ty.return_type()
|
||||
.map(BlockType::Value)
|
||||
.unwrap_or(BlockType::NoResult);
|
||||
Ok((params, return_ty))
|
||||
}
|
||||
|
||||
pub fn function_labels(self) -> HashMap<usize, usize> {
|
||||
self.labels
|
||||
}
|
||||
|
@ -4,9 +4,11 @@ mod module;
|
||||
mod func;
|
||||
|
||||
use std::fmt;
|
||||
use elements::{Module, ResizableLimits, MemoryType, TableType, GlobalType, FunctionType, External, Opcode, ValueType};
|
||||
use std::iter::repeat;
|
||||
use elements::{Module, ResizableLimits, MemoryType, TableType, GlobalType, FunctionType, External, Opcode, ValueType, BlockType, Type};
|
||||
use common::stack;
|
||||
use self::module::ModuleContext;
|
||||
use self::func::{Validator, FunctionValidationContext};
|
||||
|
||||
pub struct Error(String);
|
||||
|
||||
@ -23,10 +25,49 @@ impl From<stack::Error> for Error {
|
||||
}
|
||||
|
||||
pub fn validate_module(module: &Module) -> Result<(), Error> {
|
||||
prepare_context(module).map(|_| ())
|
||||
let context = prepare_context(module)?;
|
||||
|
||||
let function_section_len = module
|
||||
.function_section()
|
||||
.map(|s| s.entries().len())
|
||||
.unwrap_or(0);
|
||||
let code_section_len = module
|
||||
.code_section()
|
||||
.map(|s| s.bodies().len())
|
||||
.unwrap_or(0);
|
||||
if function_section_len != code_section_len {
|
||||
return Err(Error(format!(
|
||||
"length of function section is {}, while len of code section is {}",
|
||||
function_section_len,
|
||||
code_section_len
|
||||
)));
|
||||
}
|
||||
|
||||
// validate every function body in user modules
|
||||
if function_section_len != 0 { // tests use invalid code
|
||||
let function_section = module.function_section().expect("function_section_len != 0; qed");
|
||||
let code_section = module.code_section().expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
||||
// check every function body
|
||||
for (index, function) in function_section.entries().iter().enumerate() {
|
||||
let function_labels = {
|
||||
let function_body = code_section.bodies().get(index as usize).ok_or(Error(format!("Missing body for function {}", index)))?;
|
||||
Validator::validate_function(&context, function, function_body).map_err(|e| {
|
||||
let Error(ref msg) = e;
|
||||
Error(format!("Function #{} validation error: {}", index, msg))
|
||||
})?;
|
||||
// context.function_labels()
|
||||
};
|
||||
// self.functions_labels.insert(index as u32, function_labels);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
|
||||
// TODO: Validate start
|
||||
// TODO: Validate imports
|
||||
// TODO: Validate exports
|
||||
|
||||
// Copy types from module as is.
|
||||
let types = module
|
||||
.type_section()
|
||||
@ -34,6 +75,8 @@ fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
|
||||
.unwrap_or_default();
|
||||
|
||||
// Fill elements with imported values.
|
||||
|
||||
// TODO: Use Func::type_ref?
|
||||
let mut func_type_indexes = Vec::new();
|
||||
let mut tables = Vec::new();
|
||||
let mut memories = Vec::new();
|
||||
|
@ -1,4 +1,6 @@
|
||||
use elements::{MemoryType, TableType, GlobalType, Type};
|
||||
use elements::{Opcode, BlockType, ValueType, TableElementType};
|
||||
use validation::Error;
|
||||
|
||||
pub struct ModuleContext {
|
||||
pub memories: Vec<MemoryType>,
|
||||
@ -28,4 +30,60 @@ impl ModuleContext {
|
||||
pub fn func_type_indexes(&self) -> &[u32] {
|
||||
&self.func_type_indexes
|
||||
}
|
||||
|
||||
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
|
||||
if self.memories().get(idx as usize).is_none() {
|
||||
return Err(Error(format!("Memory at index {} doesn't exists", idx)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn require_table(&self, idx: u32, expected_type: TableElementType) -> Result<(), Error> {
|
||||
let table = match self.tables().get(idx as usize) {
|
||||
Some(table) => table,
|
||||
None => {
|
||||
return Err(Error(format!("Table at index {} doesn't exists", idx)));
|
||||
}
|
||||
};
|
||||
|
||||
if table.elem_type() != expected_type {
|
||||
return Err(Error(format!(
|
||||
"Table {} has element type {:?} while {:?} expected",
|
||||
idx,
|
||||
table.elem_type(),
|
||||
expected_type
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
|
||||
let ty_idx = match self.func_type_indexes().get(idx as usize) {
|
||||
Some(ty_idx) => *ty_idx,
|
||||
None => {
|
||||
return Err(Error(
|
||||
format!("Function at index {} doesn't exists", idx),
|
||||
));
|
||||
}
|
||||
};
|
||||
self.require_function_type(ty_idx)
|
||||
}
|
||||
|
||||
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
|
||||
let ty = match self.types().get(idx as usize) {
|
||||
Some(&Type::Function(ref func_ty)) => func_ty,
|
||||
None => {
|
||||
return Err(Error(
|
||||
format!("Type at index {} doesn't exists", idx),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let params = ty.params().to_vec();
|
||||
let return_ty = ty.return_type()
|
||||
.map(BlockType::Value)
|
||||
.unwrap_or(BlockType::NoResult);
|
||||
Ok((params, return_ty))
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user