-
Notifications
You must be signed in to change notification settings - Fork 374
WASIp2 component support for wasmtime #1877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Reviewer's GuideAdd WASIp2 component support to wasmtime by detecting whether the input is a module or a component, refactoring execution into separate runners, dynamically loading the necessary Wasmtime C API symbols, and updating example docs. File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
So I wanted to implement the Is this by choice or a bug? |
@flouthoc PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some nits, also could you use wasmtime: $DESCRIPTION
for your commits instead of [wasmtime]
, otherwise LGTM
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
0074108
to
ce8441d
Compare
Thanks for taking a look, hope I didn't oversee anything! The only thing keeping this PR as a draft is that I would like to give both WASM modules and components the ability to read & write to the working directory. But the previous implementation did not allow this. Are there any security concerns or can this feature simply be added? |
src/libcrun/handlers/wasmtime.c
Outdated
|
||
wasm_byte_vec_t wasm; | ||
// Load and parse container entrypoint | ||
FILE *file = fopen (pathname, "rbe"); |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
FROM scratch | ||
COPY hello.wasm / | ||
CMD ["/hello.wasm"] | ||
ENTRYPOINT ["/hello.wasm"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you write it in commit message or here, about why is this change needed ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry forgot to mention it ...
After adding the pass through of argv to the component I wanted to try it out. Upon running podman run mywasm-image:latest arg1 arg2
podman would replace CMD
(the wasm binary) with arg1
which of course does not work. Changing to ENTRYPOINT
resolves this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a breaking change. I think adding this message to commit logs can help us revisit this and fix this if needed.
cc @giuseppe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding this to the commit logs should be possible but I don't really see where this is a breaking change? Without the changes of this PR a wasm module runs into the same problem. IIRC this is also the same behavior a non-wasm container would show.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the message in ce7d656 OK?
…sm-wasi-example In docs/wasm-wasi-example.md the Containerfile had the WebAssembly binary in the `CMD` instruction. But running the container via podman like so `podman run mywasm-image:latest arg1 arg2` does not work as podman instructs crun to run `arg1 arg2` instead of `hello.wasm arg1 arg2` resulting in the error `arg1: command not found`. Using `ENTRYPOINT` in the Containerfile makes the previously mentioned podman command work. Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
31fd57e
to
9a073f6
Compare
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- Consider extracting the repeated dlsym() lookups into a helper utility that takes an array of symbol names and returns function pointers, reducing duplicate code.
- Relying on matching the literal error string “component passed to module validation” for component detection is brittle; consider using a dedicated component detection API or inspecting the Wasm magic/header instead.
- When a symbol lookup fails, include the specific missing symbol name in the error output to make debugging dynamic loading issues easier.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider extracting the repeated dlsym() lookups into a helper utility that takes an array of symbol names and returns function pointers, reducing duplicate code.
- Relying on matching the literal error string “component passed to module validation” for component detection is brittle; consider using a dedicated component detection API or inspecting the Wasm magic/header instead.
- When a symbol lookup fails, include the specific missing symbol name in the error output to make debugging dynamic loading issues easier.
## Individual Comments
### Comment 1
<location> `src/libcrun/handlers/wasmtime.c:91` </location>
<code_context>
+
+ wasm_byte_vec_t wasm;
+ // Load and parse container entrypoint
+ FILE *file = fopen (pathname, "rbe");
+ if (! file)
+ error (EXIT_FAILURE, 0, "error loading entrypoint");
</code_context>
<issue_to_address>
**issue:** The use of 'rbe' as the fopen mode is non-standard and may cause portability issues.
Use 'rb' for reading binary files unless 'rbe' is required and confirmed to be supported across all target platforms.
</issue_to_address>
### Comment 2
<location> `src/libcrun/handlers/wasmtime.c:107` </location>
<code_context>
+ // 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.data, file_size, &wasm_bytes);
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Suffix check for 'wat' may match unintended filenames.
This check may incorrectly match filenames like 'mywat' or 'format'. Use '.wat' as the suffix to ensure only intended files are processed.
```suggestion
if (has_suffix (pathname, ".wat") > 0)
```
</issue_to_address>
### Comment 3
<location> `src/libcrun/handlers/wasmtime.c:129` </location>
<code_context>
+ 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);
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** String comparison for error message is brittle and may break with upstream changes.
Consider using an error code or a more stable identifier instead of matching the error message string, to avoid breakage from upstream changes.
Suggested implementation:
```c
wasmtime_error_message (err, &error_message);
// Use a more robust check for the error type.
// If wasmtime_error_as_exit_status is available, use it.
int exit_status = 0;
bool is_component_error = false;
#ifdef WASMTIME_HAS_ERROR_EXIT_STATUS
if (wasmtime_error_as_exit_status(err, &exit_status)) {
// Check for a specific exit status if known for component error.
if (exit_status == WASMTIME_COMPONENT_VALIDATION_ERROR_CODE) {
is_component_error = true;
}
}
#endif
// Fallback: check for substring in error message.
if (!is_component_error) {
if (memmem(error_message.data, error_message.size, "component passed to module validation", strlen("component passed to module validation")) == NULL)
error (EXIT_FAILURE, 0, "failed to validate module: %.*s", (int) error_message.size, error_message.data);
}
wasmtime_error_delete (err);
```
- You may need to define `WASMTIME_HAS_ERROR_EXIT_STATUS` and `WASMTIME_COMPONENT_VALIDATION_ERROR_CODE` if the wasmtime API provides such error codes. If not, you can remove the conditional and rely on the substring check.
- If you have a helper function for error type checking, use that instead of the inline logic.
- Make sure to include the necessary headers for `memmem` if not already present.
</issue_to_address>
### Comment 4
<location> `src/libcrun/handlers/wasmtime.c:278` </location>
<code_context>
wasi_config_inherit_stdout (wasi_config);
wasi_config_inherit_stderr (wasi_config);
- wasi_config_preopen_dir (wasi_config, ".", ".");
+ wasi_config_preopen_dir (
+ wasi_config,
+ ".",
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Hardcoded permissions for preopened directory may be overly permissive.
Evaluate if both read and write permissions are required, or if access can be restricted to enhance security.
Suggested implementation:
```c
WASMTIME_WASI_DIR_PERMS_READ,
WASMTIME_WASI_FILE_PERMS_READ);
```
If your application requires write access, you can revert to the original permissions. Otherwise, this change will restrict access to read-only, improving security.
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
Signed-off-by: Maximilian Hüter <[email protected]>
Adds WASIp2 component support for wasmtime by checking whether the given WebAssembly is a module or a component and then calling the appropriate functions.
The calls are nothing fancy just a very basic setup giving the WebAssembly component full access to the WASIp2 implementation of wasmtime.
Closes #1871
Still a Draft because of:
wasi_config_preopen_dir
⇒ calling this function for a WASIp2 config also workswasi:cli/[email protected]#run
should be fine as wasmtime will call the highest version available (e.g.wasi:cli/[email protected]#run
)Summary by Sourcery
Enable WASIp2 component support in wasmtime by detecting if the input is a module or component and invoking the appropriate execution path with dynamically loaded Wasmtime APIs and WASIp2 configuration
New Features:
Enhancements:
Documentation:
Summary by Sourcery
Enable WASIp2 component support in the wasmtime handler by detecting input type, refactoring module logic, and adding a separate execution path for components with dynamic API loading and enhanced WASI configuration
New Features:
Enhancements:
Documentation: