Add a method to Linker and flag to wasmtime-cli to trap unknown import funcs (#4312)
* Add a method to Linker and flag to wasmtime-cli to trap unknown import funcs Sometimes users have a Command module which imports functions unknown to the wasmtime-cli, but does not call them at runtime. This PR provides a convenience method on Linker to define all unknown import functions in a given Module as a trivial implementation which traps, and hooks this up to a new cli flag --trap-unknown-imports. * add cfg guards - func_new requires compiler (naturally)
This commit is contained in:
@@ -2,8 +2,8 @@ use crate::func::HostFunc;
|
|||||||
use crate::instance::InstancePre;
|
use crate::instance::InstancePre;
|
||||||
use crate::store::StoreOpaque;
|
use crate::store::StoreOpaque;
|
||||||
use crate::{
|
use crate::{
|
||||||
AsContextMut, Caller, Engine, Extern, Func, FuncType, ImportType, Instance, IntoFunc, Module,
|
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
|
||||||
StoreContextMut, Trap, Val, ValRaw,
|
IntoFunc, Module, StoreContextMut, Trap, Val, ValRaw,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
@@ -237,6 +237,48 @@ impl<T> Linker<T> {
|
|||||||
self
|
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`].
|
/// Defines a new item in this [`Linker`].
|
||||||
///
|
///
|
||||||
/// This method will add a new definition, by name, to this instance of
|
/// This method will add a new definition, by name, to this instance of
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ pub struct RunCommand {
|
|||||||
#[clap(long = "allow-unknown-exports")]
|
#[clap(long = "allow-unknown-exports")]
|
||||||
allow_unknown_exports: bool,
|
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.
|
/// Allow executing precompiled WebAssembly modules as `*.cwasm` files.
|
||||||
///
|
///
|
||||||
/// Note that this option is not safe to pass if the module being passed in
|
/// Note that this option is not safe to pass if the module being passed in
|
||||||
@@ -313,8 +318,13 @@ impl RunCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the wasm module binary either as `*.wat` or a raw binary.
|
// 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)?;
|
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
|
linker
|
||||||
.module(&mut *store, "", &module)
|
.module(&mut *store, "", &module)
|
||||||
.context(format!("failed to instantiate {:?}", self.module))?;
|
.context(format!("failed to instantiate {:?}", self.module))?;
|
||||||
|
|||||||
@@ -340,3 +340,40 @@ fn instance_pre() -> Result<()> {
|
|||||||
instance_pre.instantiate(&mut store)?;
|
instance_pre.instantiate(&mut store)?;
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user