soroban_wasmi_validation/
lib.rs

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