Skip to content
Open
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
543cb94
wasmtime: extract running module into function
t4chib4ne Sep 10, 2025
d878baf
wasmtime: Implement basic WASIp2 components support
t4chib4ne Sep 10, 2025
c05cf2e
wasmtime: Check functions after manual loading
t4chib4ne Sep 10, 2025
ce7d656
wasmtime: Add argv to wasm components; Change CMD to ENTRYPOINT in wa…
t4chib4ne Sep 10, 2025
dc2e2e1
wasmtime: have wasip2 inherit env
t4chib4ne Sep 11, 2025
b1c4da5
wasmtime: Fix wat to wasm compiling
t4chib4ne Sep 11, 2025
9a073f6
wasmtime: misc code cleanup
t4chib4ne Sep 12, 2025
e8cbc72
wasmtime: expand reading entrypoint error message
t4chib4ne Sep 19, 2025
741045a
wasmtime: Fix and Add preopen_dir for modules & components
t4chib4ne Sep 11, 2025
cf36a28
wasmtime: properly check wat file extension
t4chib4ne Sep 21, 2025
877ca9a
wasmtime: Add helper for wasm header interpretation
t4chib4ne Sep 21, 2025
d6b9778
wasmtime: branch execution path after wasm header interpretation
t4chib4ne Sep 21, 2025
49f0f42
wasmtime: fix typo in errmsg
t4chib4ne Sep 26, 2025
6b5a217
wasmtime: harden wasm_interpret_header func
t4chib4ne Oct 1, 2025
c08bb45
wasmtime: avoid NULL pointer after wasm compilation
t4chib4ne Oct 1, 2025
fbc799c
wasmtime: share common symbol loading
t4chib4ne Oct 8, 2025
b86db3c
wasmtime: use symbol loading helper everywhere
t4chib4ne Oct 8, 2025
0f89f34
wasmtime: ftell error handling
t4chib4ne Oct 10, 2025
1a5e2c2
wasmtime: use unified wasi api
t4chib4ne Nov 6, 2025
b2aeb51
wasmtime: compat for wasmtime < 39
t4chib4ne Nov 7, 2025
73df8d8
wasmtime: refactor libwasmtime_vm compat
t4chib4ne Nov 7, 2025
13377c7
wasmtime: add proper exit status handling
t4chib4ne Nov 7, 2025
c88c048
wasmtime: misc improvements
t4chib4ne Nov 7, 2025
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
146 changes: 106 additions & 40 deletions src/libcrun/handlers/wasmtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_DLOPEN
# include <dlfcn.h>
Expand All @@ -44,16 +45,110 @@
#endif

#if HAVE_DLOPEN && HAVE_WASMTIME
static void
libwasmtime_run_module (void *cookie, char *const argv[], wasm_engine_t *engine, wasm_byte_vec_t *wasm);

static void
libwasmtime_run_component (void *cookie, char *const argv[], wasm_engine_t *engine, wasm_byte_vec_t *wasm);

static int
libwasmtime_exec (void *cookie, libcrun_container_t *container arg_unused,
const char *pathname, char *const argv[])
{
size_t args_size = 0;
char *const *arg;
wasm_byte_vec_t error_message;
wasm_byte_vec_t wasm_bytes;
wasm_engine_t *(*wasm_engine_new) ();
wasmtime_error_t *(*wasmtime_wat2wasm) (const char *wat, size_t wat_len, wasm_byte_vec_t *out);
void (*wasm_byte_vec_new_uninitialized) (wasm_byte_vec_t *, size_t);
wasmtime_error_t *(*wasmtime_module_validate) (wasm_engine_t *engine, const uint8_t *wasm, size_t wasm_len);
void (*wasmtime_error_message) (const wasmtime_error_t *error, wasm_name_t *message);
void (*wasmtime_error_delete) (wasmtime_error_t *error);

wasmtime_wat2wasm = dlsym (cookie, "wasmtime_wat2wasm");
wasm_engine_new = dlsym (cookie, "wasm_engine_new");
wasm_byte_vec_new_uninitialized = dlsym (cookie, "wasm_byte_vec_new_uninitialized");
wasmtime_module_validate = dlsym (cookie, "wasmtime_module_validate");
wasmtime_error_delete = dlsym (cookie, "wasmtime_error_delete");
wasmtime_error_message = dlsym (cookie, "wasmtime_error_message");

if (wasmtime_wat2wasm == NULL
|| wasm_engine_new == NULL
|| wasm_byte_vec_new_uninitialized == NULL
|| wasmtime_module_validate == NULL
|| wasmtime_error_delete == NULL
|| wasmtime_error_message == NULL)
error (EXIT_FAILURE, 0, "could not find symbol in `libwasmtime.so`");

// Set up wasmtime context
wasm_engine_t *engine = wasm_engine_new ();
assert (engine != NULL);

wasm_byte_vec_t wasm;
// Load and parse container entrypoint
FILE *file = fopen (pathname, "rbe");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is e in rbe here ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the code was just copied. The e seems to be a glibc extension which sets the O_CLOEXEC flag on the descriptor. This flag causes the fd to be automatically closed upon calling one of the exec functions.

A few lines later the fd is actually closed by hand and none the exec functions are visibly being called. As e is not standards compliant maybe it should be removed?

Sources: fopen(3) and open(2) man pages

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As e is not standards compliant maybe it should be removed?

Yes if it's not necessary I think we should remove it, wdyt @giuseppe

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some extra info: Closing the fd manually later does not prevent all issues O_CLOEXEC tries to solve. The glibc man page states that it is a glibc extension so I figured it would not be standards compliant but musl and FreeBSDs libc actually also implement the e in fopen.

Sources: musl and FreeBSDs libc

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove it if it is not required, I see similar comment later from review bot #1877 (comment)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading other parts of the code for quite some time, I think it is safe to say e is not required. This is also encouraged by the fact that right before the fopen we are always in a single threaded context thus the mentioned leak in open(2) is not possible.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just found this commit 0f0d5be which adds e to a lot of fopen calls. Also in wasmtime.
Even though I said removing the flag should be fine I am not 100% sure. Thus I would like to add the e flag back

if (! file)
error (EXIT_FAILURE, 0, "error loading entrypoint");
fseek (file, 0L, SEEK_END);
size_t file_size = ftell (file);
wasm_byte_vec_new_uninitialized (&wasm, file_size);
fseek (file, 0L, SEEK_SET);
if (fread (wasm.data, file_size, 1, file) != 1)
error (EXIT_FAILURE, 0, "error load");
fclose (file);

// If entrypoint contains a webassembly text format
// compile it on the fly and convert to equivalent
// binary format.
if (has_suffix (pathname, "wat") > 0)
{
wasmtime_error_t *err = wasmtime_wat2wasm ((char *) &wasm_bytes, file_size, &wasm);
if (err != NULL)
{
wasmtime_error_message (err, &error_message);
wasmtime_error_delete (err);
error (EXIT_FAILURE, 0, "failed while compiling wat to wasm binary : %.*s", (int) error_message.size, error_message.data);
}
wasm = wasm_bytes;
}

// Check if it is a valid webassembly module or
// a component.
bool is_wasm_module = true;
wasmtime_error_t *err = wasmtime_module_validate (engine, (uint8_t *) wasm.data, wasm.size);
if (err != NULL)
{
wasmtime_error_message (err, &error_message);
wasmtime_error_delete (err);

if (strcmp ((char *) error_message.data, "component passed to module validation") != 0)
{
error (EXIT_FAILURE, 0, "failed to validate module: %.*s", (int) error_message.size, error_message.data);
}

err = NULL;
is_wasm_module = false;
}

if (is_wasm_module)
{
libwasmtime_run_module (cookie, argv, engine, &wasm);
}
else
{
libwasmtime_run_component (cookie, argv, engine, &wasm);
}

exit (EXIT_SUCCESS);
}

static void
libwasmtime_run_module (void *cookie, char *const argv[], wasm_engine_t *engine, wasm_byte_vec_t *wasm)
{
size_t args_size = 0;
char *const *arg;
wasm_byte_vec_t error_message;

// Load needed functions
void (*wasm_engine_delete) (wasm_engine_t *);
void (*wasm_byte_vec_delete) (wasm_byte_vec_t *);
void (*wasm_byte_vec_new_uninitialized) (wasm_byte_vec_t *, size_t);
Expand Down Expand Up @@ -100,8 +195,6 @@ libwasmtime_exec (void *cookie, libcrun_container_t *container arg_unused,
void (*wasmtime_error_delete) (wasmtime_error_t *error);
bool (*wasi_config_preopen_dir) (wasi_config_t *config, const char *path, const char *guest_path);

wasmtime_wat2wasm = dlsym (cookie, "wasmtime_wat2wasm");
wasm_engine_new = dlsym (cookie, "wasm_engine_new");
wasm_engine_delete = dlsym (cookie, "wasm_engine_delete");
wasm_byte_vec_delete = dlsym (cookie, "wasm_byte_vec_delete");
wasm_byte_vec_new_uninitialized = dlsym (cookie, "wasm_byte_vec_new_uninitialized");
Expand All @@ -127,21 +220,18 @@ libwasmtime_exec (void *cookie, libcrun_container_t *container arg_unused,
wasmtime_error_message = dlsym (cookie, "wasmtime_error_message");
wasi_config_preopen_dir = dlsym (cookie, "wasi_config_preopen_dir");

if (wasm_engine_new == NULL || wasm_engine_delete == NULL || wasm_byte_vec_delete == NULL
if (wasm_engine_delete == NULL || wasm_byte_vec_delete == NULL
|| wasm_byte_vec_new_uninitialized == NULL || wasi_config_new == NULL || wasmtime_store_new == NULL
|| wasmtime_store_context == NULL || wasmtime_linker_new == NULL || wasmtime_linker_define_wasi == NULL
|| wasmtime_module_new == NULL || wasi_config_inherit_argv == NULL || wasi_config_inherit_stdout == NULL
|| wasi_config_inherit_stdin == NULL || wasi_config_inherit_stderr == NULL
|| wasi_config_inherit_env == NULL || wasmtime_context_set_wasi == NULL
|| wasmtime_linker_module == NULL || wasmtime_linker_get_default == NULL || wasmtime_func_call == NULL
|| wasmtime_module_delete == NULL || wasmtime_store_delete == NULL || wasi_config_set_argv == NULL
|| wasmtime_error_delete == NULL || wasmtime_error_message == NULL || wasi_config_preopen_dir == NULL
|| wasmtime_wat2wasm == NULL)
|| wasmtime_error_delete == NULL || wasmtime_error_message == NULL || wasi_config_preopen_dir == NULL)
error (EXIT_FAILURE, 0, "could not find symbol in `libwasmtime.so`");

// Set up wasmtime context
wasm_engine_t *engine = wasm_engine_new ();
assert (engine != NULL);
wasmtime_store_t *store = wasmtime_store_new (engine, NULL, NULL);
assert (store != NULL);
wasmtime_context_t *context = wasmtime_store_context (store);
Expand All @@ -156,44 +246,16 @@ libwasmtime_exec (void *cookie, libcrun_container_t *container arg_unused,
error (EXIT_FAILURE, 0, "failed to link wasi: %.*s", (int) error_message.size, error_message.data);
}

wasm_byte_vec_t wasm;
// Load and parse container entrypoint
FILE *file = fopen (pathname, "rbe");
if (! file)
error (EXIT_FAILURE, 0, "error loading entrypoint");
fseek (file, 0L, SEEK_END);
size_t file_size = ftell (file);
wasm_byte_vec_new_uninitialized (&wasm, file_size);
fseek (file, 0L, SEEK_SET);
if (fread (wasm.data, file_size, 1, file) != 1)
error (EXIT_FAILURE, 0, "error load");
fclose (file);

// If entrypoint contains a webassembly text format
// compile it on the fly and convert to equivalent
// binary format.
if (has_suffix (pathname, "wat") > 0)
{
wasmtime_error_t *err = wasmtime_wat2wasm ((char *) &wasm_bytes, file_size, &wasm);
if (err != NULL)
{
wasmtime_error_message (err, &error_message);
wasmtime_error_delete (err);
error (EXIT_FAILURE, 0, "failed while compiling wat to wasm binary : %.*s", (int) error_message.size, error_message.data);
}
wasm = wasm_bytes;
}

// Compile wasm modules
wasmtime_module_t *module = NULL;
err = wasmtime_module_new (engine, (uint8_t *) wasm.data, wasm.size, &module);
err = wasmtime_module_new (engine, (uint8_t *) wasm->data, wasm->size, &module);
if (! module)
{
wasmtime_error_message (err, &error_message);
wasmtime_error_delete (err);
error (EXIT_FAILURE, 0, "failed to compile module: %.*s", (int) error_message.size, error_message.data);
}
wasm_byte_vec_delete (&wasm);
wasm_byte_vec_delete (wasm);

// Init WASI program
wasi_config_t *wasi_config = wasi_config_new ("crun_wasi_program");
Expand Down Expand Up @@ -249,8 +311,12 @@ libwasmtime_exec (void *cookie, libcrun_container_t *container arg_unused,
wasmtime_module_delete (module);
wasmtime_store_delete (store);
wasm_engine_delete (engine);
}

exit (EXIT_SUCCESS);
static void
libwasmtime_run_component (void *cookie, char *const argv[], wasm_engine_t *engine, wasm_byte_vec_t *wasm)
{
error (EXIT_FAILURE, 0, "running components is not yet implemented!");
}

static int
Expand Down