mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-22 19:21:59 +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)));
|
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
|
// use data section to initialize linear memory regions
|
||||||
if let Some(data_section) = self.module.data_section() {
|
if let Some(data_section) = self.module.data_section() {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use std::u32;
|
use std::u32;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::iter::repeat;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use elements::{Opcode, BlockType, ValueType, TableElementType};
|
use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody};
|
||||||
use elements::{FunctionType, Type};
|
use elements::{FunctionType, Type};
|
||||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||||
use validation::module::ModuleContext;
|
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).
|
/// Constant from wabt' validator.cc to skip alignment validation (not a part of spec).
|
||||||
const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
|
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.
|
/// Function validation context.
|
||||||
pub struct FunctionValidationContext<'a> {
|
pub struct FunctionValidationContext<'a> {
|
||||||
/// Wasm module
|
/// Wasm module
|
||||||
@ -56,9 +62,31 @@ pub enum InstructionOutcome {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Validator {
|
impl Validator {
|
||||||
pub fn validate_function(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result<(), Error> {
|
pub fn validate_function(
|
||||||
context.push_label(BlockFrameType::Function, block_type)?;
|
module: &ModuleContext,
|
||||||
Validator::validate_function_block(context, body)?;
|
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() {
|
while !context.frame_stack.is_empty() {
|
||||||
context.pop_label()?;
|
context.pop_label()?;
|
||||||
}
|
}
|
||||||
@ -378,7 +406,7 @@ impl Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.pop_value(ValueType::I32.into())?;
|
context.pop_value(ValueType::I32.into())?;
|
||||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||||
context.push_value(value_type)?;
|
context.push_value(value_type)?;
|
||||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
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(value_type)?;
|
||||||
context.pop_value(ValueType::I32.into())?;
|
context.pop_value(ValueType::I32.into())?;
|
||||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||||
@ -506,7 +534,7 @@ impl Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
|
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() {
|
for argument_type in argument_types.iter().rev() {
|
||||||
context.pop_value((*argument_type).into())?;
|
context.pop_value((*argument_type).into())?;
|
||||||
}
|
}
|
||||||
@ -517,10 +545,10 @@ impl Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
|
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())?;
|
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() {
|
for argument_type in argument_types.iter().rev() {
|
||||||
context.pop_value((*argument_type).into())?;
|
context.pop_value((*argument_type).into())?;
|
||||||
}
|
}
|
||||||
@ -531,13 +559,13 @@ impl Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
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())?;
|
context.push_value(ValueType::I32.into())?;
|
||||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
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.pop_value(ValueType::I32.into())?;
|
||||||
context.push_value(ValueType::I32.into())?;
|
context.push_value(ValueType::I32.into())?;
|
||||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||||
@ -550,7 +578,7 @@ impl<'a> FunctionValidationContext<'a> {
|
|||||||
locals: &'a [ValueType],
|
locals: &'a [ValueType],
|
||||||
value_stack_limit: usize,
|
value_stack_limit: usize,
|
||||||
frame_stack_limit: usize,
|
frame_stack_limit: usize,
|
||||||
func_type: &'a FunctionType,
|
return_type: BlockType,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
FunctionValidationContext {
|
FunctionValidationContext {
|
||||||
module: module,
|
module: module,
|
||||||
@ -558,7 +586,7 @@ impl<'a> FunctionValidationContext<'a> {
|
|||||||
locals: locals,
|
locals: locals,
|
||||||
value_stack: StackWithLimit::with_limit(value_stack_limit),
|
value_stack: StackWithLimit::with_limit(value_stack_limit),
|
||||||
frame_stack: StackWithLimit::with_limit(frame_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(),
|
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> {
|
pub fn function_labels(self) -> HashMap<usize, usize> {
|
||||||
self.labels
|
self.labels
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,11 @@ mod module;
|
|||||||
mod func;
|
mod func;
|
||||||
|
|
||||||
use std::fmt;
|
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 common::stack;
|
||||||
use self::module::ModuleContext;
|
use self::module::ModuleContext;
|
||||||
|
use self::func::{Validator, FunctionValidationContext};
|
||||||
|
|
||||||
pub struct Error(String);
|
pub struct Error(String);
|
||||||
|
|
||||||
@ -23,10 +25,49 @@ impl From<stack::Error> for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_module(module: &Module) -> Result<(), 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> {
|
fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
|
||||||
|
// TODO: Validate start
|
||||||
|
// TODO: Validate imports
|
||||||
|
// TODO: Validate exports
|
||||||
|
|
||||||
// Copy types from module as is.
|
// Copy types from module as is.
|
||||||
let types = module
|
let types = module
|
||||||
.type_section()
|
.type_section()
|
||||||
@ -34,6 +75,8 @@ fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Fill elements with imported values.
|
// Fill elements with imported values.
|
||||||
|
|
||||||
|
// TODO: Use Func::type_ref?
|
||||||
let mut func_type_indexes = Vec::new();
|
let mut func_type_indexes = Vec::new();
|
||||||
let mut tables = Vec::new();
|
let mut tables = Vec::new();
|
||||||
let mut memories = Vec::new();
|
let mut memories = Vec::new();
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use elements::{MemoryType, TableType, GlobalType, Type};
|
use elements::{MemoryType, TableType, GlobalType, Type};
|
||||||
|
use elements::{Opcode, BlockType, ValueType, TableElementType};
|
||||||
|
use validation::Error;
|
||||||
|
|
||||||
pub struct ModuleContext {
|
pub struct ModuleContext {
|
||||||
pub memories: Vec<MemoryType>,
|
pub memories: Vec<MemoryType>,
|
||||||
@ -28,4 +30,60 @@ impl ModuleContext {
|
|||||||
pub fn func_type_indexes(&self) -> &[u32] {
|
pub fn func_type_indexes(&self) -> &[u32] {
|
||||||
&self.func_type_indexes
|
&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