Skip to content

Commit 8e4a224

Browse files
authored
compat.wamr: no-hw-bound-check, instruction-metering and thread-mgr features (#506)
Three runtime build options as features, with a test member that stops an endless guest loop by instruction budget and by wasm_runtime_terminate() from another thread. thread-mgr also needs its directory in the base include_dirs, and on openkal-musl a target_cfg define, because openkal has no sigaction for the wakeup handler.
1 parent 56990ac commit 8e4a224

4 files changed

Lines changed: 152 additions & 0 deletions

File tree

‎mcpp.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ members = [
182182
"tests/examples/vulkan-memory-allocator",
183183
"tests/examples/wamr",
184184
"tests/examples/wamr-features",
185+
"tests/examples/wamr-runtime-features",
185186
"tests/examples/quickjs-ng",
186187
]
187188

‎pkgs/c/compat.wamr.lua‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@
8181
-- compat.recastnavigation's DT_POLYREF64 out of the feature table does not
8282
-- apply here.
8383
--
84+
-- `no-hw-bound-check`, `instruction-metering` and `thread-mgr` select runtime
85+
-- build options rather than a guest library. They are defines of the same kind:
86+
-- neither wasm_export.h nor wasm_c_api.h branches on any of them.
87+
--
8488
-- ─────────────────────────────────────────────────────────────────────────
8589
-- CN MIRROR
8690
--
@@ -140,6 +144,8 @@ package = {
140144
"*/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include",
141145
"*/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src",
142146
"*/core/shared/platform/common/libc-util",
147+
-- wasm_c_api.c includes thread_manager.h by bare name under thread-mgr
148+
"*/core/iwasm/libraries/thread-mgr",
143149
"mcpp_generated/include",
144150
},
145151

@@ -165,6 +171,14 @@ package = {
165171
ldflags = { "-lpthread", "-lm" },
166172
},
167173

174+
target_cfg = {
175+
-- openkal has no sigaction: with thread-mgr, wasm_runtime_init cannot
176+
-- install its wakeup handler and returns false
177+
["cfg(all(kernel-abi = \"openkal\", c-abi = \"musl\"))"] = {
178+
cflags = { "-DWASM_DISABLE_WAKEUP_BLOCKING_OP=1" },
179+
},
180+
},
181+
168182
generated_files = {
169183
["mcpp_generated/include/mcpp_wamr_config.h"] =
170184
[==[
@@ -239,6 +253,18 @@ package = {
239253
targets = { ["wamr"] = { kind = "lib" } },
240254

241255
features = {
256+
-- software bound checks only: no guard pages, sigaltstack or SIGSEGV handler
257+
["no-hw-bound-check"] = {
258+
defines = { "WASM_DISABLE_HW_BOUND_CHECK=1" },
259+
},
260+
["instruction-metering"] = {
261+
defines = { "WASM_ENABLE_INSTRUCTION_METERING=1" },
262+
},
263+
-- wasm_runtime_terminate() from another thread stops a running guest
264+
["thread-mgr"] = {
265+
sources = { "*/core/iwasm/libraries/thread-mgr/*.c" },
266+
defines = { "WASM_ENABLE_THREAD_MGR=1" },
267+
},
242268
-- Built-in libc wrappers exported to the guest under `env`.
243269
["libc-builtin"] = {
244270
sources = { "*/core/iwasm/libraries/libc-builtin/*.c" },
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# WAMR execution-control features: software bound checks, instruction
2+
# metering and the thread manager. The base member (tests/examples/wamr) does
3+
# not request them.
4+
[package]
5+
name = "wamr-runtime-features-tests"
6+
version = "0.1.0"
7+
8+
[target.'cfg(linux)'.dependencies.compat]
9+
wamr = { version = "2.4.5", features = ["no-hw-bound-check", "instruction-metering", "thread-mgr"] }
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// compat.wamr with `no-hw-bound-check`, `instruction-metering` and
2+
// `thread-mgr`: an infinite loop in the guest is stopped once by the
3+
// instruction budget and once by wasm_runtime_terminate() from another thread.
4+
#ifdef __linux__
5+
6+
#include <wasm_export.h>
7+
8+
#include <pthread.h>
9+
#include <unistd.h>
10+
11+
#include <atomic>
12+
#include <cstdio>
13+
#include <cstdlib>
14+
#include <cstring>
15+
16+
namespace {
17+
18+
// (module
19+
// (func (export "spin") (loop (br 0)))
20+
// (func (export "add") (param i32 i32) (result i32)
21+
// local.get 0 local.get 1 i32.add))
22+
const unsigned char kModule[] = {
23+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
24+
0x01, 0x0a, 0x02, 0x60, 0x00, 0x00, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f,
25+
0x03, 0x03, 0x02, 0x00, 0x01,
26+
0x07, 0x0e, 0x02, 0x04, 's', 'p', 'i', 'n', 0x00, 0x00,
27+
0x03, 'a', 'd', 'd', 0x00, 0x01,
28+
0x0a, 0x11, 0x02, 0x07, 0x00, 0x03, 0x40, 0x0c, 0x00, 0x0b, 0x0b,
29+
0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
30+
};
31+
32+
wasm_module_inst_t g_inst;
33+
wasm_exec_env_t g_env;
34+
std::atomic<bool> g_returned{ false };
35+
36+
bool call(const char *name, uint32_t *argv, uint32_t argc)
37+
{
38+
return wasm_runtime_call_wasm(g_env, wasm_runtime_lookup_function(g_inst, name), argc, argv);
39+
}
40+
41+
bool exception_has(const char *text)
42+
{
43+
const char *ex = wasm_runtime_get_exception(g_inst);
44+
std::printf(" exception: %s\n", ex ? ex : "(none)");
45+
return ex && std::strstr(ex, text);
46+
}
47+
48+
bool usable_again()
49+
{
50+
wasm_runtime_clear_exception(g_inst);
51+
uint32_t argv[2] = { 2, 3 };
52+
return call("add", argv, 2) && argv[0] == 5;
53+
}
54+
55+
// Repeats until the call has returned, so a terminate that lands before the
56+
// guest started running is not lost.
57+
void *terminate_until_returned(void *)
58+
{
59+
while (!g_returned) {
60+
usleep(100 * 1000);
61+
wasm_runtime_terminate(g_inst);
62+
}
63+
return nullptr;
64+
}
65+
66+
} // namespace
67+
68+
int main()
69+
{
70+
alarm(60);
71+
72+
RuntimeInitArgs init;
73+
std::memset(&init, 0, sizeof init);
74+
init.mem_alloc_type = Alloc_With_System_Allocator;
75+
if (!wasm_runtime_full_init(&init)) {
76+
std::puts("wasm_runtime_full_init failed");
77+
return 1;
78+
}
79+
80+
char err[128] = { 0 };
81+
unsigned char *image = static_cast<unsigned char *>(std::malloc(sizeof kModule));
82+
std::memcpy(image, kModule, sizeof kModule);
83+
wasm_module_t mod = wasm_runtime_load(image, sizeof kModule, err, sizeof err);
84+
g_inst = mod ? wasm_runtime_instantiate(mod, 16 * 1024, 0, err, sizeof err) : nullptr;
85+
g_env = g_inst ? wasm_runtime_create_exec_env(g_inst, 16 * 1024) : nullptr;
86+
if (!g_env) {
87+
std::printf("module setup failed: %s\n", err);
88+
return 1;
89+
}
90+
91+
uint32_t argv[2] = { 0, 0 };
92+
bool ok = true;
93+
94+
wasm_runtime_set_instruction_count_limit(g_env, 1000000);
95+
bool stopped = !call("spin", argv, 0) && exception_has("instruction limit");
96+
std::printf("budget stops the loop %s\n", stopped ? "ok" : "FAIL");
97+
ok = ok && stopped && usable_again();
98+
99+
wasm_runtime_set_instruction_count_limit(g_env, -1);
100+
pthread_t th;
101+
pthread_create(&th, nullptr, terminate_until_returned, nullptr);
102+
bool terminated = !call("spin", argv, 0) && exception_has("terminated by user");
103+
g_returned = true;
104+
pthread_join(th, nullptr);
105+
std::printf("terminate stops the loop %s\n", terminated ? "ok" : "FAIL");
106+
ok = ok && terminated && usable_again();
107+
108+
std::printf("instance usable afterwards %s\n", ok ? "ok" : "FAIL");
109+
return ok ? 0 : 1;
110+
}
111+
112+
#else
113+
114+
int main() { return 0; }
115+
116+
#endif

0 commit comments

Comments
 (0)