6/29/2023
This proposal attempts to do two things:
Sandbox
APIThis proposal is linked from issue #754
Currently, in the dev
branch, we have several different types of Sandbox
es, detailed in this section.
UninitializedSandbox
As its name implies, the UnintializedSandbox
- represents a sandbox that has not yet been initialized and thus is not ready to execute guest code.
The primary purposes of this component are twofold:
Sandbox
As described in the previous section, the UninitializedSandbox
has an initialization function attached to it. The return value of this function is a Sandbox
type, which represents a fully initialized Hyperlight virtual machine (VM) inside of which the user can execute code.
Ths Sandbox
has two variants of note:
Sandbox
is destroyed. No further action is possibleAs implied easlier, this overall design was put in place to fulfill the requirements primarily of the first major user of Hyperlight, Hyperlight-WASM. As we look toward the future of Hyperlight and its users, we see several developments, listed below, that require a more flexible, generic design:
This document attempts to define a clear separation between three different types of code, listed below.
This distinction is important because this proposal moves a significant amount of implementation and API design from Hyperlight-Core to libraries.
As requirements have changed over time, one general idea continues to arise. We would like to implement a generic way to implement a sandbox that is "partially loaded", allow libraries to load only part of a guest's code and/or memory, and then load the remainder at a later time. What "partially loaded" actually means, and how to load the remainder of a guest depends greatly on the library.
The Hyperlight-WASM project would enlist this feature set by doing the following steps, in both forwards and backwards order:
Sandbox
from (1)This feature set would also allow an end user to "roll back" from a fully-loaded Sandbox
in (2) to a partially-loaded Sandbox
in (1) that has just the WAMR-based guest, or rollback from one intermediate state to another in other circumstances.
In C#, we have implemented a single Sandbox
class that internally has a recycleAfterRun
boolean flag to indicate whether that Sandbox
can be "recycled", and memory can be swapped in and out of it (this flag is passed in via the SandboxRunOptions.RecycleAfterRun
argument). This strategy solves the hot-swap functionality described above.
As implied by the "future requirements" section, though, the C# Sandbox
does not directly allow you to "partially load" it. In our Rust implementation, however, we have started to represent a "partial" Sandbox
state with our UninitializedSandbox
struct.
This design currently limits us in several ways with respect to the "future requirements" section:
N
partial statesFrom our requirements and our plans for developing the Hyperlight ecosystem going forward, a few themes emerge:
I propose Hyperlight provide a set of traits and reusable logic to do both these things. The code that follows illustrates this approach.
Pull Request #756 contains the above code and an extensive code sample to model Hyperlight-WASM.
It's intended that library authors will create structs as necessary to represent the states specific to their application's needs. Each state will implement Sandbox
at minimum, and will implement EvolvableSandbox
and DevolvableSandbox
as necessary to specify each state transition. If a state transition is not implemented, it will be disallowed by the Rust compiler. A simple example follows:
We've shown an example set of possible states and transitions above, but it's important to note almost all end users will interact with libraries like Hyperlight-WASM rather than Hyperlight Core.
Thus, for all practical purposes, this API, along with much of the rest of Hyperlight's public API, is directed only at library authors. I've tried to design the components in this proposal to enable library authors (like us, as we build Hyperlight-WASM) to build ergonomic, intuitive APIs for their end users.
To this end, I believe we can make available a set of "building block" APIs to ease the process of building the desired Sandbox
, EvolvableSandbox
, DevolvableSandbox
etc… implementations in a library like Hyperlight-WASM. I believe as Hyperlight Core is used more widely, our ability to provide well-design building block APIs alongside our sandbox/transition abstractions will become more essential.
The sub-sections herein describe these building blocks in detail.
Sandbox
, OneShotSandbox
, and ReusableSandbox
Most of the primary functionality within these implementations is concerned with dispatching calls to guest functions from the host, and vice-versa. The major pieces of this process are as follows:
If we provide a construct to library authors that provides a set of easy-to-use, safe APIs to do these things, I believe we'll make the task of implementing these three traits nearly trivial.
EvolvableSandbox
and DevolvableSandbox
These traits represent state transitions, so any implementation thereof represents an action of moving from one sandbox to the next. Generally speaking, this doesn't involve a lot of work, but it may involve taking memory snapshots and/or changing the set of registered host functions (e.g. functions the guest can call on the host) available to guests.
I expect if we implement utilities for the previous section, we will have most of what we need to implement utilities for this one.
Those familiar with Hyperlight's C# codebase will notice this proposal represents the conceptually-large, but practically-small, task of breaking up the monolithic Sandbox
C# class into utility functions as we rebuild them in Rust, and ending up with no single Sandbox
implementation. In fact, this document proposes we end up with no single Sandbox
-like "entrypoint" to Hyperlight whatsoever.
This design would enable a kind of inversion-of-control between Hyperlight-Core and its libraries like Hyperlight-WASM. If this proposal is implemented, library authors, like us as we build Hyperlight-WASM (after we rewrite it in Rust), would use the aforementioned "building blocks" to build a set of Transition
and Sandbox
implementations, then use those to implement a single HyerlightWasm.Sandbox
class in that library.
I believe this design in Hyperlight Core can safely scale to a wide, diverse set of libraries and can accommodate arbitrary design changes within any one of them.