Skip to content

Commit

Permalink
wasm: Add new functions to the WASI interface. (envoyproxy#431)
Browse files Browse the repository at this point in the history
fd_read: Always returns ENOSYS
fd_fdstat_get: Returns a reasonable response for fds 0 and 1,
  and EBADF otherwise.

Add a "WASI" test configuration to the existing tests that checks
for proper implementation of these by testing "fprintf,"
"fread," and "isatty".

Fixes envoyproxy/envoy-wasm#425

Signed-off-by: Gregory Brail <gregbrail@google.com>
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
  • Loading branch information
gbrail authored and PiotrSikora committed Mar 3, 2020
1 parent 6d0cf31 commit db0d3a7
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 0 deletions.
27 changes: 27 additions & 0 deletions source/extensions/common/wasm/exports.cc
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,13 @@ Word wasi_unstable_fd_write(void* raw_context, Word fd, Word iovs, Word iovs_len
return 0; // __WASI_ESUCCESS
}

// __wasi_errno_t __wasi_fd_read(_wasi_fd_t fd, const __wasi_iovec_t *iovs,
// size_t iovs_len, __wasi_size_t *nread);
Word wasi_unstable_fd_read(void*, Word, Word, Word, Word) {
// Don't support reading of any files.
return 52; // __WASI_ERRNO_ENOSYS
}

// __wasi_errno_t __wasi_fd_seek(__wasi_fd_t fd, __wasi_filedelta_t offset, __wasi_whence_t
// whence,__wasi_filesize_t *newoffset);
Word wasi_unstable_fd_seek(void*, Word, int64_t, Word, Word) {
Expand All @@ -714,6 +721,26 @@ Word wasi_unstable_fd_seek(void*, Word, int64_t, Word, Word) {
// __wasi_errno_t __wasi_fd_close(__wasi_fd_t fd);
Word wasi_unstable_fd_close(void*, Word) { throw WasmException("wasi_unstable fd_close"); }

// __wasi_errno_t __wasi_fd_fdstat_get(__wasi_fd_t fd, __wasi_fdstat_t *stat)
Word wasi_unstable_fd_fdstat_get(void* raw_context, Word fd, Word statOut) {
// We will only support this interface on stdout and stderr
if (fd.u64_ != 1 && fd.u64_ != 2) {
return 8; // __WASI_EBADF;
}

// The last word points to a 24-byte structure, which we
// are mostly going to zero out.
uint64_t wasi_fdstat[3];
wasi_fdstat[0] = 0;
wasi_fdstat[1] = 64; // This sets "fs_rights_base" to __WASI_RIGHTS_FD_WRITE
wasi_fdstat[2] = 0;

auto context = WASM_CONTEXT(raw_context);
context->wasmVm()->setMemory(statOut.u64_, 3 * sizeof(uint64_t), &wasi_fdstat);

return 0; // __WASI_ESUCCESS
}

// __wasi_errno_t __wasi_environ_get(char **environ, char *environ_buf);
Word wasi_unstable_environ_get(void*, Word, Word) {
return 0; // __WASI_ESUCCESS
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/common/wasm/exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ Word call_foreign_function(void* raw_context, Word function_name, Word function_

Word wasi_unstable_fd_write(void* raw_context, Word fd, Word iovs, Word iovs_len,
Word nwritten_ptr);
Word wasi_unstable_fd_read(void*, Word, Word, Word, Word);
Word wasi_unstable_fd_seek(void*, Word, int64_t, Word, Word);
Word wasi_unstable_fd_close(void*, Word);
Word wasi_unstable_fd_fdstat_get(void*, Word fd, Word statOut);
Word wasi_unstable_environ_get(void*, Word, Word);
Word wasi_unstable_environ_sizes_get(void* raw_context, Word count_ptr, Word buf_size_ptr);
Word wasi_unstable_args_get(void* raw_context, Word argc_ptr, Word argv_buf_size_ptr);
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/common/wasm/wasm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,10 @@ void Wasm::registerCallbacks() {
&ConvertFunctionWordToUint32<decltype(Exports::wasi_unstable_##_fn), \
Exports::wasi_unstable_##_fn>::convertFunctionWordToUint32)
_REGISTER_WASI(fd_write);
_REGISTER_WASI(fd_read);
_REGISTER_WASI(fd_seek);
_REGISTER_WASI(fd_close);
_REGISTER_WASI(fd_fdstat_get);
_REGISTER_WASI(environ_get);
_REGISTER_WASI(environ_sizes_get);
_REGISTER_WASI(args_get);
Expand Down
47 changes: 47 additions & 0 deletions test/extensions/common/wasm/test_data/test_cpp.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// NOLINT(namespace-envoy)
#include <unistd.h>

#include <cerrno>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <string>

Expand Down Expand Up @@ -27,6 +32,13 @@ static float gInfinity = INFINITY;
} while (0)
#endif

#define FAIL_NOW(_msg) \
do { \
const std::string __message = _msg; \
proxy_log(LogLevel::critical, __message.c_str(), __message.size()); \
abort(); \
} while (0)

WASM_EXPORT(uint32_t, proxy_on_vm_start, (uint32_t, uint32_t context_id)) {
const char* configuration_ptr = nullptr;
size_t size;
Expand Down Expand Up @@ -123,6 +135,41 @@ WASM_EXPORT(uint32_t, proxy_on_vm_start, (uint32_t, uint32_t context_id)) {
}
::free(compressed);
::free(result);
} else if (configuration == "WASI") {
// These checks depend on Emscripten's support for WASI and will only
// work if invoked on a "real" WASM VM.
int err = fprintf(stdout, "WASI write to stdout\n");
if (err < 0) {
FAIL_NOW("stdout write should succeed");
}
err = fprintf(stderr, "WASI write to stderr\n");
if (err < 0) {
FAIL_NOW("stderr write should succeed");
}
// We explicitly don't support reading from stdin
char tmp[16];
size_t rc = fread(static_cast<void*>(tmp), 1, 16, stdin);
if (rc != 0 || errno != ENOSYS) {
FAIL_NOW("stdin read should fail. errno = " + std::to_string(errno));
}
// No environment variables should be available
char* pathenv = getenv("PATH");
if (pathenv != nullptr) {
FAIL_NOW("PATH environment variable should not be available");
}
// Exercise the WASI "fd_fdstat_get" a little bit
int tty = isatty(1);
if (errno != ENOTTY || tty != 0) {
FAIL_NOW("stdout is not a tty");
}
tty = isatty(2);
if (errno != ENOTTY || tty != 0) {
FAIL_NOW("stderr is not a tty");
}
tty = isatty(99);
if (errno != EBADF || tty != 0) {
FAIL_NOW("isatty errors on bad fds. errno = " + std::to_string(errno));
}
} else {
std::string message = "on_vm_start " + configuration;
proxy_log(LogLevel::info, message.c_str(), message.size());
Expand Down
Binary file modified test/extensions/common/wasm/test_data/test_cpp.wasm
Binary file not shown.
41 changes: 41 additions & 0 deletions test/extensions/common/wasm/wasm_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,47 @@ TEST_P(WasmCommonTest, Foreign) {
wasm->startForTesting(std::move(context), plugin);
}

TEST_P(WasmCommonTest, WASI) {
if (GetParam() == "null") {
// This test has no meaning unless it is invoked by actual WASM code
return;
}
Stats::IsolatedStoreImpl stats_store;
Api::ApiPtr api = Api::createApiForTest(stats_store);
Upstream::MockClusterManager cluster_manager;
Event::DispatcherPtr dispatcher(api->allocateDispatcher());
auto scope = Stats::ScopeSharedPtr(stats_store.createScope("wasm."));
NiceMock<LocalInfo::MockLocalInfo> local_info;
auto name = "";
auto root_id = "";
auto vm_id = "";
auto vm_configuration = "WASI";
auto vm_key = "";
auto plugin = std::make_shared<Extensions::Common::Wasm::Plugin>(
name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info,
nullptr);
auto wasm = std::make_unique<Extensions::Common::Wasm::Wasm>(
absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope,
cluster_manager, *dispatcher);
EXPECT_NE(wasm, nullptr);
std::string code;
if (GetParam() != "null") {
code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(
absl::StrCat("{{ test_rundir }}/test/extensions/common/wasm/test_data/test_cpp.wasm")));
} else {
// The name of the Null VM plugin.
code = "CommonWasmTestCpp";
}
EXPECT_FALSE(code.empty());
auto context = std::make_unique<TestContext>(wasm.get());

EXPECT_CALL(*context, scriptLog_(spdlog::level::info, Eq("WASI write to stdout"))).Times(1);
EXPECT_CALL(*context, scriptLog_(spdlog::level::err, Eq("WASI write to stderr"))).Times(1);

EXPECT_TRUE(wasm->initialize(code, false));
wasm->startForTesting(std::move(context), plugin);
}

TEST_P(WasmCommonTest, VmCache) {
Stats::IsolatedStoreImpl stats_store;
Api::ApiPtr api = Api::createApiForTest(stats_store);
Expand Down

0 comments on commit db0d3a7

Please sign in to comment.