Repository navigation
HIP 0008: Postcard serialization for host-guest messages #1999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ludfjig
wants to merge
1
commit into
hyperlight-dev:main
Choose a base branch
from
ludfjig:ll/hip-postcard
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+142
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| # HIP 0008 - Postcard serialization for host-guest messages | ||
|
|
||
| <!-- toc --> | ||
| - [Summary](#summary) | ||
| - [Motivation](#motivation) | ||
| - [Goals](#goals) | ||
| - [Non-Goals](#non-goals) | ||
| - [Proposal](#proposal) | ||
| - [Design Details](#design-details) | ||
| - [Results](#results) | ||
| - [Drawbacks](#drawbacks) | ||
| - [Alternatives](#alternatives) | ||
| <!-- /toc --> | ||
|
|
||
| ## Summary | ||
|
|
||
| Replace FlatBuffers with [serde](https://serde.rs) and | ||
| [postcard](https://docs.rs/postcard) for the control data of host-guest | ||
| messages: function calls, results, guest logs and guest trace events. Byte | ||
| payloads stay outside the control data as external values, as described in | ||
| [HIP 0002](../0002-envelope-external-byte-stream/README.md). The Rust message | ||
| types become the schema. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Hyperlight uses none of FlatBuffers' strengths: | ||
|
|
||
| * **Reading in place.** The host can't parse guest memory in place safely, | ||
| because the guest can change it during parsing. The host copies control data | ||
| out first. | ||
| * **Schema evolution.** Host and guest must be built from the same Hyperlight | ||
| version, which the host enforces when it loads a guest. | ||
| * **Large payloads.** Byte values travel as external values since HIP 0002. | ||
| * **Multiple languages.** Only Rust encodes and decodes messages. C guests use | ||
| the Rust C API, and other languages can do the same. | ||
|
|
||
| Hyperlight still pays these costs for FlatBuffers: | ||
|
|
||
| * About 6,100 lines of generated code, a pinned `flatc` and a manual | ||
| regeneration step. | ||
| * Conversion code between each generated type and its Rust type, and a | ||
| verifier pass on every decode. | ||
| * A hand-tuned size estimator for trace events, with its own fuzz target. | ||
| * Slow encoding. Builder overhead dominates for messages this small. | ||
|
|
||
| serde lets the Rust types be the schema. Deriving `Serialize` and | ||
| `Deserialize` replaces the generated code and the conversions, and decoding | ||
| can borrow strings from the received buffer. | ||
|
|
||
| postcard fits this use: | ||
|
|
||
| * **Not self-describing.** Formats such as CBOR and MessagePack tag each value | ||
| with its type. Host and guest share the Rust types, so postcard encodes only | ||
| the values, with varint integers and lengths. | ||
| * **Stable, specified format.** postcard 1.0 has a written | ||
| [wire specification](https://postcard.jamesmunns.com/wire-format) that stays | ||
| fixed until 2.0. Host and guest can build with different postcard 1.x | ||
| releases. bitcode, for example, may change its format in any major version. | ||
| * **Built for `no_std`.** postcard targets embedded devices first, so it runs | ||
| in the guest without the standard library. | ||
|
|
||
| ### Goals | ||
|
|
||
| * Faster encoding and decoding of control data. | ||
| * Less code: no generated code, conversions or size estimator. | ||
| * Equal or stronger validation of untrusted guest data. | ||
|
|
||
| ### Non-Goals | ||
|
|
||
| * Wire compatibility across Hyperlight versions. | ||
| * Changing how byte payloads travel. | ||
|
|
||
| ## Proposal | ||
|
|
||
| Each message's control data is one postcard value. Message types derive | ||
| `Serialize` and `Deserialize`. Decoding borrows the host function name and the | ||
| strings in logs and trace events. Byte values appear in control data only as | ||
| their length. | ||
|
|
||
| The message header carries the control data length in a new `control_len` | ||
| field. The header grows from 12 to 16 bytes: | ||
|
|
||
| ```rust | ||
| #[repr(C)] | ||
| pub struct MsgHeader { | ||
| pub kind: MsgKind, | ||
| reserved: [u8; 3], | ||
| pub cid: u32, | ||
| /// Control data plus external byte values. | ||
| pub payload_len: u32, | ||
| /// Control data only. Never exceeds `payload_len`. New in this HIP. | ||
| pub control_len: u32, | ||
| } | ||
| ``` | ||
|
|
||
| A postcard value marks its own end, but the host must copy control data out | ||
| of guest memory before it parses it. It needs the length to copy exactly the | ||
| control data in one read. Without it, the host would have to read guest memory | ||
| byte by byte while parsing, or copy the external bytes as well. | ||
|
|
||
| ## Design Details | ||
|
|
||
| Message types: | ||
|
|
||
| * **Function call:** name, call type, return type and parameters. | ||
| * **Value:** `Int`, `UInt`, `Long`, `ULong`, `Float`, `Double`, `String`, | ||
| `Bool`, `Void`, or `ExternalBytes { length, chunked }` for an external byte | ||
| value. `chunked` selects `ByteChunks` or `VecBytes` on the receiver. | ||
| * **Result:** `Ok(value)` or `Err { code, message }`. | ||
| * **Guest log:** message, source, level, caller, source file and line. | ||
| * **Trace batch:** postcard events back to back in one buffer of up to | ||
| 4 KiB. The encoder sends the batch when the next event does not fit, so no | ||
| size estimate is needed. | ||
|
Comment on lines
+111
to
+113
|
||
|
|
||
| FlatBuffers is removed: the generated code, `src/schema`, the `flatbuffers` | ||
| dependency, the `flatc` recipes and the FlatBuffers docs. | ||
|
|
||
| ## Results | ||
|
|
||
| Preliminary estimates from short benchmark runs: | ||
|
|
||
| | Benchmark | KVM | MSHV | | ||
| |---|---|---| | ||
| | Guest call | about −5% | −10% to −18% | | ||
| | Guest call with a host function call | −9% to −29% | −15% to −21% | | ||
| | 24 KiB in, 8 KiB out | 0% to −4% | −3% to −5% | | ||
| | Encode control data | −53% to −73% | −51% to −75% | | ||
|
|
||
| ## Drawbacks | ||
|
|
||
| * No language-neutral schema. A non-Rust encoder or decoder would need to | ||
| implement postcard's wire format. | ||
|
|
||
| ## Alternatives | ||
|
|
||
| * **Keep FlatBuffers:** the costs listed in [Motivation](#motivation). | ||
| * **rkyv:** built for reading in place, which the host can't use because it | ||
| must copy first. | ||
| * **No control data length:** postcard needs none, but the host does. See | ||
| [Proposal](#proposal). | ||
| * **A hand-written codec:** possibly slightly faster for these small | ||
| messages, but more code to own and fuzz. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.