> +static u32 sgx_calc_ssa_frame_size(u32 miscselect, u64 xfrm)
> +{
> +     u32 size_max = PAGE_SIZE;
> +     u32 size;
> +     int i;
> +
> +     for (i = 2; i < 64; i++) {

Should this be:

        for (i = XFEATURE_YMM; i < XFEATURE_MAX; i++) {

Basically, does this need to be 64, or should it be limited to the
kernel-known XFEATURES?  Or, should this be looping through all the bits
set in xfeatures_mask_user().

> +             if (!((1 << i) & xfrm))
> +                     continue;
> +
> +             size = SGX_SSA_GPRS_SIZE + sgx_xsave_size_tbl[i];
> +
> +             if (miscselect & SGX_MISC_EXINFO)
> +                     size += SGX_SSA_MISC_EXINFO_SIZE;
> +
> +             if (size > size_max)
> +                     size_max = size;
> +     }
> +
> +     return PFN_UP(size_max);
> +}
> +
> +static int sgx_validate_secs(const struct sgx_secs *secs)
> +{

What's the overall point of this function?  Does it avoid a #GP from an
instruction later?

Does all of the 'secs' content come from userspace?

> +     u64 max_size = (secs->attributes & SGX_ATTR_MODE64BIT) ?
> +                    sgx_encl_size_max_64 : sgx_encl_size_max_32;
> +
> +     if (secs->size < (2 * PAGE_SIZE) || !is_power_of_2(secs->size))
> +             return -EINVAL;
> +
> +     if (secs->base & (secs->size - 1))
> +             return -EINVAL;
> +
> +     if (secs->miscselect & sgx_misc_reserved_mask ||
> +         secs->attributes & sgx_attributes_reserved_mask ||
> +         secs->xfrm & sgx_xfrm_reserved_mask)
> +             return -EINVAL;
> +
> +     if (secs->size > max_size)
> +             return -EINVAL;
> +
> +     if (!(secs->xfrm & XFEATURE_MASK_FP) ||
> +         !(secs->xfrm & XFEATURE_MASK_SSE) ||
> +         (((secs->xfrm >> XFEATURE_BNDREGS) & 1) != ((secs->xfrm >> 
> XFEATURE_BNDCSR) & 1)))
> +             return -EINVAL;
> +
> +     if (!secs->ssa_frame_size)
> +             return -EINVAL;
> +
> +     if (sgx_calc_ssa_frame_size(secs->miscselect, secs->xfrm) > 
> secs->ssa_frame_size)
> +             return -EINVAL;
> +
> +     if (memchr_inv(secs->reserved1, 0, sizeof(secs->reserved1)) ||
> +         memchr_inv(secs->reserved2, 0, sizeof(secs->reserved2)) ||
> +         memchr_inv(secs->reserved3, 0, sizeof(secs->reserved3)) ||
> +         memchr_inv(secs->reserved4, 0, sizeof(secs->reserved4)))
> +             return -EINVAL;
> +
> +     return 0;
> +}

I think it would be nice to at least have one comment per condition to
explain what's going on there.

> +static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
> +{
> +     struct sgx_epc_page *secs_epc;
> +     struct sgx_pageinfo pginfo;
> +     struct sgx_secinfo secinfo;
> +     unsigned long encl_size;
> +     struct file *backing;
> +     long ret;
> +
> +     if (sgx_validate_secs(secs)) {
> +             pr_debug("invalid SECS\n");
> +             return -EINVAL;
> +     }
> +
> +     /* The extra page goes to SECS. */
> +     encl_size = secs->size + PAGE_SIZE;
> +
> +     backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5),
> +                                VM_NORESERVE);

What's the >>5 adjustment for?

> +     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->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);

I'm wondering what the impact of setting this flag is.  That's hard to
figure out because the flag isn't documented.

It's also unusual to have atomic_or() used like this.  The normal
set_bit()/clear_bit() that you can use on an unsigned long are actually
implemented as atomics.

I'm curious both why this needs to be atomics, *and* why the atomic_or()
interface is being used.

> +     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
> + * @encl:    an enclave pointer
> + * @arg:     userspace pointer to a struct sgx_enclave_create instance
> + *
> + * Allocate kernel data structures for a new enclave and execute ECREATE 
> after
> + * checking that the provided data for SECS meets the expectations of ECREATE
> + * for an uninitialized enclave and size of the address space does not 
> surpass the
> + * platform expectations. This validation is done by sgx_validate_secs().
> + *
> + * 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 (atomic_read(&encl->flags) & SGX_ENCL_CREATED)
> +             return -EINVAL;
> +
> +     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);

GFP_KERNEL pages are in low memory and don't need to be kmap()'d.

This can just be:

        secs = __get_free_page(GFP_KERNEL);
        if (copy_from_user(secs, (void __user *)ecreate.src,...

and forget about the kmapping.  You also need to change __free_pages()
to free_pages().

The other alternative would be to just kmalloc() it.  kmalloc()
guarantees alignment in a stronger way than it used to.

> +     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;
> +}
> +
> +long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
> +{
> +     struct sgx_encl *encl = filep->private_data;
> +     int ret, encl_flags;
> +
> +     encl_flags = atomic_fetch_or(SGX_ENCL_IOCTL, &encl->flags);
> +     if (encl_flags & SGX_ENCL_IOCTL)
> +             return -EBUSY;

Is the SGX_ENCL_IOCTL bit essentially just a lock to single-thread
ioctl()s?  Should we name it as such?

> +     if (encl_flags & SGX_ENCL_DEAD) {
> +             ret = -EFAULT;
> +             goto out;
> +     }
> +
> +     switch (cmd) {
> +     case SGX_IOC_ENCLAVE_CREATE:
> +             ret = sgx_ioc_enclave_create(encl, (void __user *)arg);
> +             break;
> +     default:
> +             ret = -ENOIOCTLCMD;
> +             break;
> +     }
> +
> +out:
> +     atomic_andnot(SGX_ENCL_IOCTL, &encl->flags);
> +     return ret;
> +}
> 

Reply via email to