Skip to content

Commit

Permalink
Merge #95
Browse files Browse the repository at this point in the history
95: Remove warnings and add pages limitation r=adrien-zinger a=adrien-zinger

Each instance has now a limit of possible pages to require from the wasmer vm.

Also fix some little warnings

Co-authored-by: Adrien Zinger <zinger.ad@gmail.com>
  • Loading branch information
bors[bot] and adrien-zinger committed Feb 7, 2022
2 parents 2dd2321 + 38a53ab commit 8133475
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 5 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ wasmer-middlewares = "2.1.1"
cornetto = "0.1.0"
base64 = "0.13.0"
serial_test = "0.5.1"
loupe = "0.1.3"
15 changes: 10 additions & 5 deletions src/execution_impl.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
use crate::abi_impl::*;
use crate::env::{assembly_script_abort, get_remaining_points_for_instance, Env};
use crate::settings;
use crate::types::{Interface, Response};
use crate::{abi_impl::*, tunable_memory::LimitingTunables};
use crate::{
env::{assembly_script_abort, get_remaining_points_for_instance, Env},
settings::max_number_of_pages,
};
use anyhow::{bail, Result};
use as_ffi_bindings::{Read as ASRead, StringPtr, Write as ASWrite};
use std::sync::Arc;
use wasmer::wasmparser::Operator;
use wasmer::{
imports, CompilerConfig, Function, ImportObject, Instance, Module, Store, Universal, Val,
};
use wasmer::{wasmparser::Operator, BaseTunables, Pages, Target};
use wasmer_compiler_singlepass::Singlepass;
use wasmer_middlewares::metering::{self, MeteringPoints};
use wasmer_middlewares::Metering;

/// Create an instance of VM from a module with a given interface, an operation
/// number limit and a webassembly module
fn create_instance(limit: u64, module: &[u8], interface: &dyn Interface) -> Result<Instance> {
let base = BaseTunables::for_target(&Target::default());
let tunables = LimitingTunables::new(base, Pages(max_number_of_pages()));
let metering = Arc::new(Metering::new(limit, |_: &Operator| -> u64 { 1 }));
let mut compiler_config = Singlepass::new();
compiler_config.push_middleware(metering);
let store = Store::new(&Universal::new(compiler_config).engine());
let store = Store::new_with_tunables(&Universal::new(compiler_config).engine(), tunables);
let env = Env::new(interface);
let resolver: ImportObject = imports! {
"env" => {
Expand Down Expand Up @@ -49,7 +54,7 @@ fn create_instance(limit: u64, module: &[u8], interface: &dyn Interface) -> Resu
"assembly_script_address_from_public_key" => Function::new_native_with_env(&store, env.clone(), assembly_script_address_from_public_key),
"assembly_script_unsafe_random" => Function::new_native_with_env(&store, env.clone(), assembly_script_unsafe_random),
"assembly_script_get_call_coins" => Function::new_native_with_env(&store, env.clone(), assembly_script_get_call_coins),
"assembly_script_get_time" => Function::new_native_with_env(&store, env.clone(), assembly_script_get_time),
"assembly_script_get_time" => Function::new_native_with_env(&store, env, assembly_script_get_time),
},
};
let module = Module::new(&store, &module)?;
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod abi_impl;
mod env;
mod execution_impl;
mod settings;
mod tunable_memory;
mod types;

pub use execution_impl::run;
Expand Down
6 changes: 6 additions & 0 deletions src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ struct Metering {
pub get_time: u64,
#[cornetto(const, 100)]
pub get_call_coins: u64,
#[cornetto(const, 24)]
pub max_number_of_pages: u32,
}

pub(crate) fn metering_call() -> u64 {
Expand Down Expand Up @@ -159,6 +161,10 @@ pub(crate) fn metering_get_time() -> u64 {
METERING.get_time()
}

pub(crate) fn max_number_of_pages() -> u32 {
METERING.max_number_of_pages()
}

#[cfg(test)]
pub(crate) fn set_metering(call_price: u64) {
METERING._reset(call_price);
Expand Down
132 changes: 132 additions & 0 deletions src/tunable_memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use std::ptr::NonNull;
use std::sync::Arc;

use loupe::MemoryUsage;
use wasmer::{
vm::{self, MemoryError, MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition},
MemoryType, Pages, TableType, Tunables,
};

///! Limitation of the memory

/// A custom tunables that allows you to set a memory limit.
///
/// After adjusting the memory limits, it delegates all other logic
/// to the base tunables.
#[derive(MemoryUsage)]
pub struct LimitingTunables<T: Tunables> {
/// The maximum a linear memory is allowed to be (in Wasm pages, 64 KiB each).
/// Since Wasmer ensures there is only none or one memory, this is practically
/// an upper limit for the guest memory.
limit: Pages,
/// The base implementation we delegate all the logic to
base: T,
}

impl<T: Tunables> LimitingTunables<T> {
pub fn new(base: T, limit: Pages) -> Self {
Self { limit, base }
}

/// Takes an input memory type as requested by the guest and sets
/// a maximum if missing. The resulting memory type is final if
/// valid. However, this can produce invalid types, such that
/// validate_memory must be called before creating the memory.
fn adjust_memory(&self, requested: &MemoryType) -> MemoryType {
let mut adjusted = *requested;
if requested.maximum.is_none() {
adjusted.maximum = Some(self.limit);
}
adjusted
}

/// Ensures the a given memory type does not exceed the memory limit.
/// Call this after adjusting the memory.
fn validate_memory(&self, ty: &MemoryType) -> Result<(), MemoryError> {
if ty.minimum > self.limit {
return Err(MemoryError::Generic(
"Minimum exceeds the allowed memory limit".to_string(),
));
}

if let Some(max) = ty.maximum {
if max > self.limit {
return Err(MemoryError::Generic(
"Maximum exceeds the allowed memory limit".to_string(),
));
}
} else {
return Err(MemoryError::Generic("Maximum unset".to_string()));
}

Ok(())
}
}

impl<T: Tunables> Tunables for LimitingTunables<T> {
/// Construct a `MemoryStyle` for the provided `MemoryType`
///
/// Delegated to base.
fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
let adjusted = self.adjust_memory(memory);
self.base.memory_style(&adjusted)
}

/// Construct a `TableStyle` for the provided `TableType`
///
/// Delegated to base.
fn table_style(&self, table: &TableType) -> TableStyle {
self.base.table_style(table)
}

/// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`].
///
/// The requested memory type is validated, adjusted to the limited and then passed to base.
fn create_host_memory(
&self,
ty: &MemoryType,
style: &MemoryStyle,
) -> Result<Arc<dyn vm::Memory>, MemoryError> {
let adjusted = self.adjust_memory(ty);
self.validate_memory(&adjusted)?;
self.base.create_host_memory(&adjusted, style)
}

/// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
///
/// Delegated to base.
unsafe fn create_vm_memory(
&self,
ty: &MemoryType,
style: &MemoryStyle,
vm_definition_location: NonNull<VMMemoryDefinition>,
) -> Result<Arc<dyn vm::Memory>, MemoryError> {
let adjusted = self.adjust_memory(ty);
self.validate_memory(&adjusted)?;
self.base
.create_vm_memory(&adjusted, style, vm_definition_location)
}

/// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
///
/// Delegated to base.
fn create_host_table(
&self,
ty: &TableType,
style: &TableStyle,
) -> Result<Arc<dyn vm::Table>, String> {
self.base.create_host_table(ty, style)
}

/// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
///
/// Delegated to base.
unsafe fn create_vm_table(
&self,
ty: &TableType,
style: &TableStyle,
vm_definition_location: NonNull<VMTableDefinition>,
) -> Result<Arc<dyn vm::Table>, String> {
self.base.create_vm_table(ty, style, vm_definition_location)
}
}

0 comments on commit 8133475

Please sign in to comment.