Re: [PATCH v33 11/21] x86/sgx: Linux Enclave Driver

From: Jarkko Sakkinen
Date: Fri Jul 03 2020 - 21:43:05 EST


On Sat, Jun 27, 2020 at 07:43:35PM +0200, Borislav Petkov wrote:
> On Thu, Jun 18, 2020 at 01:08:33AM +0300, Jarkko Sakkinen wrote:
> > +static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
> > + void *token)
> > +{
> > + u64 mrsigner[4];
> > + int ret;
> > + int i;
> > + int j;
> > +
> > + /* Check that the required attributes have been authorized. */
> > + if (encl->secs_attributes & ~encl->allowed_attributes)
> > + return -EACCES;
> > +
> > + ret = sgx_get_key_hash(sigstruct->modulus, mrsigner);
> > + if (ret)
> > + return ret;
> > +
> > + mutex_lock(&encl->lock);
> > +
> > + if (atomic_read(&encl->flags) & SGX_ENCL_INITIALIZED) {
> > + ret = -EFAULT;
> > + goto err_out;
> > + }
>
> That test should be the first thing this function or its caller does.

Fixed.

>
> > + for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) {
> > + for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) {
>
> Ew, what's that double-loop for?
>
> It tries to init an enclave a bunch of times. Why does it need to init
> more than once?

>From SDM:

"Periodically, EINIT polls for certain asynchronous events. If such an
event is detected, it completes with failure code (ZF=1 and RAX =
SGX_UNMASKED_EVENT), and RIP is incremented to point to the next
instruction. These events includes external interrupts, non-maskable
interrupts, system-management interrupts, machine checks, INIT signals,
and the VMX-preemption timer. EINIT does not fail if the pending event
is inhibited (e.g., external interrupts could be inhibited due to
blocking by MOV SS blocking or by STI)."

Not exactly sure though why this must be polled inside the kernel though.

>
> > + ret = sgx_einit(sigstruct, token, encl->secs.epc_page,
> > + mrsigner);
> > + if (ret == SGX_UNMASKED_EVENT)
> > + continue;
> > + else
> > + break;
> > + }
> > +
> > + if (ret != SGX_UNMASKED_EVENT)
> > + break;
> > +
> > + msleep_interruptible(SGX_EINIT_SLEEP_TIME);
> > +
> > + if (signal_pending(current)) {
> > + ret = -ERESTARTSYS;
> > + goto err_out;
> > + }
> > + }
> > +
> > + if (ret & ENCLS_FAULT_FLAG) {
> > + if (encls_failed(ret))
> > + ENCLS_WARN(ret, "EINIT");
> > +
> > + sgx_encl_destroy(encl);
> > + ret = -EFAULT;
> > + } else if (ret) {
> > + pr_debug("EINIT returned %d\n", ret);
> > + ret = -EPERM;
> > + } else {
> > + atomic_or(SGX_ENCL_INITIALIZED, &encl->flags);
> > + }
> > +
> > +err_out:
> > + mutex_unlock(&encl->lock);
> > + return ret;
> > +}
> > +
> > +/**
> > + * sgx_ioc_enclave_init - handler for %SGX_IOC_ENCLAVE_INIT
> > + *
> > + * @filep: open file to /dev/sgx
>
> @encl: pointer to an enclave instance (via ioctl() file pointer)
>
> > + * @arg: userspace pointer to a struct sgx_enclave_init instance
> > + *
> > + * Flush any outstanding enqueued EADD operations and perform EINIT. The
> > + * Launch Enclave Public Key Hash MSRs are rewritten as necessary to match
> > + * the enclave's MRSIGNER, which is caculated from the provided sigstruct.
> > + *
> > + * Return:
> > + * 0 on success,
> > + * SGX error code on EINIT failure,
> > + * -errno otherwise
> > + */
> > +static long sgx_ioc_enclave_init(struct sgx_encl *encl, void __user *arg)
> > +{
> > + struct sgx_sigstruct *sigstruct;
> > + struct sgx_enclave_init einit;
> > + struct page *initp_page;
> > + void *token;
> > + int ret;
> > +
> > + if (!(atomic_read(&encl->flags) & SGX_ENCL_CREATED))
>
> Might just as well check the other flags: doing EINIT on an already
> initialized enclave - SGX_ENCL_INITIALIZED - is perhaps a nono or
> similarly on a SGX_ENCL_DEAD enclave.
>
> And you could do similar sanity checks in the other ioctl functions.

Agreed (see my earlier response, let's continue this discussion there).

/Jarkko