Skip to content
Draft
Changes from 1 commit
Commits
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.

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