WASM runtime switch to import memory (#4737)

* WASM runtime switch to import memory

Up to now runtimes have exported their memory. To unify it with
sandboxing, this pr switches runtimes to import memory as well.

From a functional perspective, exporting/importing memory makes no
difference to the runtime.

To provide backwards compatibility, WASM exported memory is still supported.

* Revert debug stuff

* Revert some stuff
This commit is contained in:
Bastian Köcher
2020-01-28 09:36:57 +01:00
committed by GitHub
parent 5c8743510e
commit 793a1eb053
15 changed files with 603 additions and 428 deletions
-100
View File
@@ -255,8 +255,6 @@ cfg_if! {
fn use_trie() -> u64;
fn benchmark_indirect_call() -> u64;
fn benchmark_direct_call() -> u64;
fn returns_mutable_static() -> u64;
fn allocates_huge_stack_array(trap: bool) -> Vec<u8>;
fn vec_with_capacity(size: u32) -> Vec<u8>;
/// Returns the initialized block number.
fn get_block_number() -> u64;
@@ -299,8 +297,6 @@ cfg_if! {
fn use_trie() -> u64;
fn benchmark_indirect_call() -> u64;
fn benchmark_direct_call() -> u64;
fn returns_mutable_static() -> u64;
fn allocates_huge_stack_array(trap: bool) -> Vec<u8>;
fn vec_with_capacity(size: u32) -> Vec<u8>;
/// Returns the initialized block number.
fn get_block_number() -> u64;
@@ -448,11 +444,6 @@ impl_opaque_keys! {
}
}
#[cfg(not(feature = "std"))]
/// Mutable static variables should be always observed to have
/// the initialized value at the start of a runtime call.
static mut MUTABLE_STATIC: u64 = 32;
cfg_if! {
if #[cfg(feature = "std")] {
impl_runtime_apis! {
@@ -558,14 +549,6 @@ cfg_if! {
(0..1000).fold(0, |p, i| p + benchmark_add_one(i))
}
fn returns_mutable_static() -> u64 {
unimplemented!("is not expected to be invoked from non-wasm builds");
}
fn allocates_huge_stack_array(_trap: bool) -> Vec<u8> {
unimplemented!("is not expected to be invoked from non-wasm builds");
}
fn vec_with_capacity(_size: u32) -> Vec<u8> {
unimplemented!("is not expected to be invoked from non-wasm builds");
}
@@ -753,41 +736,6 @@ cfg_if! {
(0..10000).fold(0, |p, i| p + benchmark_add_one(i))
}
fn returns_mutable_static() -> u64 {
unsafe {
MUTABLE_STATIC += 1;
MUTABLE_STATIC
}
}
fn allocates_huge_stack_array(trap: bool) -> Vec<u8> {
// Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB).
// This will just decrease (stacks in wasm32-u-u grow downwards) the stack
// pointer. This won't trap on the current compilers.
let mut data = [0u8; 1024 * 768];
// Then make sure we actually write something to it.
//
// If:
// 1. the stack area is placed at the beginning of the linear memory space, and
// 2. the stack pointer points to out-of-bounds area, and
// 3. a write is performed around the current stack pointer.
//
// then a trap should happen.
//
for (i, v) in data.iter_mut().enumerate() {
*v = i as u8; // deliberate truncation
}
if trap {
// There is a small chance of this to be pulled up in theory. In practice
// the probability of that is rather low.
panic!()
}
data.to_vec()
}
fn vec_with_capacity(size: u32) -> Vec<u8> {
Vec::with_capacity(size as usize)
}
@@ -970,54 +918,6 @@ mod tests {
use sp_state_machine::ExecutionStrategy;
use codec::Encode;
#[test]
fn returns_mutable_static() {
let client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::AlwaysWasm)
.build();
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.chain_info().best_number);
let ret = runtime_api.returns_mutable_static(&block_id).unwrap();
assert_eq!(ret, 33);
// We expect that every invocation will need to return the initial
// value plus one. If the value increases more than that then it is
// a sign that the wasm runtime preserves the memory content.
let ret = runtime_api.returns_mutable_static(&block_id).unwrap();
assert_eq!(ret, 33);
}
// If we didn't restore the wasm instance properly, on a trap the stack pointer would not be
// returned to its initial value and thus the stack space is going to be leaked.
//
// See https://github.com/paritytech/substrate/issues/2967 for details
#[test]
fn restoration_of_globals() {
// Allocate 32 pages (of 65536 bytes) which gives the runtime 2048KB of heap to operate on
// (plus some additional space unused from the initial pages requested by the wasm runtime
// module).
//
// The fixture performs 2 allocations of 768KB and this theoretically gives 1536KB, however, due
// to our allocator algorithm there are inefficiencies.
const REQUIRED_MEMORY_PAGES: u64 = 32;
let client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::AlwaysWasm)
.set_heap_pages(REQUIRED_MEMORY_PAGES)
.build();
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.chain_info().best_number);
// On the first invocation we allocate approx. 768KB (75%) of stack and then trap.
let ret = runtime_api.allocates_huge_stack_array(&block_id, true);
assert!(ret.is_err());
// On the second invocation we allocate yet another 768KB (75%) of stack
let ret = runtime_api.allocates_huge_stack_array(&block_id, false);
assert!(ret.is_ok());
}
#[test]
fn heap_pages_is_respected() {
// This tests that the on-chain HEAP_PAGES parameter is respected.