diff --git a/include/proxy-wasm/exports.h b/include/proxy-wasm/exports.h index f0fa5dbf..5c517176 100644 --- a/include/proxy-wasm/exports.h +++ b/include/proxy-wasm/exports.h @@ -18,6 +18,7 @@ #include #include "include/proxy-wasm/word.h" +#include "include/proxy-wasm/wasm_vm.h" namespace proxy_wasm { @@ -57,7 +58,7 @@ template void marshalPairs(const Pairs &result, char *buffer) { } } -// ABI functions exported from envoy to wasm. +// ABI functions exported from host to wasm. Word get_configuration(void *raw_context, Word address, Word size); Word get_status(void *raw_context, Word status_code, Word address, Word size); @@ -153,5 +154,67 @@ Word pthread_equal(void *, Word left, Word right); // Any currently executing Wasm call context. ::proxy_wasm::ContextBase *ContextOrEffectiveContext(::proxy_wasm::ContextBase *context); +#define FOR_ALL_HOST_FUNCTIONS(_f) \ + _f(log) _f(get_status) _f(set_property) _f(get_property) _f(send_local_response) \ + _f(get_shared_data) _f(set_shared_data) _f(register_shared_queue) _f(resolve_shared_queue) \ + _f(dequeue_shared_queue) _f(enqueue_shared_queue) _f(get_header_map_value) \ + _f(add_header_map_value) _f(replace_header_map_value) _f(remove_header_map_value) \ + _f(get_header_map_pairs) _f(set_header_map_pairs) _f(get_header_map_size) \ + _f(get_buffer_status) _f(get_buffer_bytes) _f(set_buffer_bytes) \ + _f(http_call) _f(grpc_call) _f(grpc_stream) _f(grpc_close) \ + _f(grpc_cancel) _f(grpc_send) _f(set_tick_period_milliseconds) \ + _f(get_current_time_nanoseconds) _f(define_metric) \ + _f(increment_metric) _f(record_metric) _f(get_metric) \ + _f(set_effective_context) _f(done) \ + _f(call_foreign_function) + +#define FOR_ALL_HOST_FUNCTIONS_ABI_SPECIFIC(_f) \ + _f(get_configuration) _f(continue_request) _f(continue_response) _f(clear_route_cache) \ + _f(continue_stream) _f(close_stream) _f(get_log_level) + +#define FOR_ALL_WASI_FUNCTIONS(_f) \ + _f(fd_write) _f(fd_read) _f(fd_seek) _f(fd_close) _f(fd_fdstat_get) _f(environ_get) \ + _f(environ_sizes_get) _f(args_get) _f(args_sizes_get) _f(clock_time_get) _f(random_get) \ + _f(proc_exit) + +// Helpers to generate a stub to pass to VM, in place of a restricted proxy-wasm capability. +#define _CREATE_PROXY_WASM_STUB(_fn) \ + template struct _fn##Stub; \ + template struct _fn##Stub { \ + static Word stub(void *raw_context, Args...) { \ + auto context = exports::ContextOrEffectiveContext( \ + static_cast((void)raw_context, current_context_)); \ + context->wasmVm()->integration()->error( \ + "Attempted call to restricted proxy-wasm capability: proxy_" #_fn); \ + return WasmResult::InternalFailure; \ + } \ + }; +FOR_ALL_HOST_FUNCTIONS(_CREATE_PROXY_WASM_STUB) +FOR_ALL_HOST_FUNCTIONS_ABI_SPECIFIC(_CREATE_PROXY_WASM_STUB) +#undef _CREATE_PROXY_WASM_STUB + +// Helpers to generate a stub to pass to VM, in place of a restricted WASI capability. +#define _CREATE_WASI_STUB(_fn) \ + template struct _fn##Stub; \ + template struct _fn##Stub { \ + static Word stub(void *raw_context, Args...) { \ + auto context = exports::ContextOrEffectiveContext( \ + static_cast((void)raw_context, current_context_)); \ + context->wasmVm()->integration()->error( \ + "Attempted call to restricted WASI capability: " #_fn); \ + return 76; /* __WASI_ENOTCAPABLE */ \ + } \ + }; \ + template struct _fn##Stub { \ + static void stub(void *raw_context, Args...) { \ + auto context = exports::ContextOrEffectiveContext( \ + static_cast((void)raw_context, current_context_)); \ + context->wasmVm()->integration()->error( \ + "Attempted call to restricted WASI capability: " #_fn); \ + } \ + }; +FOR_ALL_WASI_FUNCTIONS(_CREATE_WASI_STUB) +#undef _CREATE_WASI_STUB + } // namespace exports } // namespace proxy_wasm diff --git a/include/proxy-wasm/wasm.h b/include/proxy-wasm/wasm.h index db3ff740..7d0d802f 100644 --- a/include/proxy-wasm/wasm.h +++ b/include/proxy-wasm/wasm.h @@ -42,11 +42,18 @@ using WasmForeignFunction = using WasmVmFactory = std::function()>; using CallOnThreadFunction = std::function)>; +struct SanitizationConfig { + std::vector argument_list; + bool is_allowlist; +}; +using AllowedCapabilitiesMap = std::unordered_map; + // Wasm execution instance. Manages the host side of the Wasm interface. class WasmBase : public std::enable_shared_from_this { public: WasmBase(std::unique_ptr wasm_vm, std::string_view vm_id, - std::string_view vm_configuration, std::string_view vm_key); + std::string_view vm_configuration, std::string_view vm_key, + AllowedCapabilitiesMap allowed_capabilities); WasmBase(const std::shared_ptr &other, WasmVmFactory factory); virtual ~WasmBase(); @@ -92,6 +99,12 @@ class WasmBase : public std::enable_shared_from_this { return nullptr; } + // Capability restriction (restricting/exposing the ABI). + bool capabilityAllowed(std::string capability_name) { + return allowed_capabilities_.empty() || + allowed_capabilities_.find(capability_name) != allowed_capabilities_.end(); + } + virtual ContextBase *createVmContext() { return new ContextBase(this); } virtual ContextBase *createRootContext(const std::shared_ptr &plugin) { return new ContextBase(this, plugin); @@ -225,6 +238,20 @@ class WasmBase : public std::enable_shared_from_this { WasmCallVoid<1> on_log_; WasmCallVoid<1> on_delete_; +#define FOR_ALL_MODULE_FUNCTIONS(_f) \ + _f(validate_configuration) _f(on_vm_start) _f(on_configure) _f(on_tick) _f(on_context_create) \ + _f(on_new_connection) _f(on_downstream_data) _f(on_upstream_data) \ + _f(on_downstream_connection_close) _f(on_upstream_connection_close) _f(on_request_body) \ + _f(on_request_trailers) _f(on_request_metadata) _f(on_response_body) \ + _f(on_response_trailers) _f(on_response_metadata) _f(on_http_call_response) \ + _f(on_grpc_receive) _f(on_grpc_close) _f(on_grpc_receive_initial_metadata) \ + _f(on_grpc_receive_trailing_metadata) _f(on_queue_ready) _f(on_done) \ + _f(on_log) _f(on_delete) + + // Capabilities which are allowed to be linked to the module. If this is empty, restriction + // is not enforced. + AllowedCapabilitiesMap allowed_capabilities_; + std::shared_ptr base_wasm_handle_; // Used by the base_wasm to enable non-clonable thread local Wasm(s) to be constructed. diff --git a/src/wasm.cc b/src/wasm.cc index 3829a89e..6a073dc3 100644 --- a/src/wasm.cc +++ b/src/wasm.cc @@ -100,83 +100,30 @@ void WasmBase::registerCallbacks() { _REGISTER(pthread_equal); #undef _REGISTER -#define _REGISTER_WASI(_fn) \ - wasm_vm_->registerCallback( \ - "wasi_unstable", #_fn, &exports::wasi_unstable_##_fn, \ - &ConvertFunctionWordToUint32::convertFunctionWordToUint32); \ - wasm_vm_->registerCallback( \ - "wasi_snapshot_preview1", #_fn, &exports::wasi_unstable_##_fn, \ - &ConvertFunctionWordToUint32::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); - _REGISTER_WASI(args_sizes_get); - _REGISTER_WASI(clock_time_get); - _REGISTER_WASI(random_get); - _REGISTER_WASI(proc_exit); -#undef _REGISTER_WASI - - // Calls with the "proxy_" prefix. -#define _REGISTER_PROXY(_fn) \ - wasm_vm_->registerCallback( \ - "env", "proxy_" #_fn, &exports::_fn, \ - &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - _REGISTER_PROXY(log); - - _REGISTER_PROXY(get_status); - - _REGISTER_PROXY(set_property); - _REGISTER_PROXY(get_property); - - _REGISTER_PROXY(send_local_response); - - _REGISTER_PROXY(get_shared_data); - _REGISTER_PROXY(set_shared_data); - - _REGISTER_PROXY(register_shared_queue); - _REGISTER_PROXY(resolve_shared_queue); - _REGISTER_PROXY(dequeue_shared_queue); - _REGISTER_PROXY(enqueue_shared_queue); - - _REGISTER_PROXY(get_header_map_value); - _REGISTER_PROXY(add_header_map_value); - _REGISTER_PROXY(replace_header_map_value); - _REGISTER_PROXY(remove_header_map_value); - _REGISTER_PROXY(get_header_map_pairs); - _REGISTER_PROXY(set_header_map_pairs); - _REGISTER_PROXY(get_header_map_size); - - _REGISTER_PROXY(get_buffer_status); - _REGISTER_PROXY(get_buffer_bytes); - _REGISTER_PROXY(set_buffer_bytes); - - _REGISTER_PROXY(http_call); - - _REGISTER_PROXY(grpc_call); - _REGISTER_PROXY(grpc_stream); - _REGISTER_PROXY(grpc_close); - _REGISTER_PROXY(grpc_cancel); - _REGISTER_PROXY(grpc_send); - - _REGISTER_PROXY(set_tick_period_milliseconds); - _REGISTER_PROXY(get_current_time_nanoseconds); - - _REGISTER_PROXY(define_metric); - _REGISTER_PROXY(increment_metric); - _REGISTER_PROXY(record_metric); - _REGISTER_PROXY(get_metric); - - _REGISTER_PROXY(set_effective_context); - _REGISTER_PROXY(done); - _REGISTER_PROXY(call_foreign_function); + // Register the capability with the VM if it has been allowed, otherwise register a stub. +#define _REGISTER(module_name, name_prefix, export_prefix, _fn) \ + if (capabilityAllowed(name_prefix #_fn)) { \ + wasm_vm_->registerCallback( \ + module_name, name_prefix #_fn, &exports::export_prefix##_fn, \ + &ConvertFunctionWordToUint32::convertFunctionWordToUint32); \ + } else { \ + typedef decltype(exports::export_prefix##_fn) export_type; \ + constexpr export_type *stub = &exports::_fn##Stub::stub; \ + wasm_vm_->registerCallback( \ + module_name, name_prefix #_fn, stub, \ + &ConvertFunctionWordToUint32::convertFunctionWordToUint32); \ + } + +#define _REGISTER_WASI_UNSTABLE(_fn) _REGISTER("wasi_unstable", , wasi_unstable_, _fn) +#define _REGISTER_WASI_SNAPSHOT(_fn) _REGISTER("wasi_snapshot_preview1", , wasi_unstable_, _fn) + FOR_ALL_WASI_FUNCTIONS(_REGISTER_WASI_UNSTABLE); + FOR_ALL_WASI_FUNCTIONS(_REGISTER_WASI_SNAPSHOT); +#undef _REGISTER_WASI_UNSTABLE +#undef _REGISTER_WASI_SNAPSHOT + +#define _REGISTER_PROXY(_fn) _REGISTER("env", "proxy_", , _fn) + FOR_ALL_HOST_FUNCTIONS(_REGISTER_PROXY); if (abiVersion() == AbiVersion::ProxyWasm_0_1_0) { _REGISTER_PROXY(get_configuration); @@ -192,6 +139,8 @@ void WasmBase::registerCallbacks() { _REGISTER_PROXY(get_log_level); } #undef _REGISTER_PROXY + +#undef _REGISTER } void WasmBase::getFunctions() { @@ -211,36 +160,21 @@ void WasmBase::getFunctions() { #undef _GET_ALIAS #undef _GET -#define _GET_PROXY(_fn) wasm_vm_->getFunction("proxy_" #_fn, &_fn##_); -#define _GET_PROXY_ABI(_fn, _abi) wasm_vm_->getFunction("proxy_" #_fn, &_fn##_abi##_); - _GET_PROXY(validate_configuration); - _GET_PROXY(on_vm_start); - _GET_PROXY(on_configure); - _GET_PROXY(on_tick); - - _GET_PROXY(on_context_create); - - _GET_PROXY(on_new_connection); - _GET_PROXY(on_downstream_data); - _GET_PROXY(on_upstream_data); - _GET_PROXY(on_downstream_connection_close); - _GET_PROXY(on_upstream_connection_close); - - _GET_PROXY(on_request_body); - _GET_PROXY(on_request_trailers); - _GET_PROXY(on_request_metadata); - _GET_PROXY(on_response_body); - _GET_PROXY(on_response_trailers); - _GET_PROXY(on_response_metadata); - _GET_PROXY(on_http_call_response); - _GET_PROXY(on_grpc_receive); - _GET_PROXY(on_grpc_close); - _GET_PROXY(on_grpc_receive_initial_metadata); - _GET_PROXY(on_grpc_receive_trailing_metadata); - _GET_PROXY(on_queue_ready); - _GET_PROXY(on_done); - _GET_PROXY(on_log); - _GET_PROXY(on_delete); + // Try to point the capability to one of the module exports, if the capability has been allowed. +#define _GET_PROXY(_fn) \ + if (capabilityAllowed("proxy_" #_fn)) { \ + wasm_vm_->getFunction("proxy_" #_fn, &_fn##_); \ + } else { \ + _fn##_ = nullptr; \ + } +#define _GET_PROXY_ABI(_fn, _abi) \ + if (capabilityAllowed("proxy_" #_fn)) { \ + wasm_vm_->getFunction("proxy_" #_fn, &_fn##_abi##_); \ + } else { \ + _fn##_abi##_ = nullptr; \ + } + + FOR_ALL_MODULE_FUNCTIONS(_GET_PROXY); if (abiVersion() == AbiVersion::ProxyWasm_0_1_0) { _GET_PROXY_ABI(on_request_headers, _abi_01); @@ -259,6 +193,7 @@ WasmBase::WasmBase(const std::shared_ptr &base_wasm_handle, Wasm : std::enable_shared_from_this(*base_wasm_handle->wasm()), vm_id_(base_wasm_handle->wasm()->vm_id_), vm_key_(base_wasm_handle->wasm()->vm_key_), started_from_(base_wasm_handle->wasm()->wasm_vm()->cloneable()), + allowed_capabilities_(base_wasm_handle->wasm()->allowed_capabilities_), base_wasm_handle_(base_wasm_handle) { if (started_from_ != Cloneable::NotCloneable) { wasm_vm_ = base_wasm_handle->wasm()->wasm_vm()->clone(); @@ -273,8 +208,10 @@ WasmBase::WasmBase(const std::shared_ptr &base_wasm_handle, Wasm } WasmBase::WasmBase(std::unique_ptr wasm_vm, std::string_view vm_id, - std::string_view vm_configuration, std::string_view vm_key) + std::string_view vm_configuration, std::string_view vm_key, + AllowedCapabilitiesMap allowed_capabilities) : vm_id_(std::string(vm_id)), vm_key_(std::string(vm_key)), wasm_vm_(std::move(wasm_vm)), + allowed_capabilities_(std::move(allowed_capabilities)), vm_configuration_(std::string(vm_configuration)), vm_id_handle_(getVmIdHandle(vm_id)) { if (!wasm_vm_) { failed_ = FailState::UnableToCreateVM;