mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 07:37:57 +00:00
Opt-out from fast instance reuse and foundation for other refactorings (#8394)
* Establish the runtime_blob module Seed it with the existing contents of the `util` module. * Port wasmtime mutable globals instrumentation into runtime blob APIs * Opt-out from fast instance reuse * Minor clean up * Spaces * Docs clean up * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Factor out the expects * Fix the suggestion Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -23,5 +23,5 @@
|
||||
|
||||
pub mod error;
|
||||
pub mod sandbox;
|
||||
pub mod util;
|
||||
pub mod wasm_runtime;
|
||||
pub mod runtime_blob;
|
||||
|
||||
+5
-50
@@ -1,6 +1,6 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
|
||||
// Copyright (C) 2021 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
|
||||
@@ -16,53 +16,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! A set of utilities for resetting a wasm instance to its initial state.
|
||||
|
||||
use crate::error::{self, Error};
|
||||
use super::RuntimeBlob;
|
||||
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<Self> {
|
||||
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<DataSegment> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
use parity_wasm::elements::Instruction;
|
||||
|
||||
/// This is a snapshot of data segments specialzied for a particular instantiation.
|
||||
///
|
||||
@@ -75,7 +32,7 @@ pub struct DataSegmentsSnapshot {
|
||||
|
||||
impl DataSegmentsSnapshot {
|
||||
/// Create a snapshot from the data segments from the module.
|
||||
pub fn take(module: &WasmModuleInfo) -> error::Result<Self> {
|
||||
pub fn take(module: &RuntimeBlob) -> error::Result<Self> {
|
||||
let data_segments = module
|
||||
.data_segments()
|
||||
.into_iter()
|
||||
@@ -105,9 +62,7 @@ impl DataSegmentsSnapshot {
|
||||
// if/when we gain those.
|
||||
return Err(Error::ImportedGlobalsUnsupported);
|
||||
}
|
||||
insn => {
|
||||
return Err(Error::InvalidInitializerExpression(format!("{:?}", insn)))
|
||||
}
|
||||
insn => return Err(Error::InvalidInitializerExpression(format!("{:?}", insn))),
|
||||
};
|
||||
|
||||
Ok((offset, contents))
|
||||
@@ -0,0 +1,110 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::RuntimeBlob;
|
||||
|
||||
/// Saved value of particular exported global.
|
||||
struct SavedValue<Global> {
|
||||
/// The handle of this global which can be used to refer to this global.
|
||||
handle: Global,
|
||||
/// The global value that was observed during the snapshot creation.
|
||||
value: sp_wasm_interface::Value,
|
||||
}
|
||||
|
||||
/// An adapter for a wasm module instance that is focused on getting and setting globals.
|
||||
pub trait InstanceGlobals {
|
||||
/// A handle to a global which can be used to get or set a global variable. This is supposed to
|
||||
/// be a lightweight handle, like an index or an Rc-like smart-pointer, which is cheap to clone.
|
||||
type Global: Clone;
|
||||
/// Get a handle to a global by it's export name.
|
||||
///
|
||||
/// The requested export is must exist in the exported list, and it should be a mutable global.
|
||||
fn get_global(&self, export_name: &str) -> Self::Global;
|
||||
/// Get the current value of the global.
|
||||
fn get_global_value(&self, global: &Self::Global) -> sp_wasm_interface::Value;
|
||||
/// Update the current value of the global.
|
||||
///
|
||||
/// The global behind the handle is guaranteed to be mutable and the value to be the same type
|
||||
/// as the global.
|
||||
fn set_global_value(&self, global: &Self::Global, value: sp_wasm_interface::Value);
|
||||
}
|
||||
|
||||
/// A set of exposed mutable globals.
|
||||
///
|
||||
/// This is set of globals required to create a [`GlobalsSnapshot`] and that are collected from
|
||||
/// a runtime blob that was instrumented by [`InstrumentModule::expose_mutable_globals`].
|
||||
///
|
||||
/// If the code wasn't instrumented then it would be empty and snapshot would do nothing.
|
||||
pub struct ExposedMutableGlobalsSet(Vec<String>);
|
||||
|
||||
impl ExposedMutableGlobalsSet {
|
||||
/// Collect the set from the given runtime blob. See the struct documentation for details.
|
||||
pub fn collect(runtime_blob: &RuntimeBlob) -> Self {
|
||||
let global_names = runtime_blob
|
||||
.exported_internal_global_names()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect();
|
||||
Self(global_names)
|
||||
}
|
||||
}
|
||||
|
||||
/// A snapshot of a global variables values. This snapshot can be later used for restoring the
|
||||
/// values to the preserved state.
|
||||
///
|
||||
/// Technically, a snapshot stores only values of mutable global variables. This is because
|
||||
/// immutable global variables always have the same values.
|
||||
///
|
||||
/// We take it from an instance rather from a module because the start function could potentially
|
||||
/// change any of the mutable global values.
|
||||
pub struct GlobalsSnapshot<Global>(Vec<SavedValue<Global>>);
|
||||
|
||||
impl<Global> GlobalsSnapshot<Global> {
|
||||
/// Take a snapshot of global variables for a given instance.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the instance doesn't correspond to the module from which the
|
||||
/// [`ExposedMutableGlobalsSet`] was collected.
|
||||
pub fn take<Instance>(mutable_globals: &ExposedMutableGlobalsSet, instance: &Instance) -> Self
|
||||
where
|
||||
Instance: InstanceGlobals<Global = Global>,
|
||||
{
|
||||
let global_names = &mutable_globals.0;
|
||||
let mut saved_values = Vec::with_capacity(global_names.len());
|
||||
|
||||
for global_name in global_names {
|
||||
let handle = instance.get_global(global_name);
|
||||
let value = instance.get_global_value(&handle);
|
||||
saved_values.push(SavedValue { handle, value });
|
||||
}
|
||||
|
||||
Self(saved_values)
|
||||
}
|
||||
|
||||
/// Apply the snapshot to the given instance.
|
||||
///
|
||||
/// This instance must be the same that was used for creation of this snapshot.
|
||||
pub fn apply<Instance>(&self, instance: &Instance)
|
||||
where
|
||||
Instance: InstanceGlobals<Global = Global>,
|
||||
{
|
||||
for saved_value in &self.0 {
|
||||
instance.set_global_value(&saved_value.handle, saved_value.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! This module allows for inspection and instrumentation, i.e. modifying the module to alter it's
|
||||
//! structure or behavior, of a wasm module.
|
||||
//!
|
||||
//! ## Instrumentation
|
||||
//!
|
||||
//! In ideal world, there would be no instrumentation. However, in the real world the execution
|
||||
//! engines we use are somewhat limited in their APIs or abilities.
|
||||
//!
|
||||
//! To give you some examples:
|
||||
//!
|
||||
//! - wasmi allows reaching to non-exported mutable globals so that we could reset them.
|
||||
//! Wasmtime doesn’t support that.
|
||||
//!
|
||||
//! We need to reset the globals because when we
|
||||
//! execute the Substrate Runtime, we do not drop and create the instance anew, instead
|
||||
//! we restore some selected parts of the state.
|
||||
//!
|
||||
//! - stack depth metering can be performed via instrumentation or deferred to the engine and say
|
||||
//! be added directly in machine code. Implementing this in machine code is rather cumbersome so
|
||||
//! instrumentation looks like a good solution.
|
||||
//!
|
||||
//! Stack depth metering is needed to make a wasm blob
|
||||
//! execution deterministic, which in turn is needed by the Parachain Validation Function in Polkadot.
|
||||
//!
|
||||
//! ## Inspection
|
||||
//!
|
||||
//! Inspection of a wasm module may be needed to extract some useful information, such as to extract
|
||||
//! data segment snapshot, which is helpful for quickly restoring the initial state of instances.
|
||||
//! Inspection can be also useful to prove that a wasm module possesses some properties, such as,
|
||||
//! is free of any floating point operations, which is a useful step towards making instances produced
|
||||
//! from such a module deterministic.
|
||||
|
||||
mod data_segments_snapshot;
|
||||
mod globals_snapshot;
|
||||
mod runtime_blob;
|
||||
|
||||
pub use data_segments_snapshot::DataSegmentsSnapshot;
|
||||
pub use globals_snapshot::{GlobalsSnapshot, ExposedMutableGlobalsSet, InstanceGlobals};
|
||||
pub use runtime_blob::RuntimeBlob;
|
||||
@@ -0,0 +1,93 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use parity_wasm::elements::{DataSegment, Module as RawModule, deserialize_buffer, serialize};
|
||||
|
||||
use crate::error::WasmError;
|
||||
|
||||
/// A bunch of information collected from a WebAssembly module.
|
||||
#[derive(Clone)]
|
||||
pub struct RuntimeBlob {
|
||||
raw_module: RawModule,
|
||||
}
|
||||
|
||||
impl RuntimeBlob {
|
||||
/// Create `RuntimeBlob` from the given wasm code.
|
||||
///
|
||||
/// Returns `Err` if the wasm code cannot be deserialized.
|
||||
pub fn new(wasm_code: &[u8]) -> Result<Self, WasmError> {
|
||||
let raw_module: RawModule = deserialize_buffer(wasm_code)
|
||||
.map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?;
|
||||
Ok(Self { raw_module })
|
||||
}
|
||||
|
||||
/// Extract the data segments from the given wasm code.
|
||||
pub(super) fn data_segments(&self) -> Vec<DataSegment> {
|
||||
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)
|
||||
}
|
||||
|
||||
/// Perform an instrumentation that makes sure that the mutable globals are exported.
|
||||
pub fn expose_mutable_globals(&mut self) {
|
||||
pwasm_utils::export_mutable_globals(&mut self.raw_module, "exported_internal_global");
|
||||
}
|
||||
|
||||
/// Returns an iterator of all globals which were exported by [`expose_mutable_globals`].
|
||||
pub(super) fn exported_internal_global_names<'module>(
|
||||
&'module self,
|
||||
) -> impl Iterator<Item = &'module str> {
|
||||
let exports = self
|
||||
.raw_module
|
||||
.export_section()
|
||||
.map(|es| es.entries())
|
||||
.unwrap_or(&[]);
|
||||
exports.iter().filter_map(|export| match export.internal() {
|
||||
parity_wasm::elements::Internal::Global(_)
|
||||
if export.field().starts_with("exported_internal_global") =>
|
||||
{
|
||||
Some(export.field())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes this runtime blob and serializes it.
|
||||
pub fn serialize(self) -> Vec<u8> {
|
||||
serialize(self.raw_module)
|
||||
.expect("serializing into a vec should succeed; qed")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user