Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/rlimited-memory.rs
1685 views
1
// This test only works on Linux. It may be portable to MacOS as well,
2
// but the original author did not have a machine available to test it.
3
#![cfg(target_os = "linux")]
4
5
use wasmtime::*;
6
7
#[derive(Default)]
8
struct MemoryGrowFailureDetector {
9
current: usize,
10
desired: usize,
11
error: Option<String>,
12
}
13
14
impl ResourceLimiter for MemoryGrowFailureDetector {
15
fn memory_growing(
16
&mut self,
17
current: usize,
18
desired: usize,
19
_maximum: Option<usize>,
20
) -> Result<bool> {
21
self.current = current;
22
self.desired = desired;
23
Ok(true)
24
}
25
fn memory_grow_failed(&mut self, err: anyhow::Error) -> Result<()> {
26
self.error = Some(err.to_string());
27
Ok(())
28
}
29
fn table_growing(
30
&mut self,
31
_current: usize,
32
_desired: usize,
33
_maximum: Option<usize>,
34
) -> Result<bool> {
35
Ok(true)
36
}
37
}
38
39
#[test]
40
#[cfg_attr(miri, ignore)]
41
#[cfg_attr(asan, ignore)]
42
fn custom_limiter_detect_os_oom_failure() -> Result<()> {
43
if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
44
return Ok(());
45
}
46
47
// Skip this test if it looks like we're in a cross-compiled situation,
48
// and we're emulating this test for a different platform. In that
49
// scenario QEMU ignores the data rlimit, which this test relies on. See
50
// QEMU commits 5dfa88f7162f ("linux-user: do setrlimit selectively") and
51
// 055d92f8673c ("linux-user: do prlimit selectively") for more
52
// information.
53
if wasmtime_test_util::cargo_test_runner().is_some() {
54
return Ok(());
55
}
56
57
// Default behavior of on-demand memory allocation so that a
58
// memory grow will hit Linux for a larger mmap.
59
let mut config = Config::new();
60
config.wasm_reference_types(false);
61
let engine = Engine::new(&config)?;
62
let linker = Linker::new(&engine);
63
let module = Module::new(&engine, r#"(module (memory (export "m") 0))"#).unwrap();
64
65
// Ask Linux to limit this process to 256MiB of memory
66
let process_max_memory: usize = 256 * 1024 * 1024;
67
unsafe {
68
// limit process to 256MiB memory
69
let rlimit = libc::rlimit {
70
rlim_cur: 0,
71
rlim_max: process_max_memory as libc::rlim_t,
72
};
73
let res = libc::setrlimit(libc::RLIMIT_DATA, &rlimit);
74
assert_eq!(res, 0, "setrlimit failed: {res}");
75
};
76
77
let context = MemoryGrowFailureDetector::default();
78
79
let mut store = Store::new(&engine, context);
80
store.limiter(|s| s as &mut dyn ResourceLimiter);
81
let instance = linker.instantiate(&mut store, &module).unwrap();
82
let memory = instance.get_memory(&mut store, "m").unwrap();
83
84
// Small (640KiB) grow should succeed
85
memory.grow(&mut store, 10).unwrap();
86
assert!(store.data().error.is_none());
87
assert_eq!(store.data().current, 0);
88
assert_eq!(store.data().desired, 10 * 64 * 1024);
89
90
// Try to grow past the process's memory limit.
91
// This should fail.
92
let pages_exceeding_limit = process_max_memory / (64 * 1024);
93
let err_msg = memory
94
.grow(&mut store, pages_exceeding_limit as u64)
95
.unwrap_err()
96
.to_string();
97
assert!(
98
err_msg.starts_with("failed to grow memory"),
99
"unexpected error: {err_msg}"
100
);
101
102
assert_eq!(store.data().current, 10 * 64 * 1024);
103
assert_eq!(
104
store.data().desired,
105
(pages_exceeding_limit + 10) * 64 * 1024
106
);
107
// The memory_grow_failed hook should show Linux gave OOM:
108
let err_msg = store.data().error.as_ref().unwrap();
109
assert!(
110
err_msg.starts_with("Cannot allocate memory"),
111
"unexpected error: {err_msg}"
112
);
113
Ok(())
114
}
115
116