unc_vm_runner/prepare/
prepare_v0.rs

1//! Legacy validation for very old protocol versions.
2
3use crate::logic::errors::PrepareError;
4use crate::logic::Config;
5use parity_wasm_41::builder;
6use parity_wasm_41::elements::{self, External, MemorySection, Type};
7use pwasm_utils_12 as pwasm_utils;
8
9pub(crate) fn prepare_contract(
10    original_code: &[u8],
11    config: &Config,
12) -> Result<Vec<u8>, PrepareError> {
13    ContractModule::init(original_code, config)?
14        .standardize_mem()
15        .ensure_no_internal_memory()?
16        .inject_gas_metering()?
17        .inject_stack_height_metering()?
18        .scan_imports()?
19        .into_wasm_code()
20}
21
22struct ContractModule<'a> {
23    module: elements::Module,
24    config: &'a Config,
25}
26
27impl<'a> ContractModule<'a> {
28    fn init(original_code: &[u8], config: &'a Config) -> Result<Self, PrepareError> {
29        let module = elements::deserialize_buffer(original_code).map_err(|e| {
30            tracing::debug!(err=?e, "parity_wasm_41 failed decoding a contract");
31            PrepareError::Deserialization
32        })?;
33        Ok(ContractModule { module, config })
34    }
35
36    fn standardize_mem(self) -> Self {
37        let Self { mut module, config } = self;
38
39        let mut tmp = MemorySection::default();
40
41        module.memory_section_mut().unwrap_or(&mut tmp).entries_mut().pop();
42
43        let entry = elements::MemoryType::new(
44            config.limit_config.initial_memory_pages,
45            Some(config.limit_config.max_memory_pages),
46        );
47
48        let mut builder = builder::from_module(module);
49        builder.push_import(elements::ImportEntry::new(
50            "env".to_string(),
51            "memory".to_string(),
52            elements::External::Memory(entry),
53        ));
54
55        Self { module: builder.build(), config }
56    }
57
58    /// Ensures that module doesn't declare internal memories.
59    ///
60    /// In this runtime we only allow wasm module to import memory from the environment.
61    /// Memory section contains declarations of internal linear memories, so if we find one
62    /// we reject such a module.
63    fn ensure_no_internal_memory(self) -> Result<Self, PrepareError> {
64        if self.module.memory_section().map_or(false, |ms| !ms.entries().is_empty()) {
65            Err(PrepareError::InternalMemoryDeclared)
66        } else {
67            Ok(self)
68        }
69    }
70
71    fn inject_gas_metering(self) -> Result<Self, PrepareError> {
72        let Self { module, config } = self;
73        // Free config, no need for gas metering.
74        if config.regular_op_cost == 0 {
75            return Ok(Self { module, config });
76        }
77        let gas_rules = pwasm_utils::rules::Set::new(1, Default::default())
78            .with_grow_cost(config.grow_mem_cost);
79        let module = pwasm_utils::inject_gas_counter(module, &gas_rules)
80            .map_err(|_| PrepareError::GasInstrumentation)?;
81        Ok(Self { module, config })
82    }
83
84    fn inject_stack_height_metering(self) -> Result<Self, PrepareError> {
85        let Self { module, config } = self;
86        let module =
87            pwasm_utils::stack_height::inject_limiter(module, config.limit_config.max_stack_height)
88                .map_err(|_| PrepareError::StackHeightInstrumentation)?;
89        Ok(Self { module, config })
90    }
91
92    /// Scan an import section if any.
93    ///
94    /// This accomplishes two tasks:
95    ///
96    /// - checks any imported function against defined host functions set, incl.
97    ///   their signatures.
98    /// - if there is a memory import, returns it's descriptor
99    fn scan_imports(self) -> Result<Self, PrepareError> {
100        let Self { module, config } = self;
101
102        let types = module.type_section().map(elements::TypeSection::types).unwrap_or(&[]);
103        let import_entries =
104            module.import_section().map(elements::ImportSection::entries).unwrap_or(&[]);
105
106        let mut imported_mem_type = None;
107
108        for import in import_entries {
109            if import.module() != "env" {
110                // This import tries to import something from non-"env" module,
111                // but all imports are located in "env" at the moment.
112                return Err(PrepareError::Instantiate);
113            }
114
115            let type_idx = match *import.external() {
116                External::Function(ref type_idx) => type_idx,
117                External::Memory(ref memory_type) => {
118                    imported_mem_type = Some(memory_type);
119                    continue;
120                }
121                _ => continue,
122            };
123
124            let Type::Function(ref _func_ty) =
125                types.get(*type_idx as usize).ok_or(PrepareError::Instantiate)?;
126
127            // TODO: Function type check with Env
128            /*
129
130            let ext_func = env
131                .funcs
132                .get(import.field().as_bytes())
133                .ok_or_else(|| Error::Instantiate)?;
134            if !ext_func.func_type_matches(func_ty) {
135                return Err(Error::Instantiate);
136            }
137            */
138        }
139        if let Some(memory_type) = imported_mem_type {
140            // Inspect the module to extract the initial and maximum page count.
141            let limits = memory_type.limits();
142            if limits.initial() != config.limit_config.initial_memory_pages
143                || limits.maximum() != Some(config.limit_config.max_memory_pages)
144            {
145                return Err(PrepareError::Memory);
146            }
147        } else {
148            return Err(PrepareError::Memory);
149        };
150        Ok(Self { module, config })
151    }
152
153    fn into_wasm_code(self) -> Result<Vec<u8>, PrepareError> {
154        elements::serialize(self.module).map_err(|_| PrepareError::Serialization)
155    }
156}