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

From: Sean Christopherson
Date: Fri Jun 26 2020 - 10:16:31 EST


On Fri, Jun 26, 2020 at 11:14:19AM +0200, Borislav Petkov wrote:
> On Thu, Jun 18, 2020 at 01:08:33AM +0300, Jarkko Sakkinen wrote:
> > +static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
> > +{
> > + unsigned long encl_size = secs->size + PAGE_SIZE;
>
> Wait, you just copied @secs from user memory in sgx_ioc_enclave_create()
> and now use ->size unverified? You're kidding, right?

The size of the enclave is checked in sgx_validate_secs() before it is used
to configure the shmem backing.

> > + struct sgx_epc_page *secs_epc;
> > + unsigned long ssaframesize;
> > + struct sgx_pageinfo pginfo;
> > + struct sgx_secinfo secinfo;
> > + struct file *backing;
> > + long ret;
> > +
> > + if (atomic_read(&encl->flags) & SGX_ENCL_CREATED)
> > + return -EINVAL;
> > +
> > + ssaframesize = sgx_calc_ssaframesize(secs->miscselect, secs->xfrm);
>
> So this is using more un-validated user input to do further calculations.
> What can possibly go wrong?

ssaframesize is also validated below, and the computations on miscselect and
xfm in sgx_calc_ssaframesize() are bounded such that bad input won't send
the kernel into the weeds.

That being said, I agree that it would be safer to move sgx_calc_ssaframesize()
inside sgx_validate_secs() and only compute encl_size after the secs is
validated.

> I sure hope *I* am wrong and am missing something here.
>
> If not, please, for the next version, audit all your user input and
> validate it before using it. Srsly.
>
> > + if (sgx_validate_secs(secs, ssaframesize)) {
> > + pr_debug("invalid SECS\n");
> > + return -EINVAL;
> > + }
> > +
> > + backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5),
> > + VM_NORESERVE);
> > + if (IS_ERR(backing))
> > + return PTR_ERR(backing);
> > +
> > + encl->backing = backing;
> > +
> > + secs_epc = __sgx_alloc_epc_page();
> > + if (IS_ERR(secs_epc)) {
> > + ret = PTR_ERR(secs_epc);
> > + goto err_out_backing;
> > + }
> > +
> > + encl->secs.epc_page = secs_epc;
> > +
> > + pginfo.addr = 0;
> > + pginfo.contents = (unsigned long)secs;
> > + pginfo.metadata = (unsigned long)&secinfo;
> > + pginfo.secs = 0;
> > + memset(&secinfo, 0, sizeof(secinfo));
> > +
> > + ret = __ecreate((void *)&pginfo, sgx_get_epc_addr(secs_epc));
> > + if (ret) {
> > + pr_debug("ECREATE returned %ld\n", ret);
> > + goto err_out;
> > + }
> > +
> > + if (secs->attributes & SGX_ATTR_DEBUG)
> > + atomic_or(SGX_ENCL_DEBUG, &encl->flags);
> > +
> > + encl->secs.encl = encl;
> > + encl->secs_attributes = secs->attributes;
> > + encl->allowed_attributes |= SGX_ATTR_ALLOWED_MASK;
> > + encl->base = secs->base;
> > + encl->size = secs->size;
> > + encl->ssaframesize = secs->ssa_frame_size;
> > +
> > + /*
> > + * Set SGX_ENCL_CREATED only after the enclave is fully prepped. This
> > + * allows setting and checking enclave creation without having to take
> > + * encl->lock.
> > + */
> > + atomic_or(SGX_ENCL_CREATED, &encl->flags);
> > +
> > + return 0;
> > +
> > +err_out:
> > + sgx_free_epc_page(encl->secs.epc_page);
> > + encl->secs.epc_page = NULL;
> > +
> > +err_out_backing:
> > + fput(encl->backing);
> > + encl->backing = NULL;
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * sgx_ioc_enclave_create - handler for %SGX_IOC_ENCLAVE_CREATE
> > + * @filep: open file to /dev/sgx
>
> That's
>
> @encl: enclave pointer
>
> or so.
>
> > + * @arg: userspace pointer to a struct sgx_enclave_create instance
> > + *
> > + * Allocate kernel data structures for a new enclave and execute ECREATE after
> > + * verifying the correctness of the provided SECS.
> > + *
> > + * Note, enforcement of restricted and disallowed attributes is deferred until
> > + * sgx_ioc_enclave_init(), only the architectural correctness of the SECS is
> > + * checked by sgx_ioc_enclave_create().
>
> Well, I don't see that checking. Where is it?
>
> > + *
> > + * Return:
> > + * 0 on success,
> > + * -errno otherwise
> > + */
> > +static long sgx_ioc_enclave_create(struct sgx_encl *encl, void __user *arg)
> > +{
> > + struct sgx_enclave_create ecreate;
> > + struct page *secs_page;
> > + struct sgx_secs *secs;
> > + int ret;
> > +
> > + if (copy_from_user(&ecreate, arg, sizeof(ecreate)))
> > + return -EFAULT;
> > +
> > + secs_page = alloc_page(GFP_KERNEL);
> > + if (!secs_page)
> > + return -ENOMEM;
> > +
> > + secs = kmap(secs_page);
> > + if (copy_from_user(secs, (void __user *)ecreate.src, sizeof(*secs))) {
> > + ret = -EFAULT;
> > + goto out;
> > + }
> > +
> > + ret = sgx_encl_create(encl, secs);
> > +
> > +out:
> > + kunmap(secs_page);
> > + __free_page(secs_page);
> > + return ret;
> > +}
>
> --
> Regards/Gruss,
> Boris.
>
> https://people.kernel.org/tglx/notes-about-netiquette