API for the hypervisor-microkernel boundary
sallyport
is a protocol crate for proxying service requests (such as syscalls) from an Enarx Keep
to the host. A sally port is a secure gateway through
which a defending army might "sally forth" from the protection of their fortification.
An astute reader may notice that sallyport
is a thin layer around the Linux syscall ABI as it is
predicated on the conveyance of a service request number (such as rax
for x86_64) as well as the
maximum number (6) of syscall parameter registers:
| Architecture | arg 1 | arg 2 | arg 3 | arg 4 | arg 5 | arg 6 | | ------------ | ----- | ----- | ----- | ----- | ----- | ----- | | x86_64 | rdi | rsi | rdx | r10 | r8 | r9 |
The above table was taken from the syscall(2) man page
Note that sallyport
is meant to generalize over all architectures that Enarx anticipates proxying
syscalls to, not just x86_64 which was listed in the above table for illustration purposes.
sallyport
works by providing the host with the most minimal register context it requires to
perform the syscall on the Keep's behalf. In doing so, the host can immediately call the desired
syscall without any additional logic required. This "register context" is known as a Message
in
sallyport
parlance.
The Message
union has two representations:
Request
: The register context needed to perform a request or syscall. This includes an identifier
and up to the 6 maximum syscall parameter registers expected by the Linux syscall ABI.Reply
: A response from the host. This representation exists to cater to how each architecture
indicates a return value.The Message
union serves as the header for a Block
struct, which will be examined next.
The Block
struct is a page-sized buffer which must be written to a page that is accessible
to both the host and the Keep to facilitate request proxying. The region of memory that is
left over after storing the Message
header on the block should be used for storing additional
parameters that must be shared with the host so it can complete the service request. In the
context of a syscall, this would be the sequence bytes to be written with a write
syscall.
If the Keep forms a request that requires additional parameter data to be written to the Block
,
the register context must reflect this. For example, the second parameter to the write
syscall
is a pointer to the string of bytes to be written. In this case, the Keep
must ensure the
second register parameter points to the location where the bytes have been written within the Block
,
NOT a pointer to its protected address space. Furthermore, once the request has been proxied, it is
the Keep's responsibility to propagate any potentially modified data back to its protected pages.
Here's an example of how the sallyport
protocol might be used to proxy a syscall between
the host and a protected virtual machine:
write
syscall.write
syscall.Block
onto the page it shares with the untrusted host.Block
. It is now
accessible to the host.Message
header to be a Request
variant. In the case of the write
syscall, the shim:
num
to the Linux integral value for SYS_write
.arg[0]
= The file descriptor to write to.arg[1]
= The address within the Block
where the bytes have been copied to.arg[2]
= The number of bytes that the write
syscall should emit from the bytes pointed to
in the second parameter.Block
's Message
header.Block
's header and set it to a
Reply
variant of the Message
union and write the syscall return code to it.Reply
in the Message
header of the Block
and propagates any mutated data back to
the protected address space. It may then return control to its workload.License: Apache-2.0