fix(runtime): support new wasm opcodes by removing unused memory limit setting (#299)

remove unused max memory setting
This commit is contained in:
Valery Antopol 2023-03-17 15:34:55 +03:00 committed by GitHub
parent 04914dd291
commit b9dbf67376
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 254 additions and 473 deletions

517
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,6 @@ it-memory-traits = { workspace = true }
bytesize = "1.1.0"
multimap = "0.8.3"
boolinator = "2.4.0"
parity-wasm = "0.45.0"
pwasm-utils = "0.19.0"
once_cell = "1.16.0"
semver = "1.0.14"
serde = "1.0.147"

View File

@ -108,8 +108,6 @@ impl<WB: WasmBackend> MarineCore<WB> {
wasm_bytes: &[u8],
config: MModuleConfig<WB>,
) -> MResult<()> {
let _prepared_wasm_bytes =
crate::misc::prepare_module(wasm_bytes, config.max_heap_pages_count)?;
let module = MModule::new(
&name,
self.store.get_mut(),

View File

@ -20,13 +20,6 @@ use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum PrepareError {
/// Error that raises on a Wasm module validation.
#[error("validation error: {0}, probably module is malformed")]
ParseError(#[from] parity_wasm::elements::Error),
#[error(transparent)]
HeapBaseInvalidOrMissing(#[from] HeapBaseError),
#[error("overflow was happened while summation globals size '{globals_pages_count}' and heap size '{max_heap_pages_count}'")]
MemSizesOverflow {
globals_pages_count: u32,
@ -57,15 +50,3 @@ pub enum PrepareError {
provided: semver::Version,
},
}
#[derive(Debug, ThisError)]
pub enum HeapBaseError {
#[error("a Wasm module doesn't expose __heap_base entry")]
ExportNotFound,
#[error("__heap_base is initialized not by i32.const, but by a different set of instructions, that's unsupported")]
InitializationNotI32Const,
#[error("__heap_base has not a i32 type")]
WrongType,
}

View File

@ -15,12 +15,9 @@
*/
mod errors;
mod prepare;
mod version_checker;
pub(crate) use errors::HeapBaseError;
pub(crate) use errors::PrepareError;
pub(crate) use prepare::prepare_module;
pub(crate) use version_checker::check_sdk_version;
pub(crate) use version_checker::check_it_version;

View File

@ -1,101 +0,0 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Similar to
// https://github.com/paritytech/substrate/blob/master/srml/contracts/src/wasm/prepare.rs
// https://github.com/nearprotocol/nearcore/blob/master/runtime/near-vm-runner/src/prepare.rs
mod heap_base;
use super::PrepareResult;
use crate::misc::PrepareError;
use heap_base::get_heap_base;
use marine_utils::bytes_to_wasm_pages_ceil;
use parity_wasm::builder;
use parity_wasm::elements;
// not all clangs versions emits __heap_base, and this consts is a temporary solution
// until node has a dedicated config for that
const DEFAULT_GLOBALS_SIZE: u32 = 50;
struct ModuleBootstrapper {
module: elements::Module,
}
impl ModuleBootstrapper {
fn init(module_code: &[u8]) -> PrepareResult<Self> {
let module = elements::deserialize_buffer(module_code)?;
Ok(Self { module })
}
fn set_max_heap_pages_count(self, max_heap_pages_count: u32) -> PrepareResult<Self> {
use elements::MemoryType;
use elements::MemorySection;
let Self { mut module } = self;
let globals_pages_count = get_heap_base(&module)
.map(bytes_to_wasm_pages_ceil)
.unwrap_or(DEFAULT_GLOBALS_SIZE);
let max_mem_pages_count = globals_pages_count
.checked_add(max_heap_pages_count)
.ok_or(PrepareError::MemSizesOverflow {
globals_pages_count,
max_heap_pages_count,
})?;
// At now, there is could be only one memory section, so
// it needs just to extract previous initial page count,
// delete an old entry and add create a new one with updated limits
let mem_initial_size = match module.memory_section_mut() {
Some(section) => match section.entries_mut().pop() {
Some(entry) => entry.limits().initial(),
None => 0,
},
None => 0,
};
let mem_initial_size = std::cmp::min(mem_initial_size, max_mem_pages_count);
let memory_entry = MemoryType::new(mem_initial_size, Some(max_mem_pages_count));
let mut default_mem_section = MemorySection::default();
module
.memory_section_mut()
.unwrap_or(&mut default_mem_section)
.entries_mut()
.push(memory_entry);
let builder = builder::from_module(module);
let module = builder.build();
Ok(Self { module })
}
fn into_wasm(self) -> PrepareResult<Vec<u8>> {
elements::serialize(self.module).map_err(Into::into)
}
}
/// Prepares a Wasm module:
/// - extracts __heap_base global
/// - computes module max memory size by summation of heap and globals sizes
/// - sets computed value as max memory page count of a module
pub(crate) fn prepare_module(module: &[u8], max_heap_pages_count: u32) -> PrepareResult<Vec<u8>> {
ModuleBootstrapper::init(module)?
.set_max_heap_pages_count(max_heap_pages_count)?
.into_wasm()
}

View File

@ -1,80 +0,0 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::misc::HeapBaseError::*;
use parity_wasm::elements;
const HEAP_BASE_NAME: &str = "__heap_base";
type HResult<T> = std::result::Result<T, crate::misc::HeapBaseError>;
/// Return value of __heap_base.
pub(super) fn get_heap_base(wasm_module: &elements::Module) -> HResult<u32> {
let heap_base_index = find_global_name_index(wasm_module, HEAP_BASE_NAME)?;
let global_entry = find_global_by_index(wasm_module, heap_base_index as usize)?;
u32_from_global_entry(global_entry)
}
fn find_global_name_index(wasm_module: &elements::Module, name: &str) -> HResult<u32> {
use elements::Internal;
wasm_module
.export_section()
.and_then(|export_section| {
export_section
.entries()
.iter()
.find_map(|entry| match entry.internal() {
Internal::Global(index) if entry.field() == name => Some(*index),
_ => None,
})
})
.ok_or(ExportNotFound)
}
fn find_global_by_index(
wasm_module: &elements::Module,
index: usize,
) -> HResult<&elements::GlobalEntry> {
wasm_module
.global_section()
.and_then(|section| section.entries().get(index))
.ok_or(ExportNotFound)
}
fn u32_from_global_entry(global_entry: &elements::GlobalEntry) -> HResult<u32> {
use elements::Instruction;
use elements::ValueType;
let entry_type = global_entry.global_type().content_type();
if !matches!(entry_type, ValueType::I32) {
return Err(WrongType);
}
let init_expr = global_entry.init_expr().code();
// check that initialization expression consists of two instructions:
// i32.const <heap_base>
// end
if init_expr.len() != 2 {
return Err(InitializationNotI32Const);
}
match (&init_expr[0], &init_expr[1]) {
(Instruction::I32Const(value), Instruction::End) => Ok(*value as u32),
_ => Err(InitializationNotI32Const),
}
}

View File

@ -1,5 +1,5 @@
[toolchain]
channel = "nightly-2022-12-01"
channel = "nightly-2023-03-12"
targets = [
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",