// This file is part of Substrate. // Copyright (C) 2020 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! A set of utilities for resetting a wasm instance to its initial state. use crate::error::{self, Error}; use std::mem; use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule}; /// A bunch of information collected from a WebAssembly module. pub struct WasmModuleInfo { raw_module: RawModule, } impl WasmModuleInfo { /// Create `WasmModuleInfo` from the given wasm code. /// /// Returns `None` if the wasm code cannot be deserialized. pub fn new(wasm_code: &[u8]) -> Option { let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?; Some(Self { raw_module }) } /// Extract the data segments from the given wasm code. /// /// Returns `Err` if the given wasm code cannot be deserialized. fn data_segments(&self) -> Vec { self.raw_module .data_section() .map(|ds| ds.entries()) .unwrap_or(&[]) .to_vec() } /// The number of globals defined in locally in this module. pub fn declared_globals_count(&self) -> u32 { self.raw_module .global_section() .map(|gs| gs.entries().len() as u32) .unwrap_or(0) } /// The number of imports of globals. pub fn imported_globals_count(&self) -> u32 { self.raw_module .import_section() .map(|is| is.globals() as u32) .unwrap_or(0) } } /// This is a snapshot of data segments specialzied for a particular instantiation. /// /// Note that this assumes that no mutable globals are used. #[derive(Clone)] pub struct DataSegmentsSnapshot { /// The list of data segments represented by (offset, contents). data_segments: Vec<(u32, Vec)>, } impl DataSegmentsSnapshot { /// Create a snapshot from the data segments from the module. pub fn take(module: &WasmModuleInfo) -> error::Result { let data_segments = module .data_segments() .into_iter() .map(|mut segment| { // Just replace contents of the segment since the segments will be discarded later // anyway. let contents = mem::replace(segment.value_mut(), vec![]); let init_expr = match segment.offset() { Some(offset) => offset.code(), // Return if the segment is passive None => return Err(Error::SharedMemUnsupported), }; // [op, End] if init_expr.len() != 2 { return Err(Error::InitializerHasTooManyExpressions); } let offset = match &init_expr[0] { Instruction::I32Const(v) => *v as u32, Instruction::GetGlobal(_) => { // In a valid wasm file, initializer expressions can only refer imported // globals. // // At the moment of writing the Substrate Runtime Interface does not provide // any globals. There is nothing that prevents us from supporting this // if/when we gain those. return Err(Error::ImportedGlobalsUnsupported); } insn => { return Err(Error::InvalidInitializerExpression(format!("{:?}", insn))) } }; Ok((offset, contents)) }) .collect::>>()?; Ok(Self { data_segments }) } /// Apply the given snapshot to a linear memory. /// /// Linear memory interface is represented by a closure `memory_set`. pub fn apply( &self, mut memory_set: impl FnMut(u32, &[u8]) -> Result<(), E>, ) -> Result<(), E> { for (offset, contents) in &self.data_segments { memory_set(*offset, contents)?; } Ok(()) } }