Skip to content

Commit b6c4b81

Browse files
authored
Handle Wasm errors by failing closed or open. (proxy-wasm#29)
Signed-off-by: John Plevyak <[email protected]>
1 parent bf829e5 commit b6c4b81

File tree

9 files changed

+191
-163
lines changed

9 files changed

+191
-163
lines changed

include/proxy-wasm/context.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,19 @@ class WasmVm;
4444
* @param root_id is an identifier for the in VM handlers for this plugin.
4545
* @param vm_id is a string used to differentiate VMs with the same code and VM configuration.
4646
* @param plugin_configuration is configuration for this plugin.
47+
* @param fail_open if true the plugin will pass traffic as opposed to close all streams.
4748
*/
4849
struct PluginBase {
4950
PluginBase(string_view name, string_view root_id, string_view vm_id,
50-
string_view plugin_configuration)
51+
string_view plugin_configuration, bool fail_open)
5152
: name_(std::string(name)), root_id_(std::string(root_id)), vm_id_(std::string(vm_id)),
52-
plugin_configuration_(plugin_configuration) {}
53+
plugin_configuration_(plugin_configuration), fail_open_(fail_open) {}
5354

5455
const std::string name_;
5556
const std::string root_id_;
5657
const std::string vm_id_;
5758
std::string plugin_configuration_;
59+
const bool fail_open_;
5860
const std::string &log_prefix() const { return log_prefix_; }
5961

6062
private:
@@ -216,6 +218,8 @@ class ContextBase : public RootInterface,
216218
error("unimplemented proxy-wasm API");
217219
return WasmResult::Unimplemented;
218220
}
221+
bool isFailed();
222+
bool isFailOpen() { return plugin_->fail_open_; }
219223

220224
//
221225
// General Callbacks.
@@ -308,6 +312,7 @@ class ContextBase : public RootInterface,
308312
string_view /* details */) override {
309313
return unimplemented();
310314
}
315+
void failStream(WasmStreamType stream_type) override { closeStream(stream_type); }
311316

312317
// Shared Data
313318
WasmResult getSharedData(string_view key,
@@ -353,12 +358,7 @@ class ContextBase : public RootInterface,
353358
protected:
354359
friend class WasmBase;
355360

356-
// NB: initializeRootBase is non-virtual and can be called in the constructor without ambiguity.
357361
void initializeRootBase(WasmBase *wasm, std::shared_ptr<PluginBase> plugin);
358-
// NB: initializeRoot is virtual and should be called only outside of the constructor.
359-
virtual void initializeRoot(WasmBase *wasm, std::shared_ptr<PluginBase> plugin) {
360-
initializeRootBase(wasm, plugin);
361-
}
362362
std::string makeRootLogPrefix(string_view vm_id) const;
363363

364364
WasmBase *wasm_{nullptr};

include/proxy-wasm/context_interface.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,9 @@ struct StreamInterface {
315315
// Close a stream.
316316
virtual WasmResult closeStream(WasmStreamType type) = 0;
317317

318+
// Called when a Wasm VM has failed and the plugin is set to fail closed.
319+
virtual void failStream(WasmStreamType) = 0;
320+
318321
/**
319322
* Provides a BufferInterface to be used to return buffered data to the VM.
320323
* @param type is the type of buffer to provide.

include/proxy-wasm/null_vm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct NullVm : public WasmVm {
3636
Cloneable cloneable() override { return Cloneable::InstantiatedModule; };
3737
std::unique_ptr<WasmVm> clone() override;
3838
bool load(const std::string &code, bool allow_precompiled) override;
39-
void link(string_view debug_name) override;
39+
bool link(string_view debug_name) override;
4040
uint64_t getMemorySize() override;
4141
optional<string_view> getMemory(uint64_t pointer, uint64_t size) override;
4242
bool setMemory(uint64_t pointer, uint64_t size, const void *data) override;

include/proxy-wasm/wasm.h

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
7171
return nullptr;
7272
}
7373
uint32_t allocContextId();
74+
bool isFailed() { return failed_; }
7475

7576
const std::string &code() const { return code_; }
7677
const std::string &vm_configuration() const;
@@ -91,20 +92,17 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
9192
unimplemented();
9293
return nullptr;
9394
}
94-
// NB: if plugin is nullptr, then a VM Context is returned.
95-
virtual ContextBase *createContext(std::shared_ptr<PluginBase> plugin) {
96-
if (plugin)
97-
return new ContextBase(this, plugin);
98-
return new ContextBase(this);
95+
96+
virtual ContextBase *createVmContext() { return new ContextBase(this); }
97+
virtual ContextBase *createRootContext(const std::shared_ptr<PluginBase> &plugin) {
98+
return new ContextBase(this, plugin);
99+
}
100+
virtual ContextBase *createContext(const std::shared_ptr<PluginBase> &plugin) {
101+
return new ContextBase(this, plugin);
99102
}
100103
virtual void setTimerPeriod(uint32_t root_context_id, std::chrono::milliseconds period) {
101104
timer_period_[root_context_id] = period;
102105
}
103-
virtual void error(string_view message) {
104-
std::cerr << message << "\n";
105-
abort();
106-
}
107-
virtual void unimplemented() { error("unimplemented proxy-wasm API"); }
108106

109107
// Support functions.
110108
//
@@ -117,12 +115,12 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
117115

118116
WasmForeignFunction getForeignFunction(string_view function_name);
119117

120-
// For testing.
121-
//
122-
void setContextForTesting(ContextBase *context) { contexts_[context->id()] = context; }
123-
// Returns false if onStart returns false.
124-
bool startForTesting(std::unique_ptr<ContextBase> root_context,
125-
std::shared_ptr<PluginBase> plugin);
118+
void fail(string_view message) {
119+
error(message);
120+
failed_ = true;
121+
}
122+
virtual void error(string_view message) { std::cerr << message << "\n"; }
123+
virtual void unimplemented() { error("unimplemented proxy-wasm API"); }
126124

127125
bool getEmscriptenVersion(uint32_t *emscripten_metadata_major_version,
128126
uint32_t *emscripten_metadata_minor_version,
@@ -238,6 +236,7 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
238236
std::string code_;
239237
std::string vm_configuration_;
240238
bool allow_precompiled_ = false;
239+
bool failed_ = false; // The Wasm VM has experienced a fatal error.
241240

242241
bool is_emscripten_ = false;
243242
uint32_t emscripten_metadata_major_version_ = 0;
@@ -259,7 +258,13 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
259258
class WasmHandleBase : public std::enable_shared_from_this<WasmHandleBase> {
260259
public:
261260
explicit WasmHandleBase(std::shared_ptr<WasmBase> wasm_base) : wasm_base_(wasm_base) {}
262-
~WasmHandleBase() { wasm_base_->startShutdown(); }
261+
~WasmHandleBase() {
262+
if (wasm_base_) {
263+
wasm_base_->startShutdown();
264+
}
265+
}
266+
267+
void kill() { wasm_base_ = nullptr; }
263268

264269
std::shared_ptr<WasmBase> &wasm() { return wasm_base_; }
265270

@@ -276,8 +281,7 @@ using WasmHandleCloneFactory =
276281
// Returns nullptr on failure (i.e. initialization of the VM fails).
277282
std::shared_ptr<WasmHandleBase>
278283
createWasm(std::string vm_key, std::string code, std::shared_ptr<PluginBase> plugin,
279-
WasmHandleFactory factory, bool allow_precompiled,
280-
std::unique_ptr<ContextBase> root_context_for_testing = nullptr);
284+
WasmHandleFactory factory, WasmHandleCloneFactory clone_factory, bool allow_precompiled);
281285
// Get an existing ThreadLocal VM matching 'vm_id' or nullptr if there isn't one.
282286
std::shared_ptr<WasmHandleBase> getThreadLocalWasm(string_view vm_id);
283287
// Get an existing ThreadLocal VM matching 'vm_id' or create one using 'base_wavm' by cloning or by
@@ -286,6 +290,9 @@ std::shared_ptr<WasmHandleBase>
286290
getOrCreateThreadLocalWasm(std::shared_ptr<WasmHandleBase> base_wasm,
287291
std::shared_ptr<PluginBase> plugin, WasmHandleCloneFactory factory);
288292

293+
// Clear Base Wasm cache and the thread-local Wasm sandbox cache for the calling thread.
294+
void clearWasmCachesForTesting();
295+
289296
inline const std::string &WasmBase::vm_configuration() const {
290297
if (base_wasm_handle_)
291298
return base_wasm_handle_->wasm()->vm_configuration_;

include/proxy-wasm/wasm_vm.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,9 @@ class WasmVm {
171171
* should be loaded and the ABI callbacks registered (see above). Linking should be done once
172172
* after load().
173173
* @param debug_name user-provided name for use in log and error messages.
174+
* @return whether or not the link was successful.
174175
*/
175-
virtual void link(string_view debug_name) = 0;
176+
virtual bool link(string_view debug_name) = 0;
176177

177178
/**
178179
* Get size of the currently allocated memory in the VM.
@@ -249,12 +250,24 @@ class WasmVm {
249250
FOR_ALL_WASM_VM_IMPORTS(_REGISTER_CALLBACK)
250251
#undef _REGISTER_CALLBACK
251252

253+
bool isFailed() { return failed_; }
254+
void fail(string_view message) {
255+
error(message);
256+
failed_ = true;
257+
if (fail_callback_) {
258+
fail_callback_();
259+
}
260+
}
261+
void setFailCallback(std::function<void()> fail_callback) { fail_callback_ = fail_callback; }
262+
252263
// Integrator operations.
253264
std::unique_ptr<WasmVmIntegration> &integration() { return integration_; }
254265
void error(string_view message) { integration()->error(message); }
255266

256267
private:
257268
std::unique_ptr<WasmVmIntegration> integration_;
269+
bool failed_ = false;
270+
std::function<void()> fail_callback_;
258271
};
259272

260273
// Thread local state set during a call into a WASM VM so that calls coming out of the

0 commit comments

Comments
 (0)