Skip to content

Commit ff08530

Browse files
rchatrehansendc
authored andcommitted
x86/sgx: Support restricting of enclave page permissions
In the initial (SGX1) version of SGX, pages in an enclave need to be created with permissions that support all usages of the pages, from the time the enclave is initialized until it is unloaded. For example, pages used by a JIT compiler or when code needs to otherwise be relocated need to always have RWX permissions. SGX2 includes a new function ENCLS[EMODPR] that is run from the kernel and can be used to restrict the EPCM permissions of regular enclave pages within an initialized enclave. Introduce ioctl() SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS to support restricting EPCM permissions. With this ioctl() the user specifies a page range and the EPCM permissions to be applied to all pages in the provided range. ENCLS[EMODPR] is run to restrict the EPCM permissions followed by the ENCLS[ETRACK] flow that will ensure no cached linear-to-physical address mappings to the changed pages remain. It is possible for the permission change request to fail on any page within the provided range, either with an error encountered by the kernel or by the SGX hardware while running ENCLS[EMODPR]. To support partial success the ioctl() returns an error code based on failures encountered by the kernel as well as two result output parameters: one for the number of pages that were successfully changed and one for the SGX return code. The page table entry permissions are not impacted by the EPCM permission changes. VMAs and PTEs will continue to allow the maximum vetted permissions determined at the time the pages are added to the enclave. The SGX error code in a page fault will indicate if it was an EPCM permission check that prevented an access attempt. No checking is done to ensure that the permissions are actually being restricted. This is because the enclave may have relaxed the EPCM permissions from within the enclave without the kernel knowing. An attempt to relax permissions using this call will be ignored by the hardware. Signed-off-by: Reinette Chatre <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Jarkko Sakkinen <[email protected]> Tested-by: Jarkko Sakkinen <[email protected]> Tested-by: Haitao Huang <[email protected]> Tested-by: Vijay Dhanraj <[email protected]> Link: https://lkml.kernel.org/r/082cee986f3c1a2f4fdbf49501d7a8c5a98446f8.1652137848.git.reinette.chatre@intel.com
1 parent a76e7f1 commit ff08530

File tree

2 files changed

+237
-0
lines changed

2 files changed

+237
-0
lines changed

arch/x86/include/uapi/asm/sgx.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ enum sgx_page_flags {
2929
_IOW(SGX_MAGIC, 0x03, struct sgx_enclave_provision)
3030
#define SGX_IOC_VEPC_REMOVE_ALL \
3131
_IO(SGX_MAGIC, 0x04)
32+
#define SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS \
33+
_IOWR(SGX_MAGIC, 0x05, struct sgx_enclave_restrict_permissions)
3234

3335
/**
3436
* struct sgx_enclave_create - parameter structure for the
@@ -76,6 +78,25 @@ struct sgx_enclave_provision {
7678
__u64 fd;
7779
};
7880

81+
/**
82+
* struct sgx_enclave_restrict_permissions - parameters for ioctl
83+
* %SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS
84+
* @offset: starting page offset (page aligned relative to enclave base
85+
* address defined in SECS)
86+
* @length: length of memory (multiple of the page size)
87+
* @permissions:new permission bits for pages in range described by @offset
88+
* and @length
89+
* @result: (output) SGX result code of ENCLS[EMODPR] function
90+
* @count: (output) bytes successfully changed (multiple of page size)
91+
*/
92+
struct sgx_enclave_restrict_permissions {
93+
__u64 offset;
94+
__u64 length;
95+
__u64 permissions;
96+
__u64 result;
97+
__u64 count;
98+
};
99+
79100
struct sgx_enclave_run;
80101

81102
/**

arch/x86/kernel/cpu/sgx/ioctl.c

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,218 @@ static long sgx_ioc_enclave_provision(struct sgx_encl *encl, void __user *arg)
660660
return sgx_set_attribute(&encl->attributes_mask, params.fd);
661661
}
662662

663+
/*
664+
* Ensure enclave is ready for SGX2 functions. Readiness is checked
665+
* by ensuring the hardware supports SGX2 and the enclave is initialized
666+
* and thus able to handle requests to modify pages within it.
667+
*/
668+
static int sgx_ioc_sgx2_ready(struct sgx_encl *encl)
669+
{
670+
if (!(cpu_feature_enabled(X86_FEATURE_SGX2)))
671+
return -ENODEV;
672+
673+
if (!test_bit(SGX_ENCL_INITIALIZED, &encl->flags))
674+
return -EINVAL;
675+
676+
return 0;
677+
}
678+
679+
/*
680+
* Some SGX functions require that no cached linear-to-physical address
681+
* mappings are present before they can succeed. Collaborate with
682+
* hardware via ENCLS[ETRACK] to ensure that all cached
683+
* linear-to-physical address mappings belonging to all threads of
684+
* the enclave are cleared. See sgx_encl_cpumask() for details.
685+
*
686+
* Must be called with enclave's mutex held from the time the
687+
* SGX function requiring that no cached linear-to-physical mappings
688+
* are present is executed until this ETRACK flow is complete.
689+
*/
690+
static int sgx_enclave_etrack(struct sgx_encl *encl)
691+
{
692+
void *epc_virt;
693+
int ret;
694+
695+
epc_virt = sgx_get_epc_virt_addr(encl->secs.epc_page);
696+
ret = __etrack(epc_virt);
697+
if (ret) {
698+
/*
699+
* ETRACK only fails when there is an OS issue. For
700+
* example, two consecutive ETRACK was sent without
701+
* completed IPI between.
702+
*/
703+
pr_err_once("ETRACK returned %d (0x%x)", ret, ret);
704+
/*
705+
* Send IPIs to kick CPUs out of the enclave and
706+
* try ETRACK again.
707+
*/
708+
on_each_cpu_mask(sgx_encl_cpumask(encl), sgx_ipi_cb, NULL, 1);
709+
ret = __etrack(epc_virt);
710+
if (ret) {
711+
pr_err_once("ETRACK repeat returned %d (0x%x)",
712+
ret, ret);
713+
return -EFAULT;
714+
}
715+
}
716+
on_each_cpu_mask(sgx_encl_cpumask(encl), sgx_ipi_cb, NULL, 1);
717+
718+
return 0;
719+
}
720+
721+
/**
722+
* sgx_enclave_restrict_permissions() - Restrict EPCM permissions
723+
* @encl: Enclave to which the pages belong.
724+
* @modp: Checked parameters from user on which pages need modifying and
725+
* their new permissions.
726+
*
727+
* Return:
728+
* - 0: Success.
729+
* - -errno: Otherwise.
730+
*/
731+
static long
732+
sgx_enclave_restrict_permissions(struct sgx_encl *encl,
733+
struct sgx_enclave_restrict_permissions *modp)
734+
{
735+
struct sgx_encl_page *entry;
736+
struct sgx_secinfo secinfo;
737+
unsigned long addr;
738+
unsigned long c;
739+
void *epc_virt;
740+
int ret;
741+
742+
memset(&secinfo, 0, sizeof(secinfo));
743+
secinfo.flags = modp->permissions & SGX_SECINFO_PERMISSION_MASK;
744+
745+
for (c = 0 ; c < modp->length; c += PAGE_SIZE) {
746+
addr = encl->base + modp->offset + c;
747+
748+
mutex_lock(&encl->lock);
749+
750+
entry = sgx_encl_load_page(encl, addr);
751+
if (IS_ERR(entry)) {
752+
ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
753+
goto out_unlock;
754+
}
755+
756+
/*
757+
* Changing EPCM permissions is only supported on regular
758+
* SGX pages. Attempting this change on other pages will
759+
* result in #PF.
760+
*/
761+
if (entry->type != SGX_PAGE_TYPE_REG) {
762+
ret = -EINVAL;
763+
goto out_unlock;
764+
}
765+
766+
/*
767+
* Apart from ensuring that read-access remains, do not verify
768+
* the permission bits requested. Kernel has no control over
769+
* how EPCM permissions can be relaxed from within the enclave.
770+
* ENCLS[EMODPR] can only remove existing EPCM permissions,
771+
* attempting to set new permissions will be ignored by the
772+
* hardware.
773+
*/
774+
775+
/* Change EPCM permissions. */
776+
epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
777+
ret = __emodpr(&secinfo, epc_virt);
778+
if (encls_faulted(ret)) {
779+
/*
780+
* All possible faults should be avoidable:
781+
* parameters have been checked, will only change
782+
* permissions of a regular page, and no concurrent
783+
* SGX1/SGX2 ENCLS instructions since these
784+
* are protected with mutex.
785+
*/
786+
pr_err_once("EMODPR encountered exception %d\n",
787+
ENCLS_TRAPNR(ret));
788+
ret = -EFAULT;
789+
goto out_unlock;
790+
}
791+
if (encls_failed(ret)) {
792+
modp->result = ret;
793+
ret = -EFAULT;
794+
goto out_unlock;
795+
}
796+
797+
ret = sgx_enclave_etrack(encl);
798+
if (ret) {
799+
ret = -EFAULT;
800+
goto out_unlock;
801+
}
802+
803+
mutex_unlock(&encl->lock);
804+
}
805+
806+
ret = 0;
807+
goto out;
808+
809+
out_unlock:
810+
mutex_unlock(&encl->lock);
811+
out:
812+
modp->count = c;
813+
814+
return ret;
815+
}
816+
817+
/**
818+
* sgx_ioc_enclave_restrict_permissions() - handler for
819+
* %SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS
820+
* @encl: an enclave pointer
821+
* @arg: userspace pointer to a &struct sgx_enclave_restrict_permissions
822+
* instance
823+
*
824+
* SGX2 distinguishes between relaxing and restricting the enclave page
825+
* permissions maintained by the hardware (EPCM permissions) of pages
826+
* belonging to an initialized enclave (after SGX_IOC_ENCLAVE_INIT).
827+
*
828+
* EPCM permissions cannot be restricted from within the enclave, the enclave
829+
* requires the kernel to run the privileged level 0 instructions ENCLS[EMODPR]
830+
* and ENCLS[ETRACK]. An attempt to relax EPCM permissions with this call
831+
* will be ignored by the hardware.
832+
*
833+
* Return:
834+
* - 0: Success
835+
* - -errno: Otherwise
836+
*/
837+
static long sgx_ioc_enclave_restrict_permissions(struct sgx_encl *encl,
838+
void __user *arg)
839+
{
840+
struct sgx_enclave_restrict_permissions params;
841+
long ret;
842+
843+
ret = sgx_ioc_sgx2_ready(encl);
844+
if (ret)
845+
return ret;
846+
847+
if (copy_from_user(&params, arg, sizeof(params)))
848+
return -EFAULT;
849+
850+
if (sgx_validate_offset_length(encl, params.offset, params.length))
851+
return -EINVAL;
852+
853+
if (params.permissions & ~SGX_SECINFO_PERMISSION_MASK)
854+
return -EINVAL;
855+
856+
/*
857+
* Fail early if invalid permissions requested to prevent ENCLS[EMODPR]
858+
* from faulting later when the CPU does the same check.
859+
*/
860+
if ((params.permissions & SGX_SECINFO_W) &&
861+
!(params.permissions & SGX_SECINFO_R))
862+
return -EINVAL;
863+
864+
if (params.result || params.count)
865+
return -EINVAL;
866+
867+
ret = sgx_enclave_restrict_permissions(encl, &params);
868+
869+
if (copy_to_user(arg, &params, sizeof(params)))
870+
return -EFAULT;
871+
872+
return ret;
873+
}
874+
663875
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
664876
{
665877
struct sgx_encl *encl = filep->private_data;
@@ -681,6 +893,10 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
681893
case SGX_IOC_ENCLAVE_PROVISION:
682894
ret = sgx_ioc_enclave_provision(encl, (void __user *)arg);
683895
break;
896+
case SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS:
897+
ret = sgx_ioc_enclave_restrict_permissions(encl,
898+
(void __user *)arg);
899+
break;
684900
default:
685901
ret = -ENOIOCTLCMD;
686902
break;

0 commit comments

Comments
 (0)