blob: 25ca06c9d4d33346028c830dc339e2671909e9ad [file]
// Copyright 2020 Envoy Project Authors
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "include/proxy-wasm/limits.h"
#include "include/proxy-wasm/pairs_util.h"
#include "include/proxy-wasm/wasm.h"
#include <openssl/rand.h>
#include <utility>
namespace proxy_wasm {
thread_local ContextBase *current_context_;
thread_local uint32_t effective_context_id_ = 0;
// Any currently executing Wasm call context.
ContextBase *contextOrEffectiveContext() {
if (effective_context_id_ == 0) {
return current_context_;
}
auto *effective_context = current_context_->wasm()->getContext(effective_context_id_);
if (effective_context != nullptr) {
return effective_context;
}
// The effective_context_id_ no longer exists, revert to the true context.
return current_context_;
};
std::unordered_map<std::string, WasmForeignFunction> &foreignFunctions() {
static auto *ptr = new std::unordered_map<std::string, WasmForeignFunction>;
return *ptr;
}
WasmForeignFunction getForeignFunction(std::string_view function_name) {
auto foreign_functions = foreignFunctions();
auto it = foreign_functions.find(std::string(function_name));
if (it != foreign_functions.end()) {
return it->second;
}
return nullptr;
}
RegisterForeignFunction::RegisterForeignFunction(const std::string &name, WasmForeignFunction f) {
foreignFunctions()[name] = std::move(f);
}
namespace exports {
// General ABI.
Word set_property(Word key_ptr, Word key_size, Word value_ptr, Word value_size) {
auto *context = contextOrEffectiveContext();
auto key = context->wasmVm()->getMemory(key_ptr, key_size);
auto value = context->wasmVm()->getMemory(value_ptr, value_size);
if (!key || !value) {
return WasmResult::InvalidMemoryAccess;
}
return context->setProperty(key.value(), value.value());
}
// Generic selector
Word get_property(Word path_ptr, Word path_size, Word value_ptr_ptr, Word value_size_ptr) {
auto *context = contextOrEffectiveContext();
auto path = context->wasmVm()->getMemory(path_ptr, path_size);
if (!path.has_value()) {
return WasmResult::InvalidMemoryAccess;
}
std::string value;
auto result = context->getProperty(path.value(), &value);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->copyToPointerSize(value, value_ptr_ptr, value_size_ptr)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word get_configuration(Word value_ptr_ptr, Word value_size_ptr) {
auto *context = contextOrEffectiveContext();
auto value = context->getConfiguration();
if (!context->wasm()->copyToPointerSize(value, value_ptr_ptr, value_size_ptr)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word get_status(Word code_ptr, Word value_ptr_ptr, Word value_size_ptr) {
auto *context = contextOrEffectiveContext()->root_context();
auto status = context->getStatus();
if (!context->wasm()->setDatatype(code_ptr, status.first)) {
return WasmResult::InvalidMemoryAccess;
}
if (!context->wasm()->copyToPointerSize(status.second, value_ptr_ptr, value_size_ptr)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
// HTTP
// Continue/Reply/Route
Word continue_request() {
auto *context = contextOrEffectiveContext();
return context->continueStream(WasmStreamType::Request);
}
Word continue_response() {
auto *context = contextOrEffectiveContext();
return context->continueStream(WasmStreamType::Response);
}
Word continue_stream(Word type) {
auto *context = contextOrEffectiveContext();
if (type > static_cast<uint64_t>(WasmStreamType::MAX)) {
return WasmResult::BadArgument;
}
return context->continueStream(static_cast<WasmStreamType>(type.u64_));
}
Word close_stream(Word type) {
auto *context = contextOrEffectiveContext();
if (type > static_cast<uint64_t>(WasmStreamType::MAX)) {
return WasmResult::BadArgument;
}
return context->closeStream(static_cast<WasmStreamType>(type.u64_));
}
Word send_local_response(Word response_code, Word response_code_details_ptr,
Word response_code_details_size, Word body_ptr, Word body_size,
Word additional_response_header_pairs_ptr,
Word additional_response_header_pairs_size, Word grpc_status) {
auto *context = contextOrEffectiveContext();
auto details =
context->wasmVm()->getMemory(response_code_details_ptr, response_code_details_size);
auto body = context->wasmVm()->getMemory(body_ptr, body_size);
auto additional_response_header_pairs = context->wasmVm()->getMemory(
additional_response_header_pairs_ptr, additional_response_header_pairs_size);
if (!details || !body || !additional_response_header_pairs) {
return WasmResult::InvalidMemoryAccess;
}
auto additional_headers = PairsUtil::toPairs(additional_response_header_pairs.value());
context->sendLocalResponse(response_code, body.value(), std::move(additional_headers),
grpc_status, details.value());
context->wasm()->stopNextIteration(true);
return WasmResult::Ok;
}
Word clear_route_cache() {
auto *context = contextOrEffectiveContext();
context->clearRouteCache();
return WasmResult::Ok;
}
Word set_effective_context(Word context_id) {
auto *context = contextOrEffectiveContext();
auto cid = static_cast<uint32_t>(context_id);
auto *c = context->wasm()->getContext(cid);
if (c == nullptr) {
return WasmResult::BadArgument;
}
effective_context_id_ = cid;
return WasmResult::Ok;
}
Word done() {
auto *context = contextOrEffectiveContext();
return context->wasm()->done(context);
}
Word call_foreign_function(Word function_name, Word function_name_size, Word arguments,
Word arguments_size, Word results, Word results_size) {
auto *context = contextOrEffectiveContext();
auto function = context->wasmVm()->getMemory(function_name, function_name_size);
if (!function) {
return WasmResult::InvalidMemoryAccess;
}
auto args_opt = context->wasmVm()->getMemory(arguments, arguments_size);
if (!args_opt) {
return WasmResult::InvalidMemoryAccess;
}
auto f = getForeignFunction(function.value());
if (!f) {
return WasmResult::NotFound;
}
auto &wasm = *context->wasm();
auto &args = args_opt.value();
uint64_t address = 0;
void *result = nullptr;
size_t result_size = 0;
auto res = f(wasm, args, [&wasm, &address, &result, &result_size, results](size_t s) -> void * {
if (results != 0U) {
result = wasm.allocMemory(s, &address);
} else {
// If the caller does not want the results, allocate a temporary buffer for them.
result = ::malloc(s);
}
result_size = s;
return result;
});
if (results != 0U && !context->wasmVm()->setWord(results, Word(address))) {
return WasmResult::InvalidMemoryAccess;
}
if (results_size != 0U && !context->wasmVm()->setWord(results_size, Word(result_size))) {
return WasmResult::InvalidMemoryAccess;
}
if (results == 0U) {
::free(result);
}
return res;
}
// SharedData
Word get_shared_data(Word key_ptr, Word key_size, Word value_ptr_ptr, Word value_size_ptr,
Word cas_ptr) {
auto *context = contextOrEffectiveContext();
auto key = context->wasmVm()->getMemory(key_ptr, key_size);
if (!key) {
return WasmResult::InvalidMemoryAccess;
}
std::pair<std::string, uint32_t> data;
WasmResult result = context->getSharedData(key.value(), &data);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->copyToPointerSize(data.first, value_ptr_ptr, value_size_ptr)) {
return WasmResult::InvalidMemoryAccess;
}
if (!context->wasmVm()->setMemory(cas_ptr, sizeof(uint32_t), &data.second)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word set_shared_data(Word key_ptr, Word key_size, Word value_ptr, Word value_size, Word cas) {
auto *context = contextOrEffectiveContext();
auto key = context->wasmVm()->getMemory(key_ptr, key_size);
auto value = context->wasmVm()->getMemory(value_ptr, value_size);
if (!key || !value) {
return WasmResult::InvalidMemoryAccess;
}
return context->setSharedData(key.value(), value.value(), cas);
}
Word register_shared_queue(Word queue_name_ptr, Word queue_name_size, Word token_ptr) {
auto *context = contextOrEffectiveContext();
auto queue_name = context->wasmVm()->getMemory(queue_name_ptr, queue_name_size);
if (!queue_name) {
return WasmResult::InvalidMemoryAccess;
}
uint32_t token;
auto result = context->registerSharedQueue(queue_name.value(), &token);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->setDatatype(token_ptr, token)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word dequeue_shared_queue(Word token, Word data_ptr_ptr, Word data_size_ptr) {
auto *context = contextOrEffectiveContext();
std::string data;
WasmResult result = context->dequeueSharedQueue(token.u32(), &data);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->copyToPointerSize(data, data_ptr_ptr, data_size_ptr)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word resolve_shared_queue(Word vm_id_ptr, Word vm_id_size, Word queue_name_ptr,
Word queue_name_size, Word token_ptr) {
auto *context = contextOrEffectiveContext();
auto vm_id = context->wasmVm()->getMemory(vm_id_ptr, vm_id_size);
auto queue_name = context->wasmVm()->getMemory(queue_name_ptr, queue_name_size);
if (!vm_id || !queue_name) {
return WasmResult::InvalidMemoryAccess;
}
uint32_t token = 0;
auto result = context->lookupSharedQueue(vm_id.value(), queue_name.value(), &token);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->setDatatype(token_ptr, token)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word enqueue_shared_queue(Word token, Word data_ptr, Word data_size) {
auto *context = contextOrEffectiveContext();
auto data = context->wasmVm()->getMemory(data_ptr, data_size);
if (!data) {
return WasmResult::InvalidMemoryAccess;
}
return context->enqueueSharedQueue(token.u32(), data.value());
}
// Header/Trailer/Metadata Maps
Word add_header_map_value(Word type, Word key_ptr, Word key_size, Word value_ptr, Word value_size) {
if (type > static_cast<uint64_t>(WasmHeaderMapType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto key = context->wasmVm()->getMemory(key_ptr, key_size);
auto value = context->wasmVm()->getMemory(value_ptr, value_size);
if (!key || !value) {
return WasmResult::InvalidMemoryAccess;
}
return context->addHeaderMapValue(static_cast<WasmHeaderMapType>(type.u64_), key.value(),
value.value());
}
Word get_header_map_value(Word type, Word key_ptr, Word key_size, Word value_ptr_ptr,
Word value_size_ptr) {
if (type > static_cast<uint64_t>(WasmHeaderMapType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto key = context->wasmVm()->getMemory(key_ptr, key_size);
if (!key) {
return WasmResult::InvalidMemoryAccess;
}
std::string_view value;
auto result =
context->getHeaderMapValue(static_cast<WasmHeaderMapType>(type.u64_), key.value(), &value);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->copyToPointerSize(value, value_ptr_ptr, value_size_ptr)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word replace_header_map_value(Word type, Word key_ptr, Word key_size, Word value_ptr,
Word value_size) {
if (type > static_cast<uint64_t>(WasmHeaderMapType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto key = context->wasmVm()->getMemory(key_ptr, key_size);
auto value = context->wasmVm()->getMemory(value_ptr, value_size);
if (!key || !value) {
return WasmResult::InvalidMemoryAccess;
}
return context->replaceHeaderMapValue(static_cast<WasmHeaderMapType>(type.u64_), key.value(),
value.value());
}
Word remove_header_map_value(Word type, Word key_ptr, Word key_size) {
if (type > static_cast<uint64_t>(WasmHeaderMapType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto key = context->wasmVm()->getMemory(key_ptr, key_size);
if (!key) {
return WasmResult::InvalidMemoryAccess;
}
return context->removeHeaderMapValue(static_cast<WasmHeaderMapType>(type.u64_), key.value());
}
Word get_header_map_pairs(Word type, Word ptr_ptr, Word size_ptr) {
if (type > static_cast<uint64_t>(WasmHeaderMapType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
Pairs pairs;
auto result = context->getHeaderMapPairs(static_cast<WasmHeaderMapType>(type.u64_), &pairs);
if (result != WasmResult::Ok) {
return result;
}
if (pairs.empty()) {
if (!context->wasm()->copyToPointerSize("", ptr_ptr, size_ptr)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
uint64_t size = PairsUtil::pairsSize(pairs);
uint64_t ptr = 0;
char *buffer = static_cast<char *>(context->wasm()->allocMemory(size, &ptr));
if (buffer == nullptr) {
return WasmResult::InvalidMemoryAccess;
}
if (!PairsUtil::marshalPairs(pairs, buffer, size)) {
return WasmResult::InvalidMemoryAccess;
}
if (!context->wasmVm()->setWord(ptr_ptr, Word(ptr))) {
return WasmResult::InvalidMemoryAccess;
}
if (!context->wasmVm()->setWord(size_ptr, Word(size))) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word set_header_map_pairs(Word type, Word ptr, Word size) {
if (type > static_cast<uint64_t>(WasmHeaderMapType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto data = context->wasmVm()->getMemory(ptr, size);
if (!data) {
return WasmResult::InvalidMemoryAccess;
}
return context->setHeaderMapPairs(static_cast<WasmHeaderMapType>(type.u64_),
PairsUtil::toPairs(data.value()));
}
Word get_header_map_size(Word type, Word result_ptr) {
if (type > static_cast<uint64_t>(WasmHeaderMapType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
uint32_t size;
auto result = context->getHeaderMapSize(static_cast<WasmHeaderMapType>(type.u64_), &size);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasmVm()->setWord(result_ptr, Word(size))) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
// Buffer
Word get_buffer_bytes(Word type, Word start, Word length, Word ptr_ptr, Word size_ptr) {
if (type > static_cast<uint64_t>(WasmBufferType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto *buffer = context->getBuffer(static_cast<WasmBufferType>(type.u64_));
if (buffer == nullptr) {
return WasmResult::NotFound;
}
// Check for overflow.
if (start > start + length) {
return WasmResult::BadArgument;
}
// Don't overread.
if (start > buffer->size()) {
length = 0;
} else if (start + length > buffer->size()) {
length = buffer->size() - start;
}
if (length == 0) {
if (!context->wasmVm()->setWord(ptr_ptr, Word(0))) {
return WasmResult::InvalidMemoryAccess;
}
if (!context->wasmVm()->setWord(size_ptr, Word(0))) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
return buffer->copyTo(context->wasm(), start, length, ptr_ptr, size_ptr);
}
Word get_buffer_status(Word type, Word length_ptr, Word flags_ptr) {
if (type > static_cast<uint64_t>(WasmBufferType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto *buffer = context->getBuffer(static_cast<WasmBufferType>(type.u64_));
if (buffer == nullptr) {
return WasmResult::NotFound;
}
auto length = buffer->size();
uint32_t flags = 0;
if (!context->wasmVm()->setWord(length_ptr, Word(length))) {
return WasmResult::InvalidMemoryAccess;
}
if (!context->wasm()->setDatatype(flags_ptr, flags)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word set_buffer_bytes(Word type, Word start, Word length, Word data_ptr, Word data_size) {
if (type > static_cast<uint64_t>(WasmBufferType::MAX)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto *buffer = context->getBuffer(static_cast<WasmBufferType>(type.u64_));
if (buffer == nullptr) {
return WasmResult::NotFound;
}
auto data = context->wasmVm()->getMemory(data_ptr, data_size);
if (!data) {
return WasmResult::InvalidMemoryAccess;
}
return buffer->copyFrom(start, length, data.value());
}
Word http_call(Word uri_ptr, Word uri_size, Word header_pairs_ptr, Word header_pairs_size,
Word body_ptr, Word body_size, Word trailer_pairs_ptr, Word trailer_pairs_size,
Word timeout_milliseconds, Word token_ptr) {
auto *context = contextOrEffectiveContext()->root_context();
auto uri = context->wasmVm()->getMemory(uri_ptr, uri_size);
auto body = context->wasmVm()->getMemory(body_ptr, body_size);
auto header_pairs = context->wasmVm()->getMemory(header_pairs_ptr, header_pairs_size);
auto trailer_pairs = context->wasmVm()->getMemory(trailer_pairs_ptr, trailer_pairs_size);
if (!uri || !body || !header_pairs || !trailer_pairs) {
return WasmResult::InvalidMemoryAccess;
}
auto headers = PairsUtil::toPairs(header_pairs.value());
auto trailers = PairsUtil::toPairs(trailer_pairs.value());
uint32_t token = 0;
// NB: try to write the token to verify the memory before starting the async
// operation.
if (!context->wasm()->setDatatype(token_ptr, token)) {
return WasmResult::InvalidMemoryAccess;
}
auto result =
context->httpCall(uri.value(), headers, body.value(), trailers, timeout_milliseconds, &token);
context->wasm()->setDatatype(token_ptr, token);
return result;
}
Word define_metric(Word metric_type, Word name_ptr, Word name_size, Word metric_id_ptr) {
auto *context = contextOrEffectiveContext();
auto name = context->wasmVm()->getMemory(name_ptr, name_size);
if (!name) {
return WasmResult::InvalidMemoryAccess;
}
uint32_t metric_id = 0;
auto result =
context->defineMetric(static_cast<uint32_t>(metric_type.u64_), name.value(), &metric_id);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->setDatatype(metric_id_ptr, metric_id)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word increment_metric(Word metric_id, int64_t offset) {
auto *context = contextOrEffectiveContext();
return context->incrementMetric(metric_id, offset);
}
Word record_metric(Word metric_id, uint64_t value) {
auto *context = contextOrEffectiveContext();
return context->recordMetric(metric_id, value);
}
Word get_metric(Word metric_id, Word result_uint64_ptr) {
auto *context = contextOrEffectiveContext();
uint64_t value = 0;
auto result = context->getMetric(metric_id, &value);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->setDatatype(result_uint64_ptr, value)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word grpc_call(Word service_ptr, Word service_size, Word service_name_ptr, Word service_name_size,
Word method_name_ptr, Word method_name_size, Word initial_metadata_ptr,
Word initial_metadata_size, Word request_ptr, Word request_size,
Word timeout_milliseconds, Word token_ptr) {
auto *context = contextOrEffectiveContext()->root_context();
auto service = context->wasmVm()->getMemory(service_ptr, service_size);
auto service_name = context->wasmVm()->getMemory(service_name_ptr, service_name_size);
auto method_name = context->wasmVm()->getMemory(method_name_ptr, method_name_size);
auto initial_metadata_pairs =
context->wasmVm()->getMemory(initial_metadata_ptr, initial_metadata_size);
auto request = context->wasmVm()->getMemory(request_ptr, request_size);
if (!service || !service_name || !method_name || !initial_metadata_pairs || !request) {
return WasmResult::InvalidMemoryAccess;
}
uint32_t token = 0;
auto initial_metadata = PairsUtil::toPairs(initial_metadata_pairs.value());
auto result = context->grpcCall(service.value(), service_name.value(), method_name.value(),
initial_metadata, request.value(),
std::chrono::milliseconds(timeout_milliseconds), &token);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->setDatatype(token_ptr, token)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word grpc_stream(Word service_ptr, Word service_size, Word service_name_ptr, Word service_name_size,
Word method_name_ptr, Word method_name_size, Word initial_metadata_ptr,
Word initial_metadata_size, Word token_ptr) {
auto *context = contextOrEffectiveContext()->root_context();
auto service = context->wasmVm()->getMemory(service_ptr, service_size);
auto service_name = context->wasmVm()->getMemory(service_name_ptr, service_name_size);
auto method_name = context->wasmVm()->getMemory(method_name_ptr, method_name_size);
auto initial_metadata_pairs =
context->wasmVm()->getMemory(initial_metadata_ptr, initial_metadata_size);
if (!service || !service_name || !method_name || !initial_metadata_pairs) {
return WasmResult::InvalidMemoryAccess;
}
uint32_t token = 0;
auto initial_metadata = PairsUtil::toPairs(initial_metadata_pairs.value());
auto result = context->grpcStream(service.value(), service_name.value(), method_name.value(),
initial_metadata, &token);
if (result != WasmResult::Ok) {
return result;
}
if (!context->wasm()->setDatatype(token_ptr, token)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word grpc_cancel(Word token) {
auto *context = contextOrEffectiveContext()->root_context();
return context->grpcCancel(token);
}
Word grpc_close(Word token) {
auto *context = contextOrEffectiveContext()->root_context();
return context->grpcClose(token);
}
Word grpc_send(Word token, Word message_ptr, Word message_size, Word end_stream) {
auto *context = contextOrEffectiveContext()->root_context();
auto message = context->wasmVm()->getMemory(message_ptr, message_size);
if (!message) {
return WasmResult::InvalidMemoryAccess;
}
return context->grpcSend(token, message.value(), end_stream != 0U);
}
// WASIp1 typings in comments sourced from
// https://github.com/WebAssembly/wasi-libc/blob/446cb3f1aa21f9b1a1eab372f82d65d19003e924/libc-bottom-half/headers/public/wasi/api.h
// __wasi_errno_t path_open(__wasi_fd_t fd, __wasi_lookupflags_t dirflags, const char *path,
// size_t path_len, __wasi_oflags_t oflags, __wasi_rights_t fs_rights_base, __wasi_rights_t
// fs_rights_inheriting, __wasi_fdflags_t fdflags, __wasi_fd_t *retptr0)
Word wasi_unstable_path_open(Word /*fd*/, Word /*dir_flags*/, Word /*path*/, Word /*path_len*/,
Word /*oflags*/, int64_t /*fs_rights_base*/,
int64_t /*fg_rights_inheriting*/, Word /*fd_flags*/,
Word /*nwritten_ptr*/) {
return 44; // __WASI_ERRNO_NOENT
}
// __wasi_errno_t __wasi_fd_prestat_get(__wasi_fd_t fd, __wasi_prestat_t *retptr0)
Word wasi_unstable_fd_prestat_get(Word /*fd*/, Word /*buf_ptr*/) {
return 8; // __WASI_ERRNO_BADF
}
// __wasi_errno_t __wasi_fd_prestat_dir_name(__wasi_fd_t fd, uint8_t * path, __wasi_size_t path_len)
Word wasi_unstable_fd_prestat_dir_name(Word /*fd*/, Word /*path_ptr*/, Word /*path_len*/) {
return 52; // __WASI_ERRNO_ENOSYS
}
// Implementation of writev-like() syscall that redirects stdout/stderr to Envoy
// logs.
Word writevImpl(Word fd, Word iovs, Word iovs_len, Word *nwritten_ptr) {
auto *context = contextOrEffectiveContext();
// Read syscall args.
uint64_t log_level;
switch (fd) {
case 1 /* stdout */:
log_level = 2; // LogLevel::info
break;
case 2 /* stderr */:
log_level = 4; // LogLevel::error
break;
default:
return 8; // __WASI_EBADF
}
std::string s;
for (size_t i = 0; i < iovs_len; i++) {
auto memslice =
context->wasmVm()->getMemory(iovs + i * 2 * sizeof(uint32_t), 2 * sizeof(uint32_t));
if (!memslice) {
return 21; // __WASI_EFAULT
}
const auto *iovec = reinterpret_cast<const uint32_t *>(memslice.value().data());
if (iovec[1] != 0U /* buf_len */) {
const auto buf = wasmtoh(iovec[0], context->wasmVm()->usesWasmByteOrder());
const auto buf_len = wasmtoh(iovec[1], context->wasmVm()->usesWasmByteOrder());
memslice = context->wasmVm()->getMemory(buf, buf_len);
if (!memslice) {
return 21; // __WASI_EFAULT
}
s.append(memslice.value().data(), memslice.value().size());
}
}
size_t written = s.size();
if (written != 0U) {
// Remove trailing newline from the logs, if any.
if (s[written - 1] == '\n') {
s.erase(written - 1);
}
if (context->log(log_level, s) != WasmResult::Ok) {
return 8; // __WASI_EBADF
}
}
*nwritten_ptr = Word(written);
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_fd_write(_wasi_fd_t fd, const _wasi_ciovec_t *iov,
// size_t iovs_len, size_t* nwritten);
Word wasi_unstable_fd_write(Word fd, Word iovs, Word iovs_len, Word nwritten_ptr) {
auto *context = contextOrEffectiveContext();
Word nwritten(0);
auto result = writevImpl(fd, iovs, iovs_len, &nwritten);
if (result != 0) { // __WASI_ESUCCESS
return result;
}
if (!context->wasmVm()->setWord(nwritten_ptr, Word(nwritten))) {
return 21; // __WASI_EFAULT
}
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(Word /*fd*/, Word /*iovs_ptr*/, Word /*iovs_len*/, Word /*nread_ptr*/) {
// 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(Word /*fd*/, int64_t /*offset*/, Word /*whence*/,
Word /*newoffset_ptr*/) {
auto *context = contextOrEffectiveContext();
context->error("wasi_unstable fd_seek");
return 0;
}
// __wasi_errno_t __wasi_fd_close(__wasi_fd_t fd);
Word wasi_unstable_fd_close(Word /*fd*/) {
auto *context = contextOrEffectiveContext();
context->error("wasi_unstable fd_close");
return 0;
}
// __wasi_errno_t __wasi_fd_fdstat_get(__wasi_fd_t fd, __wasi_fdstat_t *stat)
Word wasi_unstable_fd_fdstat_get(Word fd, Word statOut) {
// We will only support this interface on stdout and stderr
if (fd != 1 && fd != 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 = contextOrEffectiveContext();
context->wasmVm()->setMemory(statOut, 3 * sizeof(uint64_t), &wasi_fdstat);
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_fd_fdstat_set_flags(__wasi_fd_t fd, __wasi_fdflags_t flags)
Word wasi_unstable_fd_fdstat_set_flags(Word /*fd*/, Word /*flags*/) {
// Flags that can be specified: append, dsync, nonblock, rsync, and sync. Proxy-wasm only supports
// STDOUT and STDERR, but none of these flags have any effect in Proxy-Wasm.
return 52; // __WASI_ERRNO_ENOSYS
}
// __wasi_errno_t __wasi_environ_get(char **environ, char *environ_buf);
Word wasi_unstable_environ_get(Word environ_array_ptr, Word environ_buf) {
auto *context = contextOrEffectiveContext();
auto word_size = context->wasmVm()->getWordSize();
const auto &envs = context->wasm()->envs();
for (const auto &e : envs) {
if (!context->wasmVm()->setWord(environ_array_ptr, environ_buf)) {
return 21; // __WASI_EFAULT
}
std::string data;
data.reserve(e.first.size() + e.second.size() + 2);
data.append(e.first);
data.append("=");
data.append(e.second);
data.append({0x0});
if (!context->wasmVm()->setMemory(environ_buf, data.size(), data.c_str())) {
return 21; // __WASI_EFAULT
}
environ_buf = environ_buf.u64_ + data.size();
environ_array_ptr = environ_array_ptr.u64_ + word_size;
}
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_environ_sizes_get(size_t *environ_count, size_t
// *environ_buf_size);
Word wasi_unstable_environ_sizes_get(Word count_ptr, Word buf_size_ptr) {
auto *context = contextOrEffectiveContext();
const auto &envs = context->wasm()->envs();
if (!context->wasmVm()->setWord(count_ptr, Word(envs.size()))) {
return 21; // __WASI_EFAULT
}
size_t size = 0;
for (const auto &e : envs) {
// len(key) + len(value) + 1('=') + 1(null terminator)
size += e.first.size() + e.second.size() + 2;
}
if (!context->wasmVm()->setWord(buf_size_ptr, Word(size))) {
return 21; // __WASI_EFAULT
}
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_args_get(uint8_t **argv, uint8_t *argv_buf);
Word wasi_unstable_args_get(Word /*argv_array_ptr*/, Word /*argv_buf_ptr*/) {
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_args_sizes_get(size_t *argc, size_t *argv_buf_size);
Word wasi_unstable_args_sizes_get(Word argc_ptr, Word argv_buf_size_ptr) {
auto *context = contextOrEffectiveContext();
if (!context->wasmVm()->setWord(argc_ptr, Word(0))) {
return 21; // __WASI_EFAULT
}
if (!context->wasmVm()->setWord(argv_buf_size_ptr, Word(0))) {
return 21; // __WASI_EFAULT
}
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_clock_time_get(uint32_t id, uint64_t precision, uint64_t* time);
Word wasi_unstable_clock_time_get(Word clock_id, uint64_t /*precision*/,
Word result_time_uint64_ptr) {
uint64_t result = 0;
auto *context = contextOrEffectiveContext();
switch (clock_id) {
case 0 /* realtime */:
result = context->getCurrentTimeNanoseconds();
break;
case 1 /* monotonic */:
result = context->getMonotonicTimeNanoseconds();
break;
default:
// process_cputime_id and thread_cputime_id are not supported yet.
return 58; // __WASI_ENOTSUP
}
if (!context->wasm()->setDatatype(result_time_uint64_ptr, result)) {
return 21; // __WASI_EFAULT
}
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_random_get(uint8_t *buf, size_t buf_len);
Word wasi_unstable_random_get(Word result_buf_ptr, Word buf_len) {
if (buf_len > PROXY_WASM_HOST_WASI_RANDOM_GET_MAX_SIZE_BYTES) {
return 28; // __WASI_EINVAL
}
if (buf_len == 0) {
return 0; // __WASI_ESUCCESS
}
auto *context = contextOrEffectiveContext();
std::vector<uint8_t> random(buf_len);
RAND_bytes(random.data(), random.size());
if (!context->wasmVm()->setMemory(result_buf_ptr, random.size(), random.data())) {
return 21; // __WASI_EFAULT
}
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_sched_yield()
Word wasi_unstable_sched_yield() {
// Per POSIX man pages, it is valid to return success if the calling thread is the only thread in
// the highest priority list. This is vacuously true for wasm without threads. There are no valid
// error cases defined.
return 0; // __WASI_ESUCCESS
}
// __wasi_errno_t __wasi_poll_oneoff(const __wasi_subscription_t *in, __wasi_event_t *out,
// __wasi_size_t nsubscriptions, __wasi_size_t *nevents)
Word wasi_unstable_poll_oneoff(Word /*in*/, Word /*out*/, Word /*nsubscriptions*/,
Word /*nevents_ptr*/) {
return 52; // __WASI_ERRNO_ENOSYS
}
// void __wasi_proc_exit(__wasi_exitcode_t rval);
void wasi_unstable_proc_exit(Word /*exit_code*/) {
auto *context = contextOrEffectiveContext();
context->error("wasi_unstable proc_exit");
}
Word pthread_equal(Word left, Word right) { return static_cast<uint64_t>(left == right); }
void emscripten_notify_memory_growth(Word /*memory_index*/) {}
Word set_tick_period_milliseconds(Word period_milliseconds) {
TimerToken token = 0;
return contextOrEffectiveContext()->setTimerPeriod(std::chrono::milliseconds(period_milliseconds),
&token);
}
Word get_current_time_nanoseconds(Word result_uint64_ptr) {
auto *context = contextOrEffectiveContext();
uint64_t result = context->getCurrentTimeNanoseconds();
if (!context->wasm()->setDatatype(result_uint64_ptr, result)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
Word log(Word level, Word address, Word size) {
if (level > static_cast<uint64_t>(LogLevel::Max)) {
return WasmResult::BadArgument;
}
auto *context = contextOrEffectiveContext();
auto message = context->wasmVm()->getMemory(address, size);
if (!message) {
return WasmResult::InvalidMemoryAccess;
}
return context->log(level, message.value());
}
Word get_log_level(Word result_level_uint32_ptr) {
auto *context = contextOrEffectiveContext();
uint32_t level = context->getLogLevel();
if (!context->wasm()->setDatatype(result_level_uint32_ptr, level)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
} // namespace exports
} // namespace proxy_wasm