Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a method to Linker and flag to wasmtime-cli to trap unknown import funcs #4312

Merged
merged 2 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions crates/wasmtime/src/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::func::HostFunc;
use crate::instance::InstancePre;
use crate::store::StoreOpaque;
use crate::{
AsContextMut, Caller, Engine, Extern, Func, FuncType, ImportType, Instance, IntoFunc, Module,
StoreContextMut, Trap, Val, ValRaw,
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
IntoFunc, Module, StoreContextMut, Trap, Val, ValRaw,
};
use anyhow::{anyhow, bail, Context, Result};
use log::warn;
Expand Down Expand Up @@ -237,6 +237,48 @@ impl<T> Linker<T> {
self
}

/// Implement any imports of the given [`Module`] with a function which traps.
///
/// By default a [`Linker`] will error when unknown imports are encountered
/// in a command module while using [`Linker::module`]. Use this function
/// when
///
/// This method can be used to allow unknown imports from command modules.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let engine = Engine::default();
/// # let module = Module::new(&engine, "(module (import \"unknown\" \"import\" (func)))")?;
/// # let mut store = Store::new(&engine, ());
/// let mut linker = Linker::new(&engine);
/// linker.define_unknown_imports_as_traps(&module)?;
/// linker.instantiate(&mut store, &module)?;
/// # Ok(())
/// # }
/// ```
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn define_unknown_imports_as_traps(&mut self, module: &Module) -> anyhow::Result<()> {
for import in module.imports() {
if self._get_by_import(&import).is_err() {
if let ExternType::Func(func_ty) = import.ty() {
let err_msg = format!(
"unknown import: `{}::{}` has not been defined",
import.module(),
import.name(),
);
self.func_new(import.module(), import.name(), func_ty, move |_, _, _| {
Err(Trap::new(err_msg.clone()))
})?;
}
}
}
Ok(())
}

/// Defines a new item in this [`Linker`].
///
/// This method will add a new definition, by name, to this instance of
Expand Down
12 changes: 11 additions & 1 deletion src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ pub struct RunCommand {
#[clap(long = "allow-unknown-exports")]
allow_unknown_exports: bool,

/// Allow the main module to import unknown functions, using an
/// implementation that immediately traps, when running commands.
#[clap(long = "trap-unknown-imports")]
trap_unknown_imports: bool,

/// Allow executing precompiled WebAssembly modules as `*.cwasm` files.
///
/// Note that this option is not safe to pass if the module being passed in
Expand Down Expand Up @@ -313,8 +318,13 @@ impl RunCommand {
}

// Read the wasm module binary either as `*.wat` or a raw binary.
// Use "" as a default module name.
let module = self.load_module(linker.engine(), &self.module)?;
// The main module might be allowed to have unknown imports, which
// should be defined as traps:
if self.trap_unknown_imports {
linker.define_unknown_imports_as_traps(&module)?;
}
// Use "" as a default module name.
linker
.module(&mut *store, "", &module)
.context(format!("failed to instantiate {:?}", self.module))?;
Expand Down
37 changes: 37 additions & 0 deletions tests/all/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,40 @@ fn instance_pre() -> Result<()> {
instance_pre.instantiate(&mut store)?;
Ok(())
}

#[test]
fn test_trapping_unknown_import() -> Result<()> {
const WAT: &str = r#"
(module
(type $t0 (func))
(import "" "imp" (func $.imp (type $t0)))
(func $run call $.imp)
(func $other)
(export "run" (func $run))
(export "other" (func $other))
)
"#;

let mut store = Store::<()>::default();
let module = Module::new(store.engine(), WAT).expect("failed to create module");
let mut linker = Linker::new(store.engine());

linker.define_unknown_imports_as_traps(&module)?;
let instance = linker.instantiate(&mut store, &module)?;

// "run" calls an import function which will not be defined, so it should trap
let run_func = instance
.get_func(&mut store, "run")
.expect("expected a run func in the module");

assert!(run_func.call(&mut store, &[], &mut []).is_err());

// "other" does not call the import function, so it should not trap
let other_func = instance
.get_func(&mut store, "other")
.expect("expected an other func in the module");

other_func.call(&mut store, &[], &mut [])?;

Ok(())
}