wasmi_validation/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(not(feature = "std"))]
4#[macro_use]
5extern crate alloc;
6#[cfg(feature = "std")]
7extern crate std as alloc;
8
9pub mod stack;
10
11/// Index of default linear memory.
12pub const DEFAULT_MEMORY_INDEX: u32 = 0;
13/// Index of default table.
14pub const DEFAULT_TABLE_INDEX: u32 = 0;
15
16/// Maximal number of pages that a wasm instance supports.
17pub const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;
18
19use alloc::{string::String, vec::Vec};
20use core::fmt;
21#[cfg(feature = "std")]
22use std::error;
23
24use self::context::ModuleContextBuilder;
25use parity_wasm::elements::{
26    BlockType,
27    ExportEntry,
28    External,
29    FuncBody,
30    GlobalEntry,
31    GlobalType,
32    InitExpr,
33    Instruction,
34    Internal,
35    MemoryType,
36    Module,
37    ResizableLimits,
38    TableType,
39    Type,
40    ValueType,
41};
42
43pub mod context;
44pub mod func;
45pub mod util;
46
47#[cfg(test)]
48mod tests;
49
50// TODO: Consider using a type other than String, because
51// of formatting machinary is not welcomed in substrate runtimes.
52#[derive(Debug)]
53pub struct Error(pub String);
54
55impl fmt::Display for Error {
56    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57        write!(f, "{}", self.0)
58    }
59}
60
61#[cfg(feature = "std")]
62impl error::Error for Error {
63    fn description(&self) -> &str {
64        &self.0
65    }
66}
67
68impl From<stack::Error> for Error {
69    fn from(e: stack::Error) -> Error {
70        Error(format!("Stack: {}", e))
71    }
72}
73
74pub trait Validator {
75    /// Custom inputs to the validator constructor.
76    type Input;
77    type Output;
78    type FuncValidator: FuncValidator;
79
80    fn new(module: &Module, input: Self::Input) -> Self;
81
82    fn func_validator_input(&mut self) -> <Self::FuncValidator as FuncValidator>::Input;
83
84    fn on_function_validated(
85        &mut self,
86        index: u32,
87        output: <<Self as Validator>::FuncValidator as FuncValidator>::Output,
88    );
89
90    fn finish(self) -> Self::Output;
91}
92
93pub trait FuncValidator {
94    /// Custom inputs to the function validator constructor.
95    type Input;
96
97    type Output;
98
99    fn new(ctx: &func::FunctionValidationContext, body: &FuncBody, input: Self::Input) -> Self;
100
101    fn next_instruction(
102        &mut self,
103        ctx: &mut func::FunctionValidationContext,
104        instruction: &Instruction,
105    ) -> Result<(), Error>;
106
107    fn finish(self, ctx: &func::FunctionValidationContext) -> Self::Output;
108}
109
110/// A module validator that just validates modules and produces no result.
111pub struct PlainValidator;
112
113impl Validator for PlainValidator {
114    type Input = ();
115    type Output = ();
116    type FuncValidator = PlainFuncValidator;
117    fn new(_module: &Module, _args: Self::Input) -> PlainValidator {
118        PlainValidator
119    }
120    fn func_validator_input(&mut self) -> <Self::FuncValidator as FuncValidator>::Input {}
121    fn on_function_validated(
122        &mut self,
123        _index: u32,
124        _output: <<Self as Validator>::FuncValidator as FuncValidator>::Output,
125    ) {
126    }
127    fn finish(self) {}
128}
129
130/// A function validator that just validates modules and produces no result.
131pub struct PlainFuncValidator;
132
133impl FuncValidator for PlainFuncValidator {
134    type Input = ();
135    type Output = ();
136
137    fn new(
138        _ctx: &func::FunctionValidationContext,
139        _body: &FuncBody,
140        _input: Self::Input,
141    ) -> PlainFuncValidator {
142        PlainFuncValidator
143    }
144
145    fn next_instruction(
146        &mut self,
147        ctx: &mut func::FunctionValidationContext,
148        instruction: &Instruction,
149    ) -> Result<(), Error> {
150        ctx.step(instruction)
151    }
152
153    fn finish(self, _ctx: &func::FunctionValidationContext) {}
154}
155
156pub fn validate_module<V: Validator>(
157    module: &Module,
158    input: <V as Validator>::Input,
159) -> Result<V::Output, Error> {
160    let mut context_builder = ModuleContextBuilder::new();
161    let mut imported_globals = Vec::new();
162    let mut validation = V::new(module, input);
163
164    // Copy types from module as is.
165    context_builder.set_types(
166        module
167            .type_section()
168            .map(|ts| {
169                ts.types()
170                    .iter()
171                    .map(|&Type::Function(ref ty)| ty)
172                    .cloned()
173                    .collect()
174            })
175            .unwrap_or_default(),
176    );
177
178    // Fill elements with imported values.
179    for import_entry in module
180        .import_section()
181        .map(|i| i.entries())
182        .unwrap_or_default()
183    {
184        match *import_entry.external() {
185            External::Function(idx) => context_builder.push_func_type_index(idx),
186            External::Table(ref table) => context_builder.push_table(*table),
187            External::Memory(ref memory) => context_builder.push_memory(*memory),
188            External::Global(ref global) => {
189                context_builder.push_global(*global);
190                imported_globals.push(*global);
191            }
192        }
193    }
194
195    // Concatenate elements with defined in the module.
196    if let Some(function_section) = module.function_section() {
197        for func_entry in function_section.entries() {
198            context_builder.push_func_type_index(func_entry.type_ref())
199        }
200    }
201    if let Some(table_section) = module.table_section() {
202        for table_entry in table_section.entries() {
203            validate_table_type(table_entry)?;
204            context_builder.push_table(*table_entry);
205        }
206    }
207    if let Some(mem_section) = module.memory_section() {
208        for mem_entry in mem_section.entries() {
209            validate_memory_type(mem_entry)?;
210            context_builder.push_memory(*mem_entry);
211        }
212    }
213    if let Some(global_section) = module.global_section() {
214        for global_entry in global_section.entries() {
215            validate_global_entry(global_entry, &imported_globals)?;
216            context_builder.push_global(*global_entry.global_type());
217        }
218    }
219
220    let context = context_builder.build();
221
222    let function_section_len = module
223        .function_section()
224        .map(|s| s.entries().len())
225        .unwrap_or(0);
226    let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0);
227    if function_section_len != code_section_len {
228        return Err(Error(format!(
229            "length of function section is {}, while len of code section is {}",
230            function_section_len, code_section_len
231        )));
232    }
233
234    // validate every function body in user modules
235    if function_section_len != 0 {
236        // tests use invalid code
237        let function_section = module
238            .function_section()
239            .expect("function_section_len != 0; qed");
240        let code_section = module
241            .code_section()
242            .expect("function_section_len != 0; function_section_len == code_section_len; qed");
243        // check every function body
244        for (index, function) in function_section.entries().iter().enumerate() {
245            let function_body = code_section
246                .bodies()
247                .get(index as usize)
248                .ok_or_else(|| Error(format!("Missing body for function {}", index)))?;
249            let func_validator_input = validation.func_validator_input();
250            let output = func::drive::<V::FuncValidator>(
251                &context,
252                function,
253                function_body,
254                func_validator_input,
255            )
256            .map_err(|Error(ref msg)| {
257                Error(format!(
258                    "Function #{} reading/validation error: {}",
259                    index, msg
260                ))
261            })?;
262            validation.on_function_validated(index as u32, output);
263        }
264    }
265
266    // validate start section
267    if let Some(start_fn_idx) = module.start_section() {
268        let (params, return_ty) = context.require_function(start_fn_idx)?;
269        if return_ty != BlockType::NoResult || !params.is_empty() {
270            return Err(Error(
271                "start function expected to have type [] -> []".into(),
272            ));
273        }
274    }
275
276    // validate export section
277    if let Some(export_section) = module.export_section() {
278        let mut export_names = export_section
279            .entries()
280            .iter()
281            .map(ExportEntry::field)
282            .collect::<Vec<_>>();
283
284        export_names.sort_unstable();
285
286        for (fst, snd) in export_names.iter().zip(export_names.iter().skip(1)) {
287            if fst == snd {
288                return Err(Error(format!("duplicate export {}", fst)));
289            }
290        }
291
292        for export in export_section.entries() {
293            match *export.internal() {
294                Internal::Function(function_index) => {
295                    context.require_function(function_index)?;
296                }
297                Internal::Global(global_index) => {
298                    context.require_global(global_index, None)?;
299                }
300                Internal::Memory(memory_index) => {
301                    context.require_memory(memory_index)?;
302                }
303                Internal::Table(table_index) => {
304                    context.require_table(table_index)?;
305                }
306            }
307        }
308    }
309
310    // validate import section
311    if let Some(import_section) = module.import_section() {
312        for import in import_section.entries() {
313            match *import.external() {
314                External::Function(function_type_index) => {
315                    context.require_function_type(function_type_index)?;
316                }
317                External::Global(_) => {}
318                External::Memory(ref memory_type) => {
319                    validate_memory_type(memory_type)?;
320                }
321                External::Table(ref table_type) => {
322                    validate_table_type(table_type)?;
323                }
324            }
325        }
326    }
327
328    // there must be no greater than 1 table in tables index space
329    if context.tables().len() > 1 {
330        return Err(Error(format!(
331            "too many tables in index space: {}",
332            context.tables().len()
333        )));
334    }
335
336    // there must be no greater than 1 linear memory in memory index space
337    if context.memories().len() > 1 {
338        return Err(Error(format!(
339            "too many memory regions in index space: {}",
340            context.memories().len()
341        )));
342    }
343
344    // use data section to initialize linear memory regions
345    if let Some(data_section) = module.data_section() {
346        for data_segment in data_section.entries() {
347            context.require_memory(data_segment.index())?;
348            let offset = data_segment
349                .offset()
350                .as_ref()
351                .ok_or_else(|| Error("passive memory segments are not supported".into()))?;
352            let init_ty = expr_const_type(offset, context.globals())?;
353            if init_ty != ValueType::I32 {
354                return Err(Error("segment offset should return I32".into()));
355            }
356        }
357    }
358
359    // use element section to fill tables
360    if let Some(element_section) = module.elements_section() {
361        for element_segment in element_section.entries() {
362            context.require_table(element_segment.index())?;
363            let offset = element_segment
364                .offset()
365                .as_ref()
366                .ok_or_else(|| Error("passive element segments are not supported".into()))?;
367            let init_ty = expr_const_type(offset, context.globals())?;
368            if init_ty != ValueType::I32 {
369                return Err(Error("segment offset should return I32".into()));
370            }
371
372            for function_index in element_segment.members() {
373                context.require_function(*function_index)?;
374            }
375        }
376    }
377
378    Ok(validation.finish())
379}
380
381fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
382    if let Some(maximum) = limits.maximum() {
383        if limits.initial() > maximum {
384            return Err(Error(format!(
385                "maximum limit {} is less than minimum {}",
386                maximum,
387                limits.initial()
388            )));
389        }
390    }
391    Ok(())
392}
393
394fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> {
395    let initial = memory_type.limits().initial();
396    let maximum: Option<u32> = memory_type.limits().maximum();
397    validate_memory(initial, maximum).map_err(Error)
398}
399
400pub fn validate_memory(initial: u32, maximum: Option<u32>) -> Result<(), String> {
401    if initial > LINEAR_MEMORY_MAX_PAGES {
402        return Err(format!(
403            "initial memory size must be at most {} pages",
404            LINEAR_MEMORY_MAX_PAGES
405        ));
406    }
407    if let Some(maximum) = maximum {
408        if initial > maximum {
409            return Err(format!(
410                "maximum limit {} is less than minimum {}",
411                maximum, initial,
412            ));
413        }
414
415        if maximum > LINEAR_MEMORY_MAX_PAGES {
416            return Err(format!(
417                "maximum memory size must be at most {} pages",
418                LINEAR_MEMORY_MAX_PAGES
419            ));
420        }
421    }
422    Ok(())
423}
424
425fn validate_table_type(table_type: &TableType) -> Result<(), Error> {
426    validate_limits(table_type.limits())
427}
428
429fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) -> Result<(), Error> {
430    let init = global_entry.init_expr();
431    let init_expr_ty = expr_const_type(init, globals)?;
432    if init_expr_ty != global_entry.global_type().content_type() {
433        return Err(Error(format!(
434            "Trying to initialize variable of type {:?} with value of type {:?}",
435            global_entry.global_type().content_type(),
436            init_expr_ty
437        )));
438    }
439    Ok(())
440}
441
442/// Returns type of this constant expression.
443fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<ValueType, Error> {
444    let code = init_expr.code();
445    if code.len() != 2 {
446        return Err(Error(
447            "Init expression should always be with length 2".into(),
448        ));
449    }
450    let expr_ty: ValueType = match code[0] {
451        Instruction::I32Const(_) => ValueType::I32,
452        Instruction::I64Const(_) => ValueType::I64,
453        Instruction::F32Const(_) => ValueType::F32,
454        Instruction::F64Const(_) => ValueType::F64,
455        Instruction::GetGlobal(idx) => match globals.get(idx as usize) {
456            Some(target_global) => {
457                if target_global.is_mutable() {
458                    return Err(Error(format!("Global {} is mutable", idx)));
459                }
460                target_global.content_type()
461            }
462            None => {
463                return Err(Error(format!(
464                    "Global {} doesn't exists or not yet defined",
465                    idx
466                )));
467            }
468        },
469        _ => return Err(Error("Non constant opcode in init expr".into())),
470    };
471    if code[1] != Instruction::End {
472        return Err(Error("Expression doesn't ends with `end` opcode".into()));
473    }
474    Ok(expr_ty)
475}