Re: [PATCH v36 21/24] x86/vdso: Implement a vDSO for Intel SGX enclave call

From: Nathaniel McCallum
Date: Mon Aug 17 2020 - 09:15:17 EST


On Mon, Aug 10, 2020 at 7:09 PM Andy Lutomirski <luto@xxxxxxxxxx> wrote:
>
> On Thu, Aug 6, 2020 at 7:55 AM Nathaniel McCallum <npmccallum@xxxxxxxxxx> wrote:
> >
> > In a past revision of this patch, I had requested a void *misc
> > parameter that could be passed through vdso_sgx_enter_enclave_t into
> > sgx_enclave_exit_handler_t. This request encountered some push back
> > and I dropped the issue. However, I'd like to revisit it or something
> > similar.
>
> Why do you need an exit handler at all? IIRC way back when I
> suggested that we simply not support it at all. If you want to
> call__vdso_sgx_enter_enclave() in a loop, call it in a loop. If you
> want to wrap it intelligently in Rust, you don't want a callback
> anyway -- that forces you have an FFI (or non-Rust, anyway) frame on
> the stack, which interacts poorly with panic handling and prevents you
> from using await in your Rust callback handler. If, on the other
> hand, you just call __vdso_sg_enter_enclave() in a loop, all these
> problems go away and, if you really want, you can pass in a callback
> in Rust and call the callback from Rust.
>
> What am I missing? I still don't really understand why we are
> supporting this mechanism at all. Just the asm code to invoke the
> callback seems to be about half of the entire function.

There are three ways to pass state between the enclave and the outside world:
1. A pre-allocated memory block at enclave creation time.
2. A contract for pushing values onto the stack during entry/exit.
3. All registers and flags besides rax, rbx, and rcx.

Under the current vDSO function:

#1 is completely possible without a handler. The challenge is how to
communicate the address of this memory to the enclave. This can be
accomplished by a parameter in a measured block or by convention.
Otherwise, it needs to use #2 or #3 to communicate the address of the
block.

#2 requires a handler written in assembly. The semantics are well known.

#3 is possible without a handler, but only for the subset of the
registers allowed by the calling convention. However, with a handler
written in assembly you can pass both in and out the full range of
registers. To do this, the assembly handler needs a pointer to a
buffer to save the registers into. How does it get said pointer?
Currently: offsetof, which Rust doesn't support.

So when I want to write a general purpose library to expose this,
which method should I expose? In particular, I want to give a single
"best practice" workflow to library consumers so that the startup
process is easy. Since #1 requires #3 to avoid a bad developer
experience, I'm inclined to provide #3.

I can provide the calling convention registers easily. But this limits
the output registers to two (practically, one for a variety of
reasons). I might just accept that. But with a misc pointer to the
handler, I could expand the one or two return registers to 11 (rdi,
rsi, rdx, r8-r15).

Put simply: I think the one extra line of assembly is worth the
flexibility it gives to consumers of this interface. In particular, it
improves the FFI experience greatly since one doesn't have to resort
to offsetof tricks to get some additional context into the handler.