#![cfg(target_os = "linux")]
use wasmtime::*;
#[derive(Default)]
struct MemoryGrowFailureDetector {
current: usize,
desired: usize,
error: Option<String>,
}
impl ResourceLimiter for MemoryGrowFailureDetector {
fn memory_growing(
&mut self,
current: usize,
desired: usize,
_maximum: Option<usize>,
) -> Result<bool> {
self.current = current;
self.desired = desired;
Ok(true)
}
fn memory_grow_failed(&mut self, err: anyhow::Error) -> Result<()> {
self.error = Some(err.to_string());
Ok(())
}
fn table_growing(
&mut self,
_current: usize,
_desired: usize,
_maximum: Option<usize>,
) -> Result<bool> {
Ok(true)
}
}
#[test]
#[cfg_attr(miri, ignore)]
#[cfg_attr(asan, ignore)]
fn custom_limiter_detect_os_oom_failure() -> Result<()> {
if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
return Ok(());
}
if wasmtime_test_util::cargo_test_runner().is_some() {
return Ok(());
}
let mut config = Config::new();
config.wasm_reference_types(false);
let engine = Engine::new(&config)?;
let linker = Linker::new(&engine);
let module = Module::new(&engine, r#"(module (memory (export "m") 0))"#).unwrap();
let process_max_memory: usize = 256 * 1024 * 1024;
unsafe {
let rlimit = libc::rlimit {
rlim_cur: 0,
rlim_max: process_max_memory as libc::rlim_t,
};
let res = libc::setrlimit(libc::RLIMIT_DATA, &rlimit);
assert_eq!(res, 0, "setrlimit failed: {res}");
};
let context = MemoryGrowFailureDetector::default();
let mut store = Store::new(&engine, context);
store.limiter(|s| s as &mut dyn ResourceLimiter);
let instance = linker.instantiate(&mut store, &module).unwrap();
let memory = instance.get_memory(&mut store, "m").unwrap();
memory.grow(&mut store, 10).unwrap();
assert!(store.data().error.is_none());
assert_eq!(store.data().current, 0);
assert_eq!(store.data().desired, 10 * 64 * 1024);
let pages_exceeding_limit = process_max_memory / (64 * 1024);
let err_msg = memory
.grow(&mut store, pages_exceeding_limit as u64)
.unwrap_err()
.to_string();
assert!(
err_msg.starts_with("failed to grow memory"),
"unexpected error: {err_msg}"
);
assert_eq!(store.data().current, 10 * 64 * 1024);
assert_eq!(
store.data().desired,
(pages_exceeding_limit + 10) * 64 * 1024
);
let err_msg = store.data().error.as_ref().unwrap();
assert!(
err_msg.starts_with("Cannot allocate memory"),
"unexpected error: {err_msg}"
);
Ok(())
}