LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH v20 00/28] Intel SGX1 support
@ 2019-04-17 10:39 Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 01/28] x86/cpufeatures: Add Intel-defined SGX feature bit Jarkko Sakkinen
` (33 more replies)
0 siblings, 34 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 22862 bytes --]
Intel(R) SGX is a set of CPU instructions that can be used by applications
to set aside private regions of code and data. The code outside the enclave
is disallowed to access the memory inside the enclave by the CPU access
control. In a way you can think that SGX provides inverted sandbox. It
protects the application from a malicious host.
There is a new hardware unit in the processor called Memory Encryption
Engine (MEE) starting from the Skylake microacrhitecture. BIOS can define
one or many MEE regions that can hold enclave data by configuring them with
PRMRR registers.
The MEE automatically encrypts the data leaving the processor package to
the MEE regions. The data is encrypted using a random key whose life-time
is exactly one power cycle.
The current implementation requires that the firmware sets
IA32_SGXLEPUBKEYHASH* MSRs as writable so that ultimately the kernel can
decide what enclaves it wants run. The implementation does not create
any bottlenecks to support read-only MSRs later on.
You can tell if your CPU supports SGX by looking into /proc/cpuinfo:
cat /proc/cpuinfo | grep sgx
v20:
* Fine-tune Kconfig messages and spacing and remove MMU_NOTIFIER
dependency as MMU notifiers are no longer used in the driver.
* Use mm_users instead of mm_count as refcount for mm_struct as mm_count
only protects from deleting mm_struct, not removing its contents.
* Sanitize EPC when the reclaimer thread starts by doing EREMOVE for all
of them. They could be in initialized state when the kernel starts
because it might be spawned by kexec().
* Documentation overhaul.
* Use a device /dev/sgx/provision for delivering the provision token
instead of securityfs.
* Create a reference to the enclave when already when opening
/dev/sgx/enclave. The file is then associated with this enclave only.
mmap() can be done at free at any point and always get a reference to
the enclave. To summarize the file now represents the enclave.
v19:
* Took 3-4 months but in some sense this was more like a rewrite of most
of the corners of the source code. If I've forgotten to deal with some
feedback, please don't shout me. Make a remark and I will fix it for
the next version. Hopefully there won't be this big turnovers anymore.
* Validate SECS attributes properly against CPUID given attributes and
against allowed attributes. SECS attributes are the ones that are
enforced whereas SIGSTRUCT attributes tell what is required to run
the enclave.
* Add KSS (Key Sharing Support) to the enclave attributes.
* Deny MAP_PRIVATE as an enclave is always a shared memory entity.
* Revert back to shmem backing storage so that it can be easily shared
by multiple processes.
* Split the recognization of an ENCLS leaf failure by using three
functions to detect it: encsl_faulted(), encls_returned_code() and
sgx_failed(). encls_failed() is only caused by a spurious expections that
should never happen. Thus, it is not defined as an inline function in
order to easily insert a kprobe to it.
* Move low-level enclave management routines, page fault handler and page
reclaiming routines from driver to the core. These cannot be separated
from each other as they are heavily interdependent. The rationale is that
the core does not call any code from the driver.
* Allow the driver to be compiled as a module now that it no code is using
its routines and it only uses exported symbols. Now the driver is
essentially just a thin ioctl layer.
* Reworked the driver to maintain a list of mm_struct's. The VMA callbacks
add new entries to this list as the process is forked. Each entry has
its own refcount because they have a different life-cycle as the enclave
does. In effect @tgid and @mm have been removed from struct sgx_encl
and we allow forking by removing VM_DONTCOPY from vm flags.
* Generate a cpu mask in the reclaimer from the cpu mask's of all
mm_struct's. This will kick out the hardware threads out of the enclave
from multiple processes. It is not a local variable because it would
eat too much of the stack space but instead a field in struct
sgx_encl.
* Allow forking i.e. remove VM_DONTCOPY. I did not change the API
because the old API scaled to the workload that Andy described. The
codebase is now mostly API independent i.e. changing the API is a
small task. For me the proper trigger to chanage it is a as concrete
as possible workload that cannot be fulfilled. I hope you understand
my thinking here. I don't want to change anything w/o proper basis
but I'm ready to change anything if there is a proper basis. I do
not have any kind of attachment to any particular type of API.
* Add Sean's vDSO ENCLS(EENTER) patches and update selftest to use the
new vDSO.
v18:
* Update the ioctl-number.txt.
* Move the driver under arch/x86.
* Add SGX features (SGX, SGX1, SGX2) to the disabled-features.h.
* Rename the selftest as test_sgx (previously sgx-selftest).
* In order to enable process accounting, swap EPC pages and PCMD's to a VMA
instead of shmem.
* Allow only to initialize and run enclaves with a subset of
{DEBUG, MODE64BIT} set.
* Add SGX_IOC_ENCLAVE_SET_ATTRIBUTE to allow an enclave to have privileged
attributes e.g. PROVISIONKEY.
v17:
* Add a simple selftest.
* Fix a null pointer dereference to section->pages when its
allocation fails.
* Add Sean's description of the exception handling to the documentation.
v16:
* Fixed SOB's in the commits that were a bit corrupted in v15.
* Implemented exceptio handling properly to detect_sgx().
* Use GENMASK() to define SGX_CPUID_SUB_LEAF_TYPE_MASK.
* Updated the documentation to use rst definition lists.
* Added the missing Documentation/x86/index.rst, which has a link to
intel_sgx.rst. Now the SGX and uapi documentation is properly generated
with 'make htmldocs'.
* While enumerating EPC sections, if an undefined section is found, fail
the driver initialization instead of continuing the initialization.
* Issue a warning if there are more than %SGX_MAX_EPC_SECTIONS.
* Remove copyright notice from arch/x86/include/asm/sgx.h.
* Migrated from ioremap_cache() to memremap().
v15:
* Split into more digestable size patches.
* Lots of small fixes and clean ups.
* Signal a "plain" SIGSEGV on an EPCM violation.
v14:
* Change the comment about X86_FEATURE_SGX_LC from “SGX launch
configuration” to “SGX launch control”.
* Move the SGX-related CPU feature flags as part of the Linux defined
virtual leaf 8.
* Add SGX_ prefix to the constants defining the ENCLS leaf functions.
* Use GENMASK*() and BIT*() in sgx_arch.h instead of raw hex numbers.
* Refine the long description for CONFIG_INTEL_SGX_CORE.
* Do not use pr_*_ratelimited() in the driver. The use of the rate limited
versions is legacy cruft from the prototyping phase.
* Detect sleep with SGX_INVALID_EINIT_TOKEN instead of counting power
cycles.
* Manually prefix with “sgx:” in the core SGX code instead of redefining
pr_fmt.
* Report if IA32_SGXLEPUBKEYHASHx MSRs are not writable in the driver
instead of core because it is a driver requirement.
* Change prompt to bool in the entry for CONFIG_INTEL_SGX_CORE because the
default is ‘n’.
* Rename struct sgx_epc_bank as struct sgx_epc_section in order to match
the SDM.
* Allocate struct sgx_epc_page instances one at a time.
* Use “__iomem void *” pointers for the mapped EPC memory consistently.
* Retry once on SGX_INVALID_TOKEN in sgx_einit() instead of counting power
cycles.
* Call enclave swapping operations directly from the driver instead of
calling them .indirectly through struct sgx_epc_page_ops because indirect
calls are not required yet as the patch set does not contain the KVM
support.
* Added special signal SEGV_SGXERR to notify about SGX EPCM violation
errors.
v13:
* Always use SGX_CPUID constant instead of a hardcoded value.
* Simplified and documented the macros and functions for ENCLS leaves.
* Enable sgx_free_page() to free active enclave pages on demand
in order to allow sgx_invalidate() to delete enclave pages.
It no longer performs EREMOVE if a page is in the process of
being reclaimed.
* Use PM notifier per enclave so that we don't have to traverse
the global list of active EPC pages to find enclaves.
* Removed unused SGX_LE_ROLLBACK constant from uapi/asm/sgx.h
* Always use ioremap() to map EPC banks as we only support 64-bit kernel.
* Invalidate IA32_SGXLEPUBKEYHASH cache used by sgx_einit() when going
to sleep.
v12:
* Split to more narrow scoped commits in order to ease the review process and
use co-developed-by tag for co-authors of commits instead of listing them in
the source files.
* Removed cruft EXPORT_SYMBOL() declarations and converted to static variables.
* Removed in-kernel LE i.e. this version of the SGX software stack only
supports unlocked IA32_SGXLEPUBKEYHASHx MSRs.
* Refined documentation on launching enclaves, swapping and enclave
construction.
* Refined sgx_arch.h to include alignment information for every struct that
requires it and removed structs that are not needed without an LE.
* Got rid of SGX_CPUID.
* SGX detection now prints log messages about firmware configuration issues.
v11:
* Polished ENCLS wrappers with refined exception handling.
* ksgxswapd was not stopped (regression in v5) in
sgx_page_cache_teardown(), which causes a leaked kthread after driver
deinitialization.
* Shutdown sgx_le_proxy when going to suspend because its EPC pages will be
invalidated when resuming, which will cause it not function properly
anymore.
* Set EINITTOKEN.VALID to zero for a token that is passed when
SGXLEPUBKEYHASH matches MRSIGNER as alloc_page() does not give a zero
page.
* Fixed the check in sgx_edbgrd() for a TCS page. Allowed to read offsets
around the flags field, which causes a #GP. Only flags read is readable.
* On read access memcpy() call inside sgx_vma_access() had src and dest
parameters in wrong order.
* The build issue with CONFIG_KASAN is now fixed. Added undefined symbols
to LE even if “KASAN_SANITIZE := false” was set in the makefile.
* Fixed a regression in the #PF handler. If a page has
SGX_ENCL_PAGE_RESERVED flag the #PF handler should unconditionally fail.
It did not, which caused weird races when trying to change other parts of
swapping code.
* EPC management has been refactored to a flat LRU cache and moved to
arch/x86. The swapper thread reads a cluster of EPC pages and swaps all
of them. It can now swap from multiple enclaves in the same round.
* For the sake of consistency with SGX_IOC_ENCLAVE_ADD_PAGE, return -EINVAL
when an enclave is already initialized or dead instead of zero.
v10:
* Cleaned up anon inode based IPC between the ring-0 and ring-3 parts
of the driver.
* Unset the reserved flag from an enclave page if EDBGRD/WR fails
(regression in v6).
* Close the anon inode when LE is stopped (regression in v9).
* Update the documentation with a more detailed description of SGX.
v9:
* Replaced kernel-LE IPC based on pipes with an anonymous inode.
The driver does not require anymore new exports.
v8:
* Check that public key MSRs match the LE public key hash in the
driver initialization when the MSRs are read-only.
* Fix the race in VA slot allocation by checking the fullness
immediately after succeesful allocation.
* Fix the race in hash mrsigner calculation between the launch
enclave and user enclaves by having a separate lock for hash
calculation.
v7:
* Fixed offset calculation in sgx_edbgr/wr(). Address was masked with PAGE_MASK
when it should have been masked with ~PAGE_MASK.
* Fixed a memory leak in sgx_ioc_enclave_create().
* Simplified swapping code by using a pointer array for a cluster
instead of a linked list.
* Squeezed struct sgx_encl_page to 32 bytes.
* Fixed deferencing of an RSA key on OpenSSL 1.1.0.
* Modified TC's CMAC to use kernel AES-NI. Restructured the code
a bit in order to better align with kernel conventions.
v6:
* Fixed semaphore underrun when accessing /dev/sgx from the launch enclave.
* In sgx_encl_create() s/IS_ERR(secs)/IS_ERR(encl)/.
* Removed virtualization chapter from the documentation.
* Changed the default filename for the signing key as signing_key.pem.
* Reworked EPC management in a way that instead of a linked list of
struct sgx_epc_page instances there is an array of integers that
encodes address and bank of an EPC page (the same data as 'pa' field
earlier). The locking has been moved to the EPC bank level instead
of a global lock.
* Relaxed locking requirements for EPC management. EPC pages can be
released back to the EPC bank concurrently.
* Cleaned up ptrace() code.
* Refined commit messages for new architectural constants.
* Sorted includes in every source file.
* Sorted local variable declarations according to the line length in
every function.
* Style fixes based on Darren's comments to sgx_le.c.
v5:
* Described IPC between the Launch Enclave and kernel in the commit messages.
* Fixed all relevant checkpatch.pl issues that I have forgot fix in earlier
versions except those that exist in the imported TinyCrypt code.
* Fixed spelling mistakes in the documentation.
* Forgot to check the return value of sgx_drv_subsys_init().
* Encapsulated properly page cache init and teardown.
* Collect epc pages to a temp list in sgx_add_epc_bank
* Removed SGX_ENCLAVE_INIT_ARCH constant.
v4:
* Tied life-cycle of the sgx_le_proxy process to /dev/sgx.
* Removed __exit annotation from sgx_drv_subsys_exit().
* Fixed a leak of a backing page in sgx_process_add_page_req() in the
case when vm_insert_pfn() fails.
* Removed unused symbol exports for sgx_page_cache.c.
* Updated sgx_alloc_page() to require encl parameter and documented the
behavior (Sean Christopherson).
* Refactored a more lean API for sgx_encl_find() and documented the behavior.
* Moved #PF handler to sgx_fault.c.
* Replaced subsys_system_register() with plain bus_register().
* Retry EINIT 2nd time only if MSRs are not locked.
v3:
* Check that FEATURE_CONTROL_LOCKED and FEATURE_CONTROL_SGX_ENABLE are set.
* Return -ERESTARTSYS in __sgx_encl_add_page() when sgx_alloc_page() fails.
* Use unused bits in epc_page->pa to store the bank number.
* Removed #ifdef for WQ_NONREENTRANT.
* If mmu_notifier_register() fails with -EINTR, return -ERESTARTSYS.
* Added --remove-section=.got.plt to objcopy flags in order to prevent a
dummy .got.plt, which will cause an inconsistent size for the LE.
* Documented sgx_encl_* functions.
* Added remark about AES implementation used inside the LE.
* Removed redundant sgx_sys_exit() from le/main.c.
* Fixed struct sgx_secinfo alignment from 128 to 64 bytes.
* Validate miscselect in sgx_encl_create().
* Fixed SSA frame size calculation to take the misc region into account.
* Implemented consistent exception handling to __encls() and __encls_ret().
* Implemented a proper device model in order to allow sysfs attributes
and in-kernel API.
* Cleaned up various "find enclave" implementations to the unified
sgx_encl_find().
* Validate that vm_pgoff is zero.
* Discard backing pages with shmem_truncate_range() after EADD.
* Added missing EEXTEND operations to LE signing and launch.
* Fixed SSA size for GPRS region from 168 to 184 bytes.
* Fixed the checks for TCS flags. Now DBGOPTIN is allowed.
* Check that TCS addresses are in ELRANGE and not just page aligned.
* Require kernel to be compiled with X64_64 and CPU_SUP_INTEL.
* Fixed an incorrect value for SGX_ATTR_DEBUG from 0x01 to 0x02.
v2:
* get_rand_uint32() changed the value of the pointer instead of value
where it is pointing at.
* Launch enclave incorrectly used sigstruct attributes-field instead of
enclave attributes-field.
* Removed unused struct sgx_add_page_req from sgx_ioctl.c
* Removed unused sgx_has_sgx2.
* Updated arch/x86/include/asm/sgx.h so that it provides stub
implementations when sgx in not enabled.
* Removed cruft rdmsr-calls from sgx_set_pubkeyhash_msrs().
* return -ENOMEM in sgx_alloc_page() when VA pages consume too much space
* removed unused global sgx_nr_pids
* moved sgx_encl_release to sgx_encl.c
* return -ERESTARTSYS instead of -EINTR in sgx_encl_init()
Jarkko Sakkinen (11):
x86/sgx: Add ENCLS architectural error codes
x86/sgx: Add SGX1 and SGX2 architectural data structures
x86/sgx: Add wrappers for ENCLS leaf functions
x86/sgx: Add functions to allocate and free EPC pages
x86/sgx: Add the Linux SGX Enclave Driver
x86/sgx: Add provisioning
x86/sgx: Add swapping code to the core and SGX driver
x86/sgx: ptrace() support for the SGX driver
selftests/x86: Add a selftest for SGX
x86/sgx: Update MAINTAINERS
docs: x86/sgx: Document the enclave API
Kai Huang (2):
x86/cpufeatures: Add Intel-defined SGX feature bit
x86/cpufeatures: Add Intel-defined SGX_LC feature bit
Sean Christopherson (15):
x86/cpufeatures: Add SGX sub-features (as Linux-defined bits)
x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition
x86/msr: Add SGX Launch Control MSR definitions
x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit
x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
x86/cpu/intel: Detect SGX support and update caps appropriately
x86/sgx: Enumerate and track EPC sections
x86/sgx: Add sgx_einit() for initializing enclaves
x86/vdso: Add support for exception fixup in vDSO functions
x86/fault: Add helper function to sanitize error code
x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling
x86/traps: Attempt to fixup exceptions in vDSO before signaling
x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave
transitions
docs: x86/sgx: Add Architecture documentation
docs: x86/sgx: Document kernel internals
Documentation/index.rst | 1 +
Documentation/ioctl/ioctl-number.txt | 1 +
Documentation/x86/index.rst | 10 +
Documentation/x86/sgx/1.Architecture.rst | 431 +++++++++
Documentation/x86/sgx/2.Kernel-internals.rst | 56 ++
Documentation/x86/sgx/3.API.rst | 27 +
Documentation/x86/sgx/index.rst | 18 +
MAINTAINERS | 12 +
arch/x86/Kconfig | 27 +
arch/x86/entry/vdso/Makefile | 6 +-
arch/x86/entry/vdso/extable.c | 37 +
arch/x86/entry/vdso/extable.h | 29 +
arch/x86/entry/vdso/vdso-layout.lds.S | 9 +-
arch/x86/entry/vdso/vdso.lds.S | 1 +
arch/x86/entry/vdso/vdso2c.h | 58 +-
arch/x86/entry/vdso/vsgx_enter_enclave.S | 101 +++
arch/x86/include/asm/cpufeatures.h | 24 +-
arch/x86/include/asm/disabled-features.h | 14 +-
arch/x86/include/asm/msr-index.h | 8 +
arch/x86/include/asm/traps.h | 1 +
arch/x86/include/asm/vdso.h | 5 +
arch/x86/include/uapi/asm/sgx.h | 86 ++
arch/x86/include/uapi/asm/sgx_errno.h | 91 ++
arch/x86/kernel/cpu/Makefile | 1 +
arch/x86/kernel/cpu/intel.c | 39 +
arch/x86/kernel/cpu/scattered.c | 2 +
arch/x86/kernel/cpu/sgx/Makefile | 2 +
arch/x86/kernel/cpu/sgx/arch.h | 424 +++++++++
arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
arch/x86/kernel/cpu/sgx/driver/driver.h | 38 +
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 850 ++++++++++++++++++
arch/x86/kernel/cpu/sgx/driver/main.c | 368 ++++++++
arch/x86/kernel/cpu/sgx/encl.c | 709 +++++++++++++++
arch/x86/kernel/cpu/sgx/encl.h | 136 +++
arch/x86/kernel/cpu/sgx/encls.c | 22 +
arch/x86/kernel/cpu/sgx/encls.h | 244 +++++
arch/x86/kernel/cpu/sgx/main.c | 360 ++++++++
arch/x86/kernel/cpu/sgx/reclaim.c | 482 ++++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 90 ++
arch/x86/kernel/traps.c | 14 +
arch/x86/mm/fault.c | 44 +-
tools/arch/x86/include/asm/cpufeatures.h | 21 +-
tools/testing/selftests/x86/Makefile | 10 +
tools/testing/selftests/x86/sgx/Makefile | 48 +
tools/testing/selftests/x86/sgx/defines.h | 39 +
tools/testing/selftests/x86/sgx/encl.c | 20 +
tools/testing/selftests/x86/sgx/encl.lds | 33 +
.../selftests/x86/sgx/encl_bootstrap.S | 94 ++
tools/testing/selftests/x86/sgx/encl_piggy.S | 18 +
tools/testing/selftests/x86/sgx/encl_piggy.h | 14 +
tools/testing/selftests/x86/sgx/main.c | 279 ++++++
tools/testing/selftests/x86/sgx/sgx_call.S | 15 +
tools/testing/selftests/x86/sgx/sgxsign.c | 508 +++++++++++
.../testing/selftests/x86/sgx/signing_key.pem | 39 +
54 files changed, 5987 insertions(+), 32 deletions(-)
create mode 100644 Documentation/x86/index.rst
create mode 100644 Documentation/x86/sgx/1.Architecture.rst
create mode 100644 Documentation/x86/sgx/2.Kernel-internals.rst
create mode 100644 Documentation/x86/sgx/3.API.rst
create mode 100644 Documentation/x86/sgx/index.rst
create mode 100644 arch/x86/entry/vdso/extable.c
create mode 100644 arch/x86/entry/vdso/extable.h
create mode 100644 arch/x86/entry/vdso/vsgx_enter_enclave.S
create mode 100644 arch/x86/include/uapi/asm/sgx.h
create mode 100644 arch/x86/include/uapi/asm/sgx_errno.h
create mode 100644 arch/x86/kernel/cpu/sgx/Makefile
create mode 100644 arch/x86/kernel/cpu/sgx/arch.h
create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
create mode 100644 arch/x86/kernel/cpu/sgx/encls.c
create mode 100644 arch/x86/kernel/cpu/sgx/encls.h
create mode 100644 arch/x86/kernel/cpu/sgx/main.c
create mode 100644 arch/x86/kernel/cpu/sgx/reclaim.c
create mode 100644 arch/x86/kernel/cpu/sgx/sgx.h
create mode 100644 tools/testing/selftests/x86/sgx/Makefile
create mode 100644 tools/testing/selftests/x86/sgx/defines.h
create mode 100644 tools/testing/selftests/x86/sgx/encl.c
create mode 100644 tools/testing/selftests/x86/sgx/encl.lds
create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S
create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.S
create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h
create mode 100644 tools/testing/selftests/x86/sgx/main.c
create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c
create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem
--
2.19.1
^ permalink raw reply [flat|nested] 285+ messages in thread
* [PATCH v20 01/28] x86/cpufeatures: Add Intel-defined SGX feature bit
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 02/28] x86/cpufeatures: Add SGX sub-features (as Linux-defined bits) Jarkko Sakkinen
` (32 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes, Kai Huang,
Jarkko Sakkinen
From: Kai Huang <kai.huang@linux.intel.com>
X86_FEATURE_SGX reflects whether or not the CPU supports Intel's
Software Guard eXtensions (SGX).
Signed-off-by: Kai Huang <kai.huang@linux.intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: Borislav Petkov <bp@suse.de>
---
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/disabled-features.h | 8 +++++++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 981ff9479648..a16325db4cff 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -236,6 +236,7 @@
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
#define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
#define X86_FEATURE_TSC_ADJUST ( 9*32+ 1) /* TSC adjustment MSR 0x3B */
+#define X86_FEATURE_SGX ( 9*32+ 2) /* Software Guard Extensions */
#define X86_FEATURE_BMI1 ( 9*32+ 3) /* 1st group bit manipulation extensions */
#define X86_FEATURE_HLE ( 9*32+ 4) /* Hardware Lock Elision */
#define X86_FEATURE_AVX2 ( 9*32+ 5) /* AVX2 instructions */
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index a5ea841cc6d2..74de07d0f390 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -62,6 +62,12 @@
# define DISABLE_PTI (1 << (X86_FEATURE_PTI & 31))
#endif
+#ifdef CONFIG_INTEL_SGX
+# define DISABLE_SGX_CORE 0
+#else
+# define DISABLE_SGX_CORE (1 << (X86_FEATURE_SGX & 31))
+#endif
+
/*
* Make sure to add features to the correct mask
*/
@@ -74,7 +80,7 @@
#define DISABLED_MASK6 0
#define DISABLED_MASK7 (DISABLE_PTI)
#define DISABLED_MASK8 0
-#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP)
+#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP|DISABLE_SGX_CORE)
#define DISABLED_MASK10 0
#define DISABLED_MASK11 0
#define DISABLED_MASK12 0
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 02/28] x86/cpufeatures: Add SGX sub-features (as Linux-defined bits)
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 01/28] x86/cpufeatures: Add Intel-defined SGX feature bit Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 03/28] x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition Jarkko Sakkinen
` (31 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
CPUID_12_EAX is an Intel-defined feature bits leaf dedicated for SGX
that enumerates the SGX instruction sets that are supported by the
CPU, e.g. SGX1, SGX2, etc... Because Linux currently only cares about
two bits (SGX1 and SGX2) and there are currently only four documented
bits in total, relocate the bits to Linux-defined word 8 to conserve
space.
But, keep the bit positions identical between the Intel-defined value
and the Linux-defined value, e.g. keep SGX1 at bit 0. This allows KVM
to use its existing code for probing guest CPUID bits using Linux's
X86_FEATURE_* definitions. To do so, shift around some existing bits
to effectively reserve bits 0-7 of word 8 for SGX sub-features.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/include/asm/cpufeatures.h | 22 ++++++++++++++++------
arch/x86/include/asm/disabled-features.h | 6 +++++-
arch/x86/kernel/cpu/scattered.c | 2 ++
tools/arch/x86/include/asm/cpufeatures.h | 21 +++++++++++++++------
4 files changed, 38 insertions(+), 13 deletions(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index a16325db4cff..313c58c04b51 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -222,12 +222,22 @@
#define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */
#define X86_FEATURE_IBRS_ENHANCED ( 7*32+30) /* Enhanced IBRS */
-/* Virtualization flags: Linux defined, word 8 */
-#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
-#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
-#define X86_FEATURE_FLEXPRIORITY ( 8*32+ 2) /* Intel FlexPriority */
-#define X86_FEATURE_EPT ( 8*32+ 3) /* Intel Extended Page Table */
-#define X86_FEATURE_VPID ( 8*32+ 4) /* Intel Virtual Processor ID */
+/*
+ * Scattered Intel features: Linux defined, word 8.
+ *
+ * Note that the bit location of the SGX features is meaningful as KVM expects
+ * the Linux defined bit to match the Intel defined bit, e.g. X86_FEATURE_SGX1
+ * must remain at bit 0, SGX2 at bit 1, etc...
+ */
+#define X86_FEATURE_SGX1 ( 8*32+ 0) /* SGX1 leaf functions */
+#define X86_FEATURE_SGX2 ( 8*32+ 1) /* SGX2 leaf functions */
+/* Bits [0:7] are reserved for SGX */
+
+#define X86_FEATURE_TPR_SHADOW ( 8*32+ 8) /* Intel TPR Shadow */
+#define X86_FEATURE_VNMI ( 8*32+ 9) /* Intel Virtual NMI */
+#define X86_FEATURE_FLEXPRIORITY ( 8*32+10) /* Intel FlexPriority */
+#define X86_FEATURE_EPT ( 8*32+11) /* Intel Extended Page Table */
+#define X86_FEATURE_VPID ( 8*32+12) /* Intel Virtual Processor ID */
#define X86_FEATURE_VMMCALL ( 8*32+15) /* Prefer VMMCALL to VMCALL */
#define X86_FEATURE_XENPV ( 8*32+16) /* "" Xen paravirtual guest */
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index 74de07d0f390..926f9dc4d75a 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -34,12 +34,16 @@
# define DISABLE_CYRIX_ARR (1<<(X86_FEATURE_CYRIX_ARR & 31))
# define DISABLE_CENTAUR_MCR (1<<(X86_FEATURE_CENTAUR_MCR & 31))
# define DISABLE_PCID 0
+# define DISABLE_SGX1 0
+# define DISABLE_SGX2 0
#else
# define DISABLE_VME 0
# define DISABLE_K6_MTRR 0
# define DISABLE_CYRIX_ARR 0
# define DISABLE_CENTAUR_MCR 0
# define DISABLE_PCID (1<<(X86_FEATURE_PCID & 31))
+# define DISABLE_SGX1 (1<<(X86_FEATURE_SGX1 & 31))
+# define DISABLE_SGX2 (1<<(X86_FEATURE_SGX2 & 31))
#endif /* CONFIG_X86_64 */
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
@@ -79,7 +83,7 @@
#define DISABLED_MASK5 0
#define DISABLED_MASK6 0
#define DISABLED_MASK7 (DISABLE_PTI)
-#define DISABLED_MASK8 0
+#define DISABLED_MASK8 (DISABLE_SGX1|DISABLE_SGX2)
#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP|DISABLE_SGX_CORE)
#define DISABLED_MASK10 0
#define DISABLED_MASK11 0
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 94aa1c72ca98..9a5f6cf947b9 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -31,6 +31,8 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_CDP_L3, CPUID_ECX, 2, 0x00000010, 1 },
{ X86_FEATURE_CDP_L2, CPUID_ECX, 2, 0x00000010, 2 },
{ X86_FEATURE_MBA, CPUID_EBX, 3, 0x00000010, 0 },
+ { X86_FEATURE_SGX1, CPUID_EAX, 0, 0x00000012, 0 },
+ { X86_FEATURE_SGX2, CPUID_EAX, 1, 0x00000012, 0 },
{ X86_FEATURE_HW_PSTATE, CPUID_EDX, 7, 0x80000007, 0 },
{ X86_FEATURE_CPB, CPUID_EDX, 9, 0x80000007, 0 },
{ X86_FEATURE_PROC_FEEDBACK, CPUID_EDX, 11, 0x80000007, 0 },
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index 981ff9479648..748180a4a1f5 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -222,12 +222,21 @@
#define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */
#define X86_FEATURE_IBRS_ENHANCED ( 7*32+30) /* Enhanced IBRS */
-/* Virtualization flags: Linux defined, word 8 */
-#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
-#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
-#define X86_FEATURE_FLEXPRIORITY ( 8*32+ 2) /* Intel FlexPriority */
-#define X86_FEATURE_EPT ( 8*32+ 3) /* Intel Extended Page Table */
-#define X86_FEATURE_VPID ( 8*32+ 4) /* Intel Virtual Processor ID */
+/*
+ * Scattered Intel features: Linux defined, word 8.
+ *
+ * Note that the bit numbers of the SGX features are meaningful as KVM expects
+ * the Linux defined bit to match the Intel defined bit, e.g. X86_FEATURE_SGX1
+ * must remain at bit 0, SGX2 at bit 1, etc...
+ */
+#define X86_FEATURE_SGX1 ( 8*32+ 0) /* SGX1 leaf functions */
+#define X86_FEATURE_SGX2 ( 8*32+ 1) /* SGX2 leaf functions */
+
+#define X86_FEATURE_TPR_SHADOW ( 8*32+ 8) /* Intel TPR Shadow */
+#define X86_FEATURE_VNMI ( 8*32+ 9) /* Intel Virtual NMI */
+#define X86_FEATURE_FLEXPRIORITY ( 8*32+10) /* Intel FlexPriority */
+#define X86_FEATURE_EPT ( 8*32+11) /* Intel Extended Page Table */
+#define X86_FEATURE_VPID ( 8*32+12) /* Intel Virtual Processor ID */
#define X86_FEATURE_VMMCALL ( 8*32+15) /* Prefer VMMCALL to VMCALL */
#define X86_FEATURE_XENPV ( 8*32+16) /* "" Xen paravirtual guest */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 03/28] x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 01/28] x86/cpufeatures: Add Intel-defined SGX feature bit Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 02/28] x86/cpufeatures: Add SGX sub-features (as Linux-defined bits) Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 04/28] x86/cpufeatures: Add Intel-defined SGX_LC feature bit Jarkko Sakkinen
` (30 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
Add a new IA32_FEATURE_CONTROL bit, SGX_ENABLE, which must be set in
order to execute SGX instructions, i.e. ENCL{S,U,V}. The existence of
the bit is enumerated by CPUID as X86_FEATURE_SGX. Like all other
flags in IA32_FEATURE_CONTROL, the MSR must be locked for SGX_ENABLE
to take effect.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/include/asm/msr-index.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index ca5bc0eacb95..6efaa8026c64 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -525,6 +525,7 @@
#define FEATURE_CONTROL_LOCKED (1<<0)
#define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1<<1)
#define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2)
+#define FEATURE_CONTROL_SGX_ENABLE (1<<18)
#define FEATURE_CONTROL_LMCE (1<<20)
#define MSR_IA32_APICBASE 0x0000001b
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 04/28] x86/cpufeatures: Add Intel-defined SGX_LC feature bit
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (2 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 03/28] x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 05/28] x86/msr: Add SGX Launch Control MSR definitions Jarkko Sakkinen
` (29 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes, Kai Huang,
Jarkko Sakkinen
From: Kai Huang <kai.huang@linux.intel.com>
X86_FEATURE_SGX_LC reflects whether or not the CPU supports SGX Launch
Control, i.e. enumerates the existence of IA32_FEATURE_CONTROL's
SGX_LE_WR bit and the IA32_SGXLEPUBKEYHASH MSRs.
Signed-off-by: Kai Huang <kai.huang@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/include/asm/cpufeatures.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 313c58c04b51..c5080842ecad 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -346,6 +346,7 @@
#define X86_FEATURE_CLDEMOTE (16*32+25) /* CLDEMOTE instruction */
#define X86_FEATURE_MOVDIRI (16*32+27) /* MOVDIRI instruction */
#define X86_FEATURE_MOVDIR64B (16*32+28) /* MOVDIR64B instruction */
+#define X86_FEATURE_SGX_LC (16*32+30) /* Software Guard Extensions Launch Control */
/* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */
#define X86_FEATURE_OVERFLOW_RECOV (17*32+ 0) /* MCA overflow recovery support */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 05/28] x86/msr: Add SGX Launch Control MSR definitions
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (3 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 04/28] x86/cpufeatures: Add Intel-defined SGX_LC feature bit Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 06/28] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit Jarkko Sakkinen
` (28 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
Add a new IA32_FEATURE_CONTROL bit, SGX_LE_WR. When set, SGX_LE_WR
allows software to write the SGXLEPUBKEYHASH MSRs (see below). The
The existence of the bit is enumerated by CPUID as X86_FEATURE_SGX_LC.
Like all other flags in IA32_FEATURE_CONTROL, the MSR must be locked
for SGX_LE_WR to take effect.
Add four MSRs, SGXLEPUBKEYHASH{0,1,2,3}, or in human readable form,
the SGX Launch Enclave Public Key Hash MSRs. These MSRs correspond to
the key that is used by the CPU to determine whether or not to allow
software to enter an enclave. When ENCLS[EINIT] is executed, which is
a prerequisite to entering the enclave, the CPU compares the key
(technically its hash) used to sign the enclave with the key hash
stored in the MSRs, and will reject EINIT if the keys do not match.
Enclaves can also be blessed by proxy, in which case a Launch Enclave
generates and signs an EINIT TOKEN. If a valid token is provided,
ENCLS[EINIT] compares the signer of the token against the MSRs instead
of the signer of the enclave. The SGXLEPUBKEYHASH MSRs only exist on
CPUs that support SGX Launch Control, enumerated by X86_FEATURE_SGX_LC.
CPUs without Launch Control use a hardcoded key for the ENCLS[EINIT]
checks. An internal hardcoded key is also used as the reset value for
the hash MSRs when they exist.
As a final note, the SGX_LEPUBKEYHASH MSRs can also be written by
pre-boot firmware prior to activating SGX (SGX activation is done by
setting bit 0 in MSR 0x7A). Thus, firmware can lock the MSRs to a
non-Intel value by writing the MSRs and locking IA32_FEATURE_CONTROL
without setting SGX_LE_WR.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Haim Cohen <haim.cohen@intel.com>
Signed-off-by: Haim Cohen <haim.cohen@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/include/asm/msr-index.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 6efaa8026c64..a4a22441d000 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -525,6 +525,7 @@
#define FEATURE_CONTROL_LOCKED (1<<0)
#define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1<<1)
#define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2)
+#define FEATURE_CONTROL_SGX_LE_WR (1<<17)
#define FEATURE_CONTROL_SGX_ENABLE (1<<18)
#define FEATURE_CONTROL_LMCE (1<<20)
@@ -538,6 +539,12 @@
#define MSR_IA32_UCODE_WRITE 0x00000079
#define MSR_IA32_UCODE_REV 0x0000008b
+/* Intel SGX Launch Enclave Public Key Hash MSRs */
+#define MSR_IA32_SGXLEPUBKEYHASH0 0x0000008C
+#define MSR_IA32_SGXLEPUBKEYHASH1 0x0000008D
+#define MSR_IA32_SGXLEPUBKEYHASH2 0x0000008E
+#define MSR_IA32_SGXLEPUBKEYHASH3 0x0000008F
+
#define MSR_IA32_SMM_MONITOR_CTL 0x0000009b
#define MSR_IA32_SMBASE 0x0000009e
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 06/28] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (4 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 05/28] x86/msr: Add SGX Launch Control MSR definitions Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 07/28] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX Jarkko Sakkinen
` (27 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Dave Hansen, Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
The SGX bit is set in the #PF error code if and only if the fault is
detected by the Enclave Page Cache Map (EPCM), a hardware-managed
table that enforces the paging permissions defined by the enclave,
e.g. to prevent the kernel from changing the permissions of an
enclave's page(s).
Despite triggering a #PF, a #PF with PF_SGX has nothing to do with
paging.
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/include/asm/traps.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index 7d6f3f3fad78..c3e02912e9c7 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -171,5 +171,6 @@ enum x86_pf_error_code {
X86_PF_RSVD = 1 << 3,
X86_PF_INSTR = 1 << 4,
X86_PF_PK = 1 << 5,
+ X86_PF_SGX = 1 << 15,
};
#endif /* _ASM_X86_TRAPS_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 07/28] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (5 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 06/28] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 08/28] x86/cpu/intel: Detect SGX support and update caps appropriately Jarkko Sakkinen
` (26 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Andy Lutomirski, Dave Hansen, Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
The PF_SGX bit is set if and only if the #PF is detected by the SGX
Enclave Page Cache Map (EPCM). The EPCM is a hardware-managed table
that enforces accesses to an enclave's EPC pages in addition to the
software-managed kernel page tables, i.e. the effective permissions
for an EPC page are a logical AND of the kernel's page tables and
the corresponding EPCM entry.
The EPCM is consulted only after an access walks the kernel's page
tables, i.e.:
a. the access was allowed by the kernel
b. the kernel's tables have become less restrictive than the EPCM
c. the kernel cannot fixup the cause of the fault
Noteably, (b) implies that either the kernel has botched the EPC
mappings or the EPCM has been invalidated (see below). Regardless of
why the fault occurred, userspace needs to be alerted so that it can
take appropriate action, e.g. restart the enclave. This is reinforced
by (c) as the kernel doesn't really have any other reasonable option,
i.e. signalling SIGSEGV is actually the least severe action possible.
Although the primary purpose of the EPCM is to prevent a malicious or
compromised kernel from attacking an enclave, e.g. by modifying the
enclave's page tables, do not WARN on a #PF w/ PF_SGX set. The SGX
architecture effectively allows the CPU to invalidate all EPCM entries
at will and requires that software be prepared to handle an EPCM fault
at any time. The architecture defines this behavior because the EPCM
is encrypted with an ephemeral key that isn't exposed to software. As
such, the EPCM entries cannot be preserved across transitions that
result in a new key being used, e.g. CPU power down as part of an S3
transition or when a VM is live migrated to a new physical system.
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/mm/fault.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 667f1da36208..78e2807fbede 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -1214,6 +1214,19 @@ access_error(unsigned long error_code, struct vm_area_struct *vma)
if (error_code & X86_PF_PK)
return 1;
+ /*
+ * Access is blocked by the Enclave Page Cache Map (EPCM), i.e. the
+ * access is allowed by the PTE but not the EPCM. This usually happens
+ * when the EPCM is yanked out from under us, e.g. by hardware after a
+ * suspend/resume cycle. In any case, software, i.e. the kernel, can't
+ * fix the source of the fault as the EPCM can't be directly modified
+ * by software. Handle the fault as an access error in order to signal
+ * userspace, e.g. so that userspace can rebuild their enclave(s), even
+ * though userspace may not have actually violated access permissions.
+ */
+ if (unlikely(error_code & X86_PF_SGX))
+ return 1;
+
/*
* Make sure to check the VMA so that we do not perform
* faults just to hit a X86_PF_PK as soon as we fill in a
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 08/28] x86/cpu/intel: Detect SGX support and update caps appropriately
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (6 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 07/28] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 09/28] x86/sgx: Add ENCLS architectural error codes Jarkko Sakkinen
` (25 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
Similar to other large Intel features such as VMX and TXT, SGX must be
explicitly enabled in IA32_FEATURE_CONTROL MSR to be truly usable.
Clear all SGX related capabilities if SGX is not fully enabled in
IA32_FEATURE_CONTROL or if the SGX1 instruction set isn't supported
(impossible on bare metal, theoretically possible in a VM if the VMM is
doing something weird).
Like SGX itself, SGX Launch Control must be explicitly enabled via a
flag in IA32_FEATURE_CONTROL. Clear the SGX_LC capability if Launch
Control is not fully enabled (or obviously if SGX itself is disabled).
Note that clearing X86_FEATURE_SGX_LC creates a bit of a conundrum
regarding the SGXLEPUBKEYHASH MSRs, as it may be desirable to read the
MSRs even if they are not writable, e.g. to query the configured key,
but clearing the capability leaves no breadcrum for discerning whether
or not the MSRs exist. But, such usage will be rare (KVM is the only
known case at this time) and not performance critical, so it's not
unreasonable to require the use of rdmsr_safe(). Clearing the cap bit
eliminates the need for an additional flag to track whether or not
Launch Control is truly enabled, which is what we care about the vast
majority of the time.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/kernel/cpu/intel.c | 39 +++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index fc3c07fe7df5..702497f34a96 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -596,6 +596,42 @@ static void detect_tme(struct cpuinfo_x86 *c)
c->x86_phys_bits -= keyid_bits;
}
+static void __maybe_unused detect_sgx(struct cpuinfo_x86 *c)
+{
+ unsigned long long fc;
+
+ rdmsrl(MSR_IA32_FEATURE_CONTROL, fc);
+ if (!(fc & FEATURE_CONTROL_LOCKED)) {
+ pr_err_once("sgx: The feature control MSR is not locked\n");
+ goto err_unsupported;
+ }
+
+ if (!(fc & FEATURE_CONTROL_SGX_ENABLE)) {
+ pr_err_once("sgx: SGX is not enabled in IA32_FEATURE_CONTROL MSR\n");
+ goto err_unsupported;
+ }
+
+ if (!cpu_has(c, X86_FEATURE_SGX1)) {
+ pr_err_once("sgx: SGX1 instruction set is not supported\n");
+ goto err_unsupported;
+ }
+
+ if (!(fc & FEATURE_CONTROL_SGX_LE_WR)) {
+ pr_info_once("sgx: The launch control MSRs are not writable\n");
+ goto err_msrs_rdonly;
+ }
+
+ return;
+
+err_unsupported:
+ setup_clear_cpu_cap(X86_FEATURE_SGX);
+ setup_clear_cpu_cap(X86_FEATURE_SGX1);
+ setup_clear_cpu_cap(X86_FEATURE_SGX2);
+
+err_msrs_rdonly:
+ setup_clear_cpu_cap(X86_FEATURE_SGX_LC);
+}
+
static void init_intel_energy_perf(struct cpuinfo_x86 *c)
{
u64 epb;
@@ -763,6 +799,9 @@ static void init_intel(struct cpuinfo_x86 *c)
if (cpu_has(c, X86_FEATURE_TME))
detect_tme(c);
+ if (IS_ENABLED(CONFIG_INTEL_SGX) && cpu_has(c, X86_FEATURE_SGX))
+ detect_sgx(c);
+
init_intel_energy_perf(c);
init_intel_misc_features(c);
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 09/28] x86/sgx: Add ENCLS architectural error codes
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (7 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 08/28] x86/cpu/intel: Detect SGX support and update caps appropriately Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-22 21:35 ` Sean Christopherson
2019-04-17 10:39 ` [PATCH v20 10/28] x86/sgx: Add SGX1 and SGX2 architectural data structures Jarkko Sakkinen
` (24 subsequent siblings)
33 siblings, 1 reply; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
The SGX architecture defines an extensive set of error codes that are
used by ENCL{S,U,V} instructions to provide software with (somewhat)
precise error information. Though they are architectural, define the
known error codes in a separate file from sgx_arch.h so that they can
be exposed to userspace. For some ENCLS leafs, e.g. EINIT, returning
the exact error code on failure can enable userspace to make informed
decisions when an operation fails.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/include/uapi/asm/sgx_errno.h | 91 +++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
create mode 100644 arch/x86/include/uapi/asm/sgx_errno.h
diff --git a/arch/x86/include/uapi/asm/sgx_errno.h b/arch/x86/include/uapi/asm/sgx_errno.h
new file mode 100644
index 000000000000..48b87aed58d7
--- /dev/null
+++ b/arch/x86/include/uapi/asm/sgx_errno.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ * Contains the architecturally defined error codes that are returned by SGX
+ * instructions, e.g. ENCLS, and may be propagated to userspace via errno.
+ */
+
+#ifndef _UAPI_ASM_X86_SGX_ERRNO_H
+#define _UAPI_ASM_X86_SGX_ERRNO_H
+
+/**
+ * enum sgx_encls_leaves - return codes for ENCLS, ENCLU and ENCLV
+ * %SGX_SUCCESS: No error.
+ * %SGX_INVALID_SIG_STRUCT: SIGSTRUCT contains an invalid value.
+ * %SGX_INVALID_ATTRIBUTE: Enclave is not attempting to access a resource
+ * for which it is not authorized.
+ * %SGX_BLKSTATE: EPC page is already blocked.
+ * %SGX_INVALID_MEASUREMENT: SIGSTRUCT or EINITTOKEN contains an incorrect
+ * measurement.
+ * %SGX_NOTBLOCKABLE: EPC page type is not one which can be blocked.
+ * %SGX_PG_INVLD: EPC page is invalid (and cannot be blocked).
+ * %SGX_EPC_PAGE_CONFLICT: EPC page in use by another SGX instruction.
+ * %SGX_INVALID_SIGNATURE: Enclave's signature does not validate with
+ * public key enclosed in SIGSTRUCT.
+ * %SGX_MAC_COMPARE_FAIL: MAC check failed when reloading EPC page.
+ * %SGX_PAGE_NOT_BLOCKED: EPC page is not marked as blocked.
+ * %SGX_NOT_TRACKED: ETRACK has not been completed on the EPC page.
+ * %SGX_VA_SLOT_OCCUPIED: Version array slot contains a valid entry.
+ * %SGX_CHILD_PRESENT: Enclave has child pages present in the EPC.
+ * %SGX_ENCLAVE_ACT: Logical processors are currently executing
+ * inside the enclave.
+ * %SGX_ENTRYEPOCH_LOCKED: SECS locked for EPOCH update, i.e. an ETRACK is
+ * currently executing on the SECS.
+ * %SGX_INVALID_EINITTOKEN: EINITTOKEN is invalid and enclave signer's
+ * public key does not match IA32_SGXLEPUBKEYHASH.
+ * %SGX_PREV_TRK_INCMPL: All processors did not complete the previous
+ * tracking sequence.
+ * %SGX_PG_IS_SECS: Target EPC page is an SECS and cannot be
+ * blocked.
+ * %SGX_PAGE_ATTRIBUTES_MISMATCH: Attributes of the EPC page do not match
+ * the expected values.
+ * %SGX_PAGE_NOT_MODIFIABLE: EPC page cannot be modified because it is in
+ * the PENDING or MODIFIED state.
+ * %SGX_PAGE_NOT_DEBUGGABLE: EPC page cannot be modified because it is in
+ * the PENDING or MODIFIED state.
+ * %SGX_INVALID_COUNTER: {In,De}crementing a counter would cause it to
+ * {over,under}flow.
+ * %SGX_PG_NONEPC: Target page is not an EPC page.
+ * %SGX_TRACK_NOT_REQUIRED: Target page type does not require tracking.
+ * %SGX_INVALID_CPUSVN: Security version number reported by CPU is less
+ * than what is required by the enclave.
+ * %SGX_INVALID_ISVSVN: Security version number of enclave is less than
+ * what is required by the KEYREQUEST struct.
+ * %SGX_UNMASKED_EVENT: An unmasked event, e.g. INTR, was received
+ * while the instruction was executing.
+ * %SGX_INVALID_KEYNAME: Requested key is not supported by hardware.
+ */
+enum sgx_return_codes {
+ SGX_SUCCESS = 0,
+ SGX_INVALID_SIG_STRUCT = 1,
+ SGX_INVALID_ATTRIBUTE = 2,
+ SGX_BLKSTATE = 3,
+ SGX_INVALID_MEASUREMENT = 4,
+ SGX_NOTBLOCKABLE = 5,
+ SGX_PG_INVLD = 6,
+ SGX_EPC_PAGE_CONFLICT = 7,
+ SGX_INVALID_SIGNATURE = 8,
+ SGX_MAC_COMPARE_FAIL = 9,
+ SGX_PAGE_NOT_BLOCKED = 10,
+ SGX_NOT_TRACKED = 11,
+ SGX_VA_SLOT_OCCUPIED = 12,
+ SGX_CHILD_PRESENT = 13,
+ SGX_ENCLAVE_ACT = 14,
+ SGX_ENTRYEPOCH_LOCKED = 15,
+ SGX_INVALID_EINITTOKEN = 16,
+ SGX_PREV_TRK_INCMPL = 17,
+ SGX_PG_IS_SECS = 18,
+ SGX_PAGE_ATTRIBUTES_MISMATCH = 19,
+ SGX_PAGE_NOT_MODIFIABLE = 20,
+ SGX_PAGE_NOT_DEBUGGABLE = 21,
+ SGX_INVALID_COUNTER = 25,
+ SGX_PG_NONEPC = 26,
+ SGX_TRACK_NOT_REQUIRED = 27,
+ SGX_INVALID_CPUSVN = 32,
+ SGX_INVALID_ISVSVN = 64,
+ SGX_UNMASKED_EVENT = 128,
+ SGX_INVALID_KEYNAME = 256,
+};
+
+#endif /* _UAPI_ASM_X86_SGX_ERRNO_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 10/28] x86/sgx: Add SGX1 and SGX2 architectural data structures
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (8 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 09/28] x86/sgx: Add ENCLS architectural error codes Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 11/28] x86/sgx: Add wrappers for ENCLS leaf functions Jarkko Sakkinen
` (23 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
Define the data structures used by various ENCLS functions needed for
Linux to support all SGX1 and SGX2 ENCLS leaf functions. This is not
an exhaustive representation of all SGX data structures as several are
only consumed by ENCLU (userspace), e.g. REPORT and KEYREQUEST, while
others are only consumed by future features, e.g. RDINFO.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/kernel/cpu/sgx/arch.h | 424 +++++++++++++++++++++++++++++++++
1 file changed, 424 insertions(+)
create mode 100644 arch/x86/kernel/cpu/sgx/arch.h
diff --git a/arch/x86/kernel/cpu/sgx/arch.h b/arch/x86/kernel/cpu/sgx/arch.h
new file mode 100644
index 000000000000..39f731580ea8
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/arch.h
@@ -0,0 +1,424 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-18 Intel Corporation.
+ *
+ * Contains data structures defined by the SGX architecture. Data structures
+ * defined by the Linux software stack should not be placed here.
+ */
+#ifndef _ASM_X86_SGX_ARCH_H
+#define _ASM_X86_SGX_ARCH_H
+
+#include <linux/types.h>
+
+#define SGX_CPUID 0x12
+#define SGX_CPUID_FIRST_VARIABLE_SUB_LEAF 2
+
+/**
+ * enum sgx_sub_leaf_types - SGX CPUID variable sub-leaf types
+ * %SGX_CPUID_SUB_LEAF_INVALID: Indicates this sub-leaf is invalid.
+ * %SGX_CPUID_SUB_LEAF_EPC_SECTION: Sub-leaf enumerates an EPC section.
+ */
+enum sgx_sub_leaf_types {
+ SGX_CPUID_SUB_LEAF_INVALID = 0x0,
+ SGX_CPUID_SUB_LEAF_EPC_SECTION = 0x1,
+};
+
+#define SGX_CPUID_SUB_LEAF_TYPE_MASK GENMASK(3, 0)
+
+/**
+ * enum sgx_encls_leaves - ENCLS leaf functions
+ * %SGX_ECREATE: Create an enclave.
+ * %SGX_EADD: Add a page to an uninitialized enclave.
+ * %SGX_EINIT: Initialize an enclave, i.e. launch an enclave.
+ * %SGX_EREMOVE: Remove a page from an enclave.
+ * %SGX_EDBGRD: Read a word from an enclve (peek).
+ * %SGX_EDBGWR: Write a word to an enclave (poke).
+ * %SGX_EEXTEND: Measure 256 bytes of an added enclave page.
+ * %SGX_ELDB: Load a swapped page in blocked state.
+ * %SGX_ELDU: Load a swapped page in unblocked state.
+ * %SGX_EBLOCK: Change page state to blocked i.e. entering hardware
+ * threads cannot access it and create new TLB entries.
+ * %SGX_EPA: Create a Version Array (VA) page used to store isvsvn
+ * number for a swapped EPC page.
+ * %SGX_EWB: Swap an enclave page to the regular memory. Checks that
+ * all threads have exited that were in the previous
+ * shoot-down sequence.
+ * %SGX_ETRACK: Start a new shoot down sequence. Used to together with
+ * EBLOCK to make sure that a page is safe to swap.
+ * %SGX_EAUG: Add a page to an initialized enclave.
+ * %SGX_EMODPR: Restrict an EPC page's permissions.
+ * %SGX_EMODT: Modify the page type of an EPC page.
+ */
+enum sgx_encls_leaves {
+ SGX_ECREATE = 0x00,
+ SGX_EADD = 0x01,
+ SGX_EINIT = 0x02,
+ SGX_EREMOVE = 0x03,
+ SGX_EDGBRD = 0x04,
+ SGX_EDGBWR = 0x05,
+ SGX_EEXTEND = 0x06,
+ SGX_ELDB = 0x07,
+ SGX_ELDU = 0x08,
+ SGX_EBLOCK = 0x09,
+ SGX_EPA = 0x0A,
+ SGX_EWB = 0x0B,
+ SGX_ETRACK = 0x0C,
+ SGX_EAUG = 0x0D,
+ SGX_EMODPR = 0x0E,
+ SGX_EMODT = 0x0F,
+};
+
+#define SGX_MODULUS_SIZE 384
+
+/**
+ * enum sgx_miscselect - additional information to an SSA frame
+ * %SGX_MISC_EXINFO: Report #PF or #GP to the SSA frame.
+ *
+ * Save State Area (SSA) is a stack inside the enclave used to store processor
+ * state when an exception or interrupt occurs. This enum defines additional
+ * information stored to an SSA frame.
+ */
+enum sgx_miscselect {
+ SGX_MISC_EXINFO = BIT(0),
+};
+
+#define SGX_MISC_RESERVED_MASK GENMASK_ULL(63, 1)
+
+#define SGX_SSA_GPRS_SIZE 182
+#define SGX_SSA_MISC_EXINFO_SIZE 16
+
+/**
+ * enum sgx_attributes - the attributes field in &struct sgx_secs
+ * %SGX_ATTR_INIT: Enclave can be entered (is initialized).
+ * %SGX_ATTR_DEBUG: Allow ENCLS(EDBGRD) and ENCLS(EDBGWR).
+ * %SGX_ATTR_MODE64BIT: Tell that this a 64-bit enclave.
+ * %SGX_ATTR_PROVISIONKEY: Allow to use provisioning keys for remote
+ * attestation.
+ * %SGX_ATTR_KSS: Allow to use key separation and sharing (KSS).
+ * %SGX_ATTR_EINITTOKENKEY: Allow to use token signing key that is used to
+ * sign cryptographic tokens that can be passed to
+ * EINIT as an authorization to run an enclave.
+ */
+enum sgx_attribute {
+ SGX_ATTR_INIT = BIT(0),
+ SGX_ATTR_DEBUG = BIT(1),
+ SGX_ATTR_MODE64BIT = BIT(2),
+ SGX_ATTR_PROVISIONKEY = BIT(4),
+ SGX_ATTR_EINITTOKENKEY = BIT(5),
+ SGX_ATTR_KSS = BIT(7),
+};
+
+#define SGX_ATTR_RESERVED_MASK (BIT_ULL(3) | BIT_ULL(7) | GENMASK_ULL(63, 8))
+#define SGX_ATTR_ALLOWED_MASK (SGX_ATTR_DEBUG | SGX_ATTR_MODE64BIT | \
+ SGX_ATTR_KSS)
+#define SGX_SECS_RESERVED1_SIZE 24
+#define SGX_SECS_RESERVED2_SIZE 32
+#define SGX_SECS_RESERVED3_SIZE 96
+#define SGX_SECS_RESERVED4_SIZE 3836
+
+/**
+ * struct sgx_secs - SGX Enclave Control Structure (SECS)
+ * @size: size of the address space
+ * @base: base address of the address space
+ * @ssa_frame_size: size of an SSA frame
+ * @miscselect: additional information stored to an SSA frame
+ * @attributes: attributes for enclave
+ * @xfrm: XSave-Feature Request Mask (subset of XCR0)
+ * @mrenclave: SHA256-hash of the enclave contents
+ * @mrsigner: SHA256-hash of the public key used to sign the SIGSTRUCT
+ * @isvprodid: a user-defined value that is used in key derivation
+ * @isvsvn: a user-defined value that is used in key derivation
+ *
+ * SGX Enclave Control Structure (SECS) is a special enclave page that is not
+ * visible in the address space. In fact, this structure defines the address
+ * range and other global attributes for the enclave and it is the first EPC
+ * page created for any enclave. It is moved from a temporary buffer to an EPC
+ * by the means of ENCLS(ECREATE) leaf.
+ */
+struct sgx_secs {
+ u64 size;
+ u64 base;
+ u32 ssa_frame_size;
+ u32 miscselect;
+ u8 reserved1[SGX_SECS_RESERVED1_SIZE];
+ u64 attributes;
+ u64 xfrm;
+ u32 mrenclave[8];
+ u8 reserved2[SGX_SECS_RESERVED2_SIZE];
+ u32 mrsigner[8];
+ u8 reserved3[SGX_SECS_RESERVED3_SIZE];
+ u16 isvprodid;
+ u16 isvsvn;
+ u8 reserved4[SGX_SECS_RESERVED4_SIZE];
+} __packed;
+
+/**
+ * enum sgx_tcs_flags - execution flags for TCS
+ * %SGX_TCS_DBGOPTIN: If enabled allows single-stepping and breakpoints
+ * inside an enclave. It is cleared by EADD but can
+ * be set later with EDBGWR.
+ */
+enum sgx_tcs_flags {
+ SGX_TCS_DBGOPTIN = 0x01,
+};
+
+#define SGX_TCS_RESERVED_MASK GENMASK_ULL(63, 1)
+#define SGX_TCS_RESERVED_SIZE 4024
+
+/**
+ * struct sgx_tcs - Thread Control Structure (TCS)
+ * @state: used to mark an entered TCS
+ * @flags: execution flags (cleared by EADD)
+ * @ssa_offset: SSA stack offset relative to the enclave base
+ * @ssa_index: the current SSA frame index (cleard by EADD)
+ * @nr_ssa_frames: the number of frame in the SSA stack
+ * @entry_offset: entry point offset relative to the enclave base
+ * @exit_addr: address outside the enclave to exit on an exception or
+ * interrupt
+ * @fs_offset: offset relative to the enclave base to become FS
+ * segment inside the enclave
+ * @gs_offset: offset relative to the enclave base to become GS
+ * segment inside the enclave
+ * @fs_limit: size to become a new FS-limit (only 32-bit enclaves)
+ * @gs_limit: size to become a new GS-limit (only 32-bit enclaves)
+ *
+ * Thread Control Structure (TCS) is an enclave page visible in its address
+ * space that defines an entry point inside the enclave. A thread enters inside
+ * an enclave by supplying address of TCS to ENCLU(EENTER). A TCS can be entered
+ * by only one thread at a time.
+ */
+struct sgx_tcs {
+ u64 state;
+ u64 flags;
+ u64 ssa_offset;
+ u32 ssa_index;
+ u32 nr_ssa_frames;
+ u64 entry_offset;
+ u64 exit_addr;
+ u64 fs_offset;
+ u64 gs_offset;
+ u32 fs_limit;
+ u32 gs_limit;
+ u8 reserved[SGX_TCS_RESERVED_SIZE];
+} __packed;
+
+/**
+ * struct sgx_pageinfo - an enclave page descriptor
+ * @addr: address of the enclave page
+ * @contents: pointer to the page contents
+ * @metadata: pointer either to a SECINFO or PCMD instance
+ * @secs: address of the SECS page
+ */
+struct sgx_pageinfo {
+ u64 addr;
+ u64 contents;
+ u64 metadata;
+ u64 secs;
+} __packed __aligned(32);
+
+
+/**
+ * enum sgx_page_type - bits in the SECINFO flags defining the page type
+ * %SGX_PAGE_TYPE_SECS: a SECS page
+ * %SGX_PAGE_TYPE_TCS: a TCS page
+ * %SGX_PAGE_TYPE_REG: a regular page
+ * %SGX_PAGE_TYPE_VA: a VA page
+ * %SGX_PAGE_TYPE_TRIM: a page in trimmed state
+ */
+enum sgx_page_type {
+ SGX_PAGE_TYPE_SECS,
+ SGX_PAGE_TYPE_TCS,
+ SGX_PAGE_TYPE_REG,
+ SGX_PAGE_TYPE_VA,
+ SGX_PAGE_TYPE_TRIM,
+};
+
+#define SGX_NR_PAGE_TYPES 5
+#define SGX_PAGE_TYPE_MASK GENMASK(7, 0)
+
+/**
+ * enum sgx_secinfo_flags - the flags field in &struct sgx_secinfo
+ * %SGX_SECINFO_R: allow read
+ * %SGX_SECINFO_W: allow write
+ * %SGX_SECINFO_X: allow execution
+ * %SGX_SECINFO_SECS: a SECS page
+ * %SGX_SECINFO_TCS: a TCS page
+ * %SGX_SECINFO_REG: a regular page
+ * %SGX_SECINFO_VA: a VA page
+ * %SGX_SECINFO_TRIM: a page in trimmed state
+ */
+enum sgx_secinfo_flags {
+ SGX_SECINFO_R = BIT(0),
+ SGX_SECINFO_W = BIT(1),
+ SGX_SECINFO_X = BIT(2),
+ SGX_SECINFO_SECS = (SGX_PAGE_TYPE_SECS << 8),
+ SGX_SECINFO_TCS = (SGX_PAGE_TYPE_TCS << 8),
+ SGX_SECINFO_REG = (SGX_PAGE_TYPE_REG << 8),
+ SGX_SECINFO_VA = (SGX_PAGE_TYPE_VA << 8),
+ SGX_SECINFO_TRIM = (SGX_PAGE_TYPE_TRIM << 8),
+};
+
+#define SGX_SECINFO_PERMISSION_MASK GENMASK_ULL(2, 0)
+#define SGX_SECINFO_PAGE_TYPE_MASK (SGX_PAGE_TYPE_MASK << 8)
+#define SGX_SECINFO_RESERVED_MASK ~(SGX_SECINFO_PERMISSION_MASK | \
+ SGX_SECINFO_PAGE_TYPE_MASK)
+#define SGX_SECINFO_RESERVED_SIZE 56
+
+/**
+ * struct sgx_secinfo - describes attributes of an EPC page
+ * @flags: permissions and type
+ *
+ * Used together with ENCLS leaves that add or modify an EPC page to an
+ * enclave to define page permissions and type.
+ */
+struct sgx_secinfo {
+ u64 flags;
+ u8 reserved[SGX_SECINFO_RESERVED_SIZE];
+} __packed __aligned(64);
+
+#define SGX_PCMD_RESERVED_SIZE 40
+
+/**
+ * struct sgx_pcmd - Paging Crypto Metadata (PCMD)
+ * @enclave_id: enclave identifier
+ * @mac: MAC over PCMD, page contents and isvsvn
+ *
+ * PCMD is stored for every swapped page to the regular memory. When ELDU loads
+ * the page back it recalculates the MAC by using a isvsvn number stored in a
+ * VA page. Together these two structures bring integrity and rollback
+ * protection.
+ */
+struct sgx_pcmd {
+ struct sgx_secinfo secinfo;
+ u64 enclave_id;
+ u8 reserved[SGX_PCMD_RESERVED_SIZE];
+ u8 mac[16];
+} __packed __aligned(128);
+
+#define SGX_SIGSTRUCT_RESERVED1_SIZE 84
+#define SGX_SIGSTRUCT_RESERVED2_SIZE 20
+#define SGX_SIGSTRUCT_RESERVED3_SIZE 32
+#define SGX_SIGSTRUCT_RESERVED4_SIZE 12
+
+/**
+ * struct sgx_sigstruct_header - defines author of the enclave
+ * @header1: constant byte string
+ * @vendor: must be either 0x0000 or 0x8086
+ * @date: YYYYMMDD in BCD
+ * @header2: costant byte string
+ * @swdefined: software defined value
+ */
+struct sgx_sigstruct_header {
+ u64 header1[2];
+ u32 vendor;
+ u32 date;
+ u64 header2[2];
+ u32 swdefined;
+ u8 reserved1[84];
+} __packed;
+
+/**
+ * struct sgx_sigstruct_body - defines contents of the enclave
+ * @miscselect: additional information stored to an SSA frame
+ * @misc_mask: required miscselect in SECS
+ * @attributes: attributes for enclave
+ * @xfrm: XSave-Feature Request Mask (subset of XCR0)
+ * @attributes_mask: required attributes in SECS
+ * @xfrm_mask: required XFRM in SECS
+ * @mrenclave: SHA256-hash of the enclave contents
+ * @isvprodid: a user-defined value that is used in key derivation
+ * @isvsvn: a user-defined value that is used in key derivation
+ */
+struct sgx_sigstruct_body {
+ u32 miscselect;
+ u32 misc_mask;
+ u8 reserved2[20];
+ u64 attributes;
+ u64 xfrm;
+ u64 attributes_mask;
+ u64 xfrm_mask;
+ u8 mrenclave[32];
+ u8 reserved3[32];
+ u16 isvprodid;
+ u16 isvsvn;
+} __packed;
+
+/**
+ * struct sgx_sigstruct - an enclave signature
+ * @header: defines author of the enclave
+ * @modulus: the modulus of the public key
+ * @exponent: the exponent of the public key
+ * @signature: the signature calculated over the fields except modulus,
+ * @body: defines contents of the enclave
+ * @q1: a value used in RSA signature verification
+ * @q2: a value used in RSA signature verification
+ *
+ * Header and body are the parts that are actual signed. The remaining fields
+ * define the signature of the enclave.
+ */
+struct sgx_sigstruct {
+ struct sgx_sigstruct_header header;
+ u8 modulus[SGX_MODULUS_SIZE];
+ u32 exponent;
+ u8 signature[SGX_MODULUS_SIZE];
+ struct sgx_sigstruct_body body;
+ u8 reserved4[12];
+ u8 q1[SGX_MODULUS_SIZE];
+ u8 q2[SGX_MODULUS_SIZE];
+} __packed;
+
+#define SGX_EINITTOKEN_RESERVED1_SIZE 11
+#define SGX_EINITTOKEN_RESERVED2_SIZE 32
+#define SGX_EINITTOKEN_RESERVED3_SIZE 32
+#define SGX_EINITTOKEN_RESERVED4_SIZE 24
+
+/**
+ * struct sgx_einittoken - a token permitting to launch an enclave
+ * @valid: one if valid and zero if invalid
+ * @attributes: attributes for enclave
+ * @xfrm: XSave-Feature Request Mask (subset of XCR0)
+ * @mrenclave: SHA256-hash of the enclave contents
+ * @mrsigner: SHA256-hash of the public key used to sign the
+ * SIGSTRUCT
+ * @le_cpusvn: a value that reflects the SGX implementation
+ * running in in the CPU
+ * @le_isvprodid: a user-defined value that is used in key
+ * derivation
+ * @le_isvsvn: a user-defined value that is used in key
+ * derivation
+ * @le_keyed_miscselect: LE's miscselect masked with the token keys
+ * miscselect
+ * @le_keyed_attributes: LE's attributes masked with the token keys
+ * attributes
+ * @le_keyed_xfrm: LE's XFRM masked with the token keys xfrm
+ * @salt: random salt for wear-out protection
+ * @mac: CMAC over the preceding fields
+ *
+ * An enclave with EINITTOKENKEY attribute can access a key with the same name
+ * by using ENCLS(EGETKEY) and use this to sign cryptographic tokens that can
+ * be passed to ENCLS(EINIT) to permit the launch of other enclaves. This is
+ * the only viable way to launch enclaves if IA32_SGXLEPUBKEYHASHn MSRs are
+ * locked assuming that there is a Launch Enclave (LE) available that can be
+ * used for generating these tokens.
+ */
+struct sgx_einittoken {
+ u32 valid;
+ u32 reserved1[SGX_EINITTOKEN_RESERVED1_SIZE];
+ u64 attributes;
+ u64 xfrm;
+ u8 mrenclave[32];
+ u8 reserved2[SGX_EINITTOKEN_RESERVED2_SIZE];
+ u8 mrsigner[32];
+ u8 reserved3[SGX_EINITTOKEN_RESERVED3_SIZE];
+ u8 le_cpusvn[16];
+ u16 le_isvprodid;
+ u16 le_isvsvn;
+ u8 reserved4[SGX_EINITTOKEN_RESERVED4_SIZE];
+ u32 le_keyed_miscselect;
+ u64 le_keyed_attributes;
+ u64 le_keyed_xfrm;
+ u8 salt[32];
+ u8 mac[16];
+} __packed __aligned(512);
+
+#endif /* _ASM_X86_SGX_ARCH_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 11/28] x86/sgx: Add wrappers for ENCLS leaf functions
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (9 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 10/28] x86/sgx: Add SGX1 and SGX2 architectural data structures Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 12/28] x86/sgx: Enumerate and track EPC sections Jarkko Sakkinen
` (22 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
ENCLS is an umbrella instruction for a variety of cpl0 SGX functions.
The ENCLS function that is executed is specified in EAX, with each
function potentially having more leaf-specific operands beyond EAX.
ENCLS introduces its own (positive value) error codes that (some)
leafs use to return failure information in EAX. Leafs that return
an error code also modify RFLAGS. And finally, ENCLS generates
ENCLS-specific non-fatal #GPs and #PFs, i.e. a bug-free kernel may
encounter faults on ENCLS that must be handled gracefully.
Because of the complexity involved in encoding ENCLS and handling its
assortment of failure paths, executing any given leaf is not a simple
matter of emitting ENCLS.
To enable adding support for ENCLS leafs with minimal fuss, add a
two-layer macro system along with an encoding scheme to allow wrappers
to return trap numbers along ENCLS-specific error codes. The bottom
layer of the macro system splits between the leafs that return an
error code and those that do not. The second layer generates the
correct input/output annotations based on the number of operands for
each leaf function.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/kernel/cpu/sgx/Makefile | 1 +
arch/x86/kernel/cpu/sgx/encls.c | 21 +++
arch/x86/kernel/cpu/sgx/encls.h | 244 +++++++++++++++++++++++++++++++
3 files changed, 266 insertions(+)
create mode 100644 arch/x86/kernel/cpu/sgx/Makefile
create mode 100644 arch/x86/kernel/cpu/sgx/encls.c
create mode 100644 arch/x86/kernel/cpu/sgx/encls.h
diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile
new file mode 100644
index 000000000000..4432d935894e
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/Makefile
@@ -0,0 +1 @@
+obj-y += encls.o
diff --git a/arch/x86/kernel/cpu/sgx/encls.c b/arch/x86/kernel/cpu/sgx/encls.c
new file mode 100644
index 000000000000..5045f1365e07
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/encls.c
@@ -0,0 +1,21 @@
+#include <asm/cpufeature.h>
+#include <asm/traps.h>
+#include "encls.h"
+#include "sgx.h"
+
+/**
+ * encls_failed() - Check if an ENCLS leaf function failed
+ * @ret: the return value of an ENCLS leaf function call
+ *
+ * Check if an ENCLS leaf function failed. This is a condition where the leaf
+ * function causes a fault that is not caused by an EPCM conflict.
+ *
+ * Return: true if there was a fault other than an EPCM conflict
+ */
+bool encls_failed(int ret)
+{
+ int epcm_trapnr = boot_cpu_has(X86_FEATURE_SGX2) ?
+ X86_TRAP_PF : X86_TRAP_GP;
+
+ return encls_faulted(ret) && ENCLS_TRAPNR(ret) != epcm_trapnr;
+}
diff --git a/arch/x86/kernel/cpu/sgx/encls.h b/arch/x86/kernel/cpu/sgx/encls.h
new file mode 100644
index 000000000000..aea3b9d09936
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/encls.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+#ifndef _X86_ENCLS_H
+#define _X86_ENCLS_H
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/rwsem.h>
+#include <linux/types.h>
+#include <asm/asm.h>
+#include "arch.h"
+
+/**
+ * ENCLS_FAULT_FLAG - flag signifying an ENCLS return code is a trapnr
+ *
+ * ENCLS has its own (positive value) error codes and also generates
+ * ENCLS specific #GP and #PF faults. And the ENCLS values get munged
+ * with system error codes as everything percolates back up the stack.
+ * Unfortunately (for us), we need to precisely identify each unique
+ * error code, e.g. the action taken if EWB fails varies based on the
+ * type of fault and on the exact SGX error code, i.e. we can't simply
+ * convert all faults to -EFAULT.
+ *
+ * To make all three error types coexist, we set bit 30 to identify an
+ * ENCLS fault. Bit 31 (technically bits N:31) is used to differentiate
+ * between positive (faults and SGX error codes) and negative (system
+ * error codes) values.
+ */
+#define ENCLS_FAULT_FLAG 0x40000000
+
+/**
+ * Retrieve the encoded trapnr from the specified return code.
+ */
+#define ENCLS_TRAPNR(r) ((r) & ~ENCLS_FAULT_FLAG)
+
+/* Issue a WARN() about an ENCLS leaf. */
+#define ENCLS_WARN(r, name) { \
+ do { \
+ int _r = (r); \
+ WARN(_r, "sgx: %s returned %d (0x%x)\n", (name), _r, \
+ _r); \
+ } while (0); \
+}
+
+/**
+ * encls_faulted() - Check if ENCLS leaf function faulted
+ * @ret: the return value of an ENCLS leaf function call
+ *
+ * Return: true if the fault flag is set
+ */
+static inline bool encls_faulted(int ret)
+{
+ return (ret & ENCLS_FAULT_FLAG) != 0;
+}
+
+/**
+ * encls_returned_code() - Check if an ENCLS leaf function returned a code
+ * @ret: the return value of an ENCLS leaf function call
+ *
+ * Check if an ENCLS leaf function returned an error or information code.
+ *
+ * Return: true if there was a fault other than an EPCM conflict
+ */
+static inline bool encls_returned_code(int ret)
+{
+ return !encls_faulted(ret) && ret;
+}
+
+bool encls_failed(int ret);
+
+/**
+ * __encls_ret_N - encode an ENCLS leaf that returns an error code in EAX
+ * @rax: leaf number
+ * @inputs: asm inputs for the leaf
+ *
+ * Emit assembly for an ENCLS leaf that returns an error code, e.g. EREMOVE.
+ * And because SGX isn't complex enough as it is, leafs that return an error
+ * code also modify flags.
+ *
+ * Return:
+ * 0 on success,
+ * SGX error code on failure
+ */
+#define __encls_ret_N(rax, inputs...) \
+ ({ \
+ int ret; \
+ asm volatile( \
+ "1: .byte 0x0f, 0x01, 0xcf;\n\t" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE_FAULT(1b, 3b) \
+ : "=a"(ret) \
+ : "a"(rax), inputs \
+ : "memory", "cc"); \
+ ret; \
+ })
+
+#define __encls_ret_1(rax, rcx) \
+ ({ \
+ __encls_ret_N(rax, "c"(rcx)); \
+ })
+
+#define __encls_ret_2(rax, rbx, rcx) \
+ ({ \
+ __encls_ret_N(rax, "b"(rbx), "c"(rcx)); \
+ })
+
+#define __encls_ret_3(rax, rbx, rcx, rdx) \
+ ({ \
+ __encls_ret_N(rax, "b"(rbx), "c"(rcx), "d"(rdx)); \
+ })
+
+/**
+ * __encls_N - encode an ENCLS leaf that doesn't return an error code
+ * @rax: leaf number
+ * @rbx_out: optional output variable
+ * @inputs: asm inputs for the leaf
+ *
+ * Emit assembly for an ENCLS leaf that does not return an error code,
+ * e.g. ECREATE. Leaves without error codes either succeed or fault.
+ * @rbx_out is an optional parameter for use by EDGBRD, which returns
+ * the the requested value in RBX.
+ *
+ * Return:
+ * 0 on success,
+ * trapnr with ENCLS_FAULT_FLAG set on fault
+ */
+#define __encls_N(rax, rbx_out, inputs...) \
+ ({ \
+ int ret; \
+ asm volatile( \
+ "1: .byte 0x0f, 0x01, 0xcf;\n\t" \
+ " xor %%eax,%%eax;\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE_FAULT(1b, 3b) \
+ : "=a"(ret), "=b"(rbx_out) \
+ : "a"(rax), inputs \
+ : "memory"); \
+ ret; \
+ })
+
+#define __encls_2(rax, rbx, rcx) \
+ ({ \
+ unsigned long ign_rbx_out; \
+ __encls_N(rax, ign_rbx_out, "b"(rbx), "c"(rcx)); \
+ })
+
+#define __encls_1_1(rax, data, rcx) \
+ ({ \
+ unsigned long rbx_out; \
+ int ret = __encls_N(rax, rbx_out, "c"(rcx)); \
+ if (!ret) \
+ data = rbx_out; \
+ ret; \
+ })
+
+static inline int __ecreate(struct sgx_pageinfo *pginfo, void *secs)
+{
+ return __encls_2(SGX_ECREATE, pginfo, secs);
+}
+
+static inline int __eextend(void *secs, void *addr)
+{
+ return __encls_2(SGX_EEXTEND, secs, addr);
+}
+
+static inline int __eadd(struct sgx_pageinfo *pginfo, void *addr)
+{
+ return __encls_2(SGX_EADD, pginfo, addr);
+}
+
+static inline int __einit(void *sigstruct, struct sgx_einittoken *einittoken,
+ void *secs)
+{
+ return __encls_ret_3(SGX_EINIT, sigstruct, secs, einittoken);
+}
+
+static inline int __eremove(void *addr)
+{
+ return __encls_ret_1(SGX_EREMOVE, addr);
+}
+
+static inline int __edbgwr(void *addr, unsigned long *data)
+{
+ return __encls_2(SGX_EDGBWR, *data, addr);
+}
+
+static inline int __edbgrd(void *addr, unsigned long *data)
+{
+ return __encls_1_1(SGX_EDGBRD, *data, addr);
+}
+
+static inline int __etrack(void *addr)
+{
+ return __encls_ret_1(SGX_ETRACK, addr);
+}
+
+static inline int __eldu(struct sgx_pageinfo *pginfo, void *addr,
+ void *va)
+{
+ return __encls_ret_3(SGX_ELDU, pginfo, addr, va);
+}
+
+static inline int __eblock(void *addr)
+{
+ return __encls_ret_1(SGX_EBLOCK, addr);
+}
+
+static inline int __epa(void *addr)
+{
+ unsigned long rbx = SGX_PAGE_TYPE_VA;
+
+ return __encls_2(SGX_EPA, rbx, addr);
+}
+
+static inline int __ewb(struct sgx_pageinfo *pginfo, void *addr,
+ void *va)
+{
+ return __encls_ret_3(SGX_EWB, pginfo, addr, va);
+}
+
+static inline int __eaug(struct sgx_pageinfo *pginfo, void *addr)
+{
+ return __encls_2(SGX_EAUG, pginfo, addr);
+}
+
+static inline int __emodpr(struct sgx_secinfo *secinfo, void *addr)
+{
+ return __encls_ret_2(SGX_EMODPR, secinfo, addr);
+}
+
+static inline int __emodt(struct sgx_secinfo *secinfo, void *addr)
+{
+ return __encls_ret_2(SGX_EMODT, secinfo, addr);
+}
+
+#endif /* _X86_ENCLS_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 12/28] x86/sgx: Enumerate and track EPC sections
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (10 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 11/28] x86/sgx: Add wrappers for ENCLS leaf functions Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 13/28] x86/sgx: Add functions to allocate and free EPC pages Jarkko Sakkinen
` (21 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
Enumerate Enclave Page Cache (EPC) sections via CPUID and add the data
structures necessary to track EPC pages so that they can be allocated,
freed and managed. As a system may have multiple EPC sections, invoke
CPUID on SGX sub-leafs until an invalid leaf is encountered.
On NUMA systems, a node can have at most one bank. A bank can be at
most part of two nodes. SGX supports both nodes with a single memory
controller and also sub-cluster nodes with severals memory controllers
on a single die.
For simplicity, support a maximum of eight EPC sections. Current
client hardware supports only a single section, while upcoming server
hardware will support at most eight sections. Bounding the number of
sections also allows the section ID to be embedded along with a page's
offset in a single unsigned long, enabling easy retrieval of both the
VA and PA for a given page.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
---
arch/x86/Kconfig | 12 +++
arch/x86/kernel/cpu/Makefile | 1 +
arch/x86/kernel/cpu/sgx/Makefile | 2 +-
arch/x86/kernel/cpu/sgx/main.c | 158 ++++++++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/reclaim.c | 84 ++++++++++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 67 +++++++++++++
6 files changed, 323 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/kernel/cpu/sgx/main.c
create mode 100644 arch/x86/kernel/cpu/sgx/reclaim.c
create mode 100644 arch/x86/kernel/cpu/sgx/sgx.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 5ad92419be19..5d90a20621cb 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1921,6 +1921,18 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS
If unsure, say y.
+config INTEL_SGX
+ bool "Intel SGX core functionality"
+ depends on X86_64 && CPU_SUP_INTEL
+ ---help---
+ Intel(R) SGX is a set of CPU instructions that can be used by
+ applications to set aside private regions of code and data, referred
+ to as enclaves. An enclave's private memory can only be accessed by
+ code running within the enclave. Accesses from outside the enclave,
+ including other enclaves, are disallowed by hardware.
+
+ If unsure, say N.
+
config EFI
bool "EFI runtime service support"
depends on ACPI
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index cfd24f9f7614..d1163c0fd5d6 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_X86_MCE) += mce/
obj-$(CONFIG_MTRR) += mtrr/
obj-$(CONFIG_MICROCODE) += microcode/
obj-$(CONFIG_X86_CPU_RESCTRL) += resctrl/
+obj-$(CONFIG_INTEL_SGX) += sgx/
obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o
diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile
index 4432d935894e..fa930e292110 100644
--- a/arch/x86/kernel/cpu/sgx/Makefile
+++ b/arch/x86/kernel/cpu/sgx/Makefile
@@ -1 +1 @@
-obj-y += encls.o
+obj-y += encls.o main.o reclaim.o
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
new file mode 100644
index 000000000000..e2317f6e4374
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-17 Intel Corporation.
+
+#include <linux/freezer.h>
+#include <linux/highmem.h>
+#include <linux/kthread.h>
+#include <linux/pagemap.h>
+#include <linux/ratelimit.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include "arch.h"
+#include "sgx.h"
+
+struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
+EXPORT_SYMBOL_GPL(sgx_epc_sections);
+
+int sgx_nr_epc_sections;
+
+static __init void sgx_free_epc_section(struct sgx_epc_section *section)
+{
+ struct sgx_epc_page *page;
+
+ while (!list_empty(§ion->page_list)) {
+ page = list_first_entry(§ion->page_list,
+ struct sgx_epc_page, list);
+ list_del(&page->list);
+ kfree(page);
+ }
+
+ while (!list_empty(§ion->unsanitized_page_list)) {
+ page = list_first_entry(§ion->unsanitized_page_list,
+ struct sgx_epc_page, list);
+ list_del(&page->list);
+ kfree(page);
+ }
+
+ memunmap(section->va);
+}
+
+static __init int sgx_init_epc_section(u64 addr, u64 size, unsigned long index,
+ struct sgx_epc_section *section)
+{
+ unsigned long nr_pages = size >> PAGE_SHIFT;
+ struct sgx_epc_page *page;
+ unsigned long i;
+
+ section->va = memremap(addr, size, MEMREMAP_WB);
+ if (!section->va)
+ return -ENOMEM;
+
+ section->pa = addr;
+ spin_lock_init(§ion->lock);
+ INIT_LIST_HEAD(§ion->page_list);
+ INIT_LIST_HEAD(§ion->unsanitized_page_list);
+
+ for (i = 0; i < nr_pages; i++) {
+ page = kzalloc(sizeof(*page), GFP_KERNEL);
+ if (!page)
+ goto out;
+ page->desc = (addr + (i << PAGE_SHIFT)) | index;
+ list_add_tail(&page->list, §ion->unsanitized_page_list);
+ section->free_cnt++;
+ }
+
+ return 0;
+out:
+ sgx_free_epc_section(section);
+ return -ENOMEM;
+}
+
+static __init void sgx_page_cache_teardown(void)
+{
+ int i;
+
+ for (i = 0; i < sgx_nr_epc_sections; i++)
+ sgx_free_epc_section(&sgx_epc_sections[i]);
+}
+
+/**
+ * A section metric is concatenated in a way that @low bits 12-31 define the
+ * bits 12-31 of the metric and @high bits 0-19 define the bits 32-51 of the
+ * metric.
+ */
+static inline u64 sgx_calc_section_metric(u64 low, u64 high)
+{
+ return (low & GENMASK_ULL(31, 12)) +
+ ((high & GENMASK_ULL(19, 0)) << 32);
+}
+
+static __init int sgx_page_cache_init(void)
+{
+ u32 eax, ebx, ecx, edx, type;
+ u64 pa, size;
+ int ret;
+ int i;
+
+ BUILD_BUG_ON(SGX_MAX_EPC_SECTIONS > (SGX_EPC_SECTION_MASK + 1));
+
+ for (i = 0; i < (SGX_MAX_EPC_SECTIONS + 1); i++) {
+ cpuid_count(SGX_CPUID, i + SGX_CPUID_FIRST_VARIABLE_SUB_LEAF,
+ &eax, &ebx, &ecx, &edx);
+
+ type = eax & SGX_CPUID_SUB_LEAF_TYPE_MASK;
+ if (type == SGX_CPUID_SUB_LEAF_INVALID)
+ break;
+ if (type != SGX_CPUID_SUB_LEAF_EPC_SECTION) {
+ pr_err_once("sgx: Unknown sub-leaf type: %u\n", type);
+ return -ENODEV;
+ }
+ if (i == SGX_MAX_EPC_SECTIONS) {
+ pr_warn("sgx: More than "
+ __stringify(SGX_MAX_EPC_SECTIONS)
+ " EPC sections\n");
+ break;
+ }
+
+ pa = sgx_calc_section_metric(eax, ebx);
+ size = sgx_calc_section_metric(ecx, edx);
+ pr_info("sgx: EPC section 0x%llx-0x%llx\n", pa, pa + size - 1);
+
+ ret = sgx_init_epc_section(pa, size, i, &sgx_epc_sections[i]);
+ if (ret) {
+ sgx_page_cache_teardown();
+ return ret;
+ }
+
+ sgx_nr_epc_sections++;
+ }
+
+ if (!sgx_nr_epc_sections) {
+ pr_err("sgx: There are zero EPC sections.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static __init int sgx_init(void)
+{
+ int ret;
+
+ if (!boot_cpu_has(X86_FEATURE_SGX))
+ return false;
+
+ ret = sgx_page_cache_init();
+ if (ret)
+ return ret;
+
+ ret = sgx_page_reclaimer_init();
+ if (ret) {
+ sgx_page_cache_teardown();
+ return ret;
+ }
+
+ return 0;
+}
+
+arch_initcall(sgx_init);
diff --git a/arch/x86/kernel/cpu/sgx/reclaim.c b/arch/x86/kernel/cpu/sgx/reclaim.c
new file mode 100644
index 000000000000..042769f03be9
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/reclaim.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-19 Intel Corporation.
+
+#include <linux/freezer.h>
+#include <linux/highmem.h>
+#include <linux/kthread.h>
+#include <linux/pagemap.h>
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/signal.h>
+#include "encls.h"
+#include "sgx.h"
+
+static struct task_struct *ksgxswapd_tsk;
+
+static void sgx_sanitize_section(struct sgx_epc_section *section)
+{
+ struct sgx_epc_page *page, *tmp;
+ LIST_HEAD(secs_list);
+ int ret;
+
+ while (!list_empty(§ion->unsanitized_page_list)) {
+ if (kthread_should_stop())
+ return;
+
+ spin_lock(§ion->lock);
+
+ page = list_first_entry(§ion->unsanitized_page_list,
+ struct sgx_epc_page, list);
+
+ ret = __eremove(sgx_epc_addr(page));
+ if (!ret)
+ list_move(&page->list, §ion->page_list);
+ else
+ list_move_tail(&page->list, &secs_list);
+
+ spin_unlock(§ion->lock);
+
+ cond_resched();
+ }
+
+ list_for_each_entry_safe(page, tmp, &secs_list, list) {
+ if (kthread_should_stop())
+ return;
+
+ ret = __eremove(sgx_epc_addr(page));
+ if (!WARN_ON_ONCE(ret)) {
+ spin_lock(§ion->lock);
+ list_move(&page->list, §ion->page_list);
+ spin_unlock(§ion->lock);
+ } else {
+ list_del(&page->list);
+ kfree(page);
+ }
+
+ cond_resched();
+ }
+}
+
+static int ksgxswapd(void *p)
+{
+ int i;
+
+ set_freezable();
+
+ for (i = 0; i < sgx_nr_epc_sections; i++)
+ sgx_sanitize_section(&sgx_epc_sections[i]);
+
+ return 0;
+}
+
+int sgx_page_reclaimer_init(void)
+{
+ struct task_struct *tsk;
+
+ tsk = kthread_run(ksgxswapd, NULL, "ksgxswapd");
+ if (IS_ERR(tsk))
+ return PTR_ERR(tsk);
+
+ ksgxswapd_tsk = tsk;
+
+ return 0;
+}
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
new file mode 100644
index 000000000000..3009ec816339
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+#ifndef _X86_SGX_H
+#define _X86_SGX_H
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/rwsem.h>
+#include <linux/types.h>
+#include <asm/asm.h>
+#include <uapi/asm/sgx_errno.h>
+
+struct sgx_epc_page {
+ unsigned long desc;
+ struct list_head list;
+};
+
+/**
+ * struct sgx_epc_section
+ *
+ * The firmware can define multiple chunks of EPC to the different areas of the
+ * physical memory e.g. for memory areas of the each node. This structure is
+ * used to store EPC pages for one EPC section and virtual memory area where
+ * the pages have been mapped.
+ */
+struct sgx_epc_section {
+ unsigned long pa;
+ void *va;
+ struct list_head page_list;
+ struct list_head unsanitized_page_list;
+ unsigned long free_cnt;
+ spinlock_t lock;
+};
+
+#define SGX_MAX_EPC_SECTIONS 8
+
+extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
+
+/**
+ * enum sgx_epc_page_desc - bits and masks for an EPC page's descriptor
+ * %SGX_EPC_SECTION_MASK: SGX allows to have multiple EPC sections in the
+ * physical memory. The existing and near-future
+ * hardware defines at most eight sections, hence
+ * three bits to hold a section.
+ */
+enum sgx_epc_page_desc {
+ SGX_EPC_SECTION_MASK = GENMASK_ULL(3, 0),
+ /* bits 12-63 are reserved for the physical page address of the page */
+};
+
+static inline struct sgx_epc_section *sgx_epc_section(struct sgx_epc_page *page)
+{
+ return &sgx_epc_sections[page->desc & SGX_EPC_SECTION_MASK];
+}
+
+static inline void *sgx_epc_addr(struct sgx_epc_page *page)
+{
+ struct sgx_epc_section *section = sgx_epc_section(page);
+
+ return section->va + (page->desc & PAGE_MASK) - section->pa;
+}
+
+extern int sgx_nr_epc_sections;
+
+int sgx_page_reclaimer_init(void);
+
+#endif /* _X86_SGX_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 13/28] x86/sgx: Add functions to allocate and free EPC pages
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (11 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 12/28] x86/sgx: Enumerate and track EPC sections Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 14/28] x86/sgx: Add sgx_einit() for initializing enclaves Jarkko Sakkinen
` (20 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
At this time there is no support for reclaiming pages prior to the
owner explicitly freeing the page. As for freeing pages, because
freeing a page is expected to succeed in the vast majority of cases
and because most call sites will not be equipped to handle failure,
provide a variant for freeing a page that warns on failure, e.g. due
to ENCLS[EREMOVE] failing.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/kernel/cpu/sgx/main.c | 90 ++++++++++++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 4 ++
2 files changed, 94 insertions(+)
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index e2317f6e4374..6b4727df72ca 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -9,6 +9,7 @@
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include "arch.h"
+#include "encls.h"
#include "sgx.h"
struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
@@ -16,6 +17,95 @@ EXPORT_SYMBOL_GPL(sgx_epc_sections);
int sgx_nr_epc_sections;
+static struct sgx_epc_page *sgx_section_get_page(
+ struct sgx_epc_section *section)
+{
+ struct sgx_epc_page *page;
+
+ if (!section->free_cnt)
+ return NULL;
+
+ page = list_first_entry(§ion->page_list,
+ struct sgx_epc_page, list);
+ list_del_init(&page->list);
+ section->free_cnt--;
+ return page;
+}
+
+/**
+ * sgx_alloc_page - Allocate an EPC page
+ *
+ * Try to grab a page from the free EPC page list.
+ *
+ * Return:
+ * a pointer to a &struct sgx_epc_page instance,
+ * -errno on error
+ */
+struct sgx_epc_page *sgx_alloc_page(void)
+{
+ struct sgx_epc_section *section;
+ struct sgx_epc_page *page;
+ int i;
+
+ for (i = 0; i < sgx_nr_epc_sections; i++) {
+ section = &sgx_epc_sections[i];
+ spin_lock(§ion->lock);
+ page = sgx_section_get_page(section);
+ spin_unlock(§ion->lock);
+
+ if (page)
+ return page;
+ }
+
+ return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(sgx_alloc_page);
+
+/**
+ * __sgx_free_page - Free an EPC page
+ * @page: pointer a previously allocated EPC page
+ *
+ * EREMOVE an EPC page and insert it back to the list of free pages.
+ *
+ * Return:
+ * 0 on success
+ * SGX error code if EREMOVE fails
+ */
+int __sgx_free_page(struct sgx_epc_page *page)
+{
+ struct sgx_epc_section *section = sgx_epc_section(page);
+ int ret;
+
+ ret = __eremove(sgx_epc_addr(page));
+ if (ret)
+ return ret;
+
+ spin_lock(§ion->lock);
+ list_add_tail(&page->list, §ion->page_list);
+ section->free_cnt++;
+ spin_unlock(§ion->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__sgx_free_page);
+
+/**
+ * sgx_free_page - Free an EPC page and WARN on failure
+ * @page: pointer to a previously allocated EPC page
+ *
+ * EREMOVE an EPC page and insert it back to the list of free pages, and WARN
+ * if EREMOVE fails. For use when the call site cannot (or chooses not to)
+ * handle failure, i.e. the page is leaked on failure.
+ */
+void sgx_free_page(struct sgx_epc_page *page)
+{
+ int ret;
+
+ ret = __sgx_free_page(page);
+ WARN(ret > 0, "sgx: EREMOVE returned %d (0x%x)", ret, ret);
+}
+EXPORT_SYMBOL_GPL(sgx_free_page);
+
static __init void sgx_free_epc_section(struct sgx_epc_section *section)
{
struct sgx_epc_page *page;
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index 3009ec816339..210510a28ce0 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -64,4 +64,8 @@ extern int sgx_nr_epc_sections;
int sgx_page_reclaimer_init(void);
+struct sgx_epc_page *sgx_alloc_page(void);
+int __sgx_free_page(struct sgx_epc_page *page);
+void sgx_free_page(struct sgx_epc_page *page);
+
#endif /* _X86_SGX_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 14/28] x86/sgx: Add sgx_einit() for initializing enclaves
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (12 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 13/28] x86/sgx: Add functions to allocate and free EPC pages Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver Jarkko Sakkinen
` (19 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
Add a helper function to perform ENCLS(EINIT) with the correct LE
hash MSR values. ENCLS[EINIT] initializes an enclave, verifying the
enclave's measurement and preparing it for execution, i.e. the enclave
cannot be run until it has been initialized. The measurement aspect
of EINIT references the MSR_IA32_SGXLEPUBKEYHASH* MSRs, with the CPU
comparing CPU compares the key (technically its hash) used to sign the
enclave[1] with the key hash stored in the MSRs, and will reject EINIT
if the keys do not match.
A per-cpu cache is used to avoid writing the MSRs as writing the MSRs
is extraordinarily expensive, e.g. 300-400 cycles per MSR. Because
the cache may become stale, force update the MSRs and retry EINIT if
the first EINIT fails due to an "invalid token". An invalid token
error does not necessarily mean the MSRs need to be updated, but the
cost of an unnecessary write is minimal relative to the cost of EINIT
itself.
[1] For EINIT's purposes, the effective signer of the enclave may be
the enclave's owner, or a separate Launch Enclave that has created
an EINIT token for the target enclave. When using an EINIT token,
the key used to sign the token must match the MSRs in order for
EINIT to succeed.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/kernel/cpu/sgx/main.c | 51 ++++++++++++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 2 ++
2 files changed, 53 insertions(+)
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index 6b4727df72ca..d3ed742e90fe 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -17,6 +17,9 @@ EXPORT_SYMBOL_GPL(sgx_epc_sections);
int sgx_nr_epc_sections;
+/* A per-cpu cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs. */
+static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache);
+
static struct sgx_epc_page *sgx_section_get_page(
struct sgx_epc_section *section)
{
@@ -106,6 +109,54 @@ void sgx_free_page(struct sgx_epc_page *page)
}
EXPORT_SYMBOL_GPL(sgx_free_page);
+static void sgx_update_lepubkeyhash_msrs(u64 *lepubkeyhash, bool enforce)
+{
+ u64 *cache;
+ int i;
+
+ cache = per_cpu(sgx_lepubkeyhash_cache, smp_processor_id());
+ for (i = 0; i < 4; i++) {
+ if (enforce || (lepubkeyhash[i] != cache[i])) {
+ wrmsrl(MSR_IA32_SGXLEPUBKEYHASH0 + i, lepubkeyhash[i]);
+ cache[i] = lepubkeyhash[i];
+ }
+ }
+}
+
+/**
+ * sgx_einit - initialize an enclave
+ * @sigstruct: a pointer a SIGSTRUCT
+ * @token: a pointer an EINITTOKEN (optional)
+ * @secs: a pointer a SECS
+ * @lepubkeyhash: the desired value for IA32_SGXLEPUBKEYHASHx MSRs
+ *
+ * Execute ENCLS[EINIT], writing the IA32_SGXLEPUBKEYHASHx MSRs according
+ * to @lepubkeyhash (if possible and necessary).
+ *
+ * Return:
+ * 0 on success,
+ * -errno or SGX error on failure
+ */
+int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
+ struct sgx_epc_page *secs, u64 *lepubkeyhash)
+{
+ int ret;
+
+ if (!boot_cpu_has(X86_FEATURE_SGX_LC))
+ return __einit(sigstruct, token, sgx_epc_addr(secs));
+
+ preempt_disable();
+ sgx_update_lepubkeyhash_msrs(lepubkeyhash, false);
+ ret = __einit(sigstruct, token, sgx_epc_addr(secs));
+ if (ret == SGX_INVALID_EINITTOKEN) {
+ sgx_update_lepubkeyhash_msrs(lepubkeyhash, true);
+ ret = __einit(sigstruct, token, sgx_epc_addr(secs));
+ }
+ preempt_enable();
+ return ret;
+}
+EXPORT_SYMBOL(sgx_einit);
+
static __init void sgx_free_epc_section(struct sgx_epc_section *section)
{
struct sgx_epc_page *page;
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index 210510a28ce0..41d4130c33a2 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -67,5 +67,7 @@ int sgx_page_reclaimer_init(void);
struct sgx_epc_page *sgx_alloc_page(void);
int __sgx_free_page(struct sgx_epc_page *page);
void sgx_free_page(struct sgx_epc_page *page);
+int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
+ struct sgx_epc_page *secs, u64 *lepubkeyhash);
#endif /* _X86_SGX_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (13 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 14/28] x86/sgx: Add sgx_einit() for initializing enclaves Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-22 21:58 ` Sean Christopherson
2019-04-17 10:39 ` [PATCH v20 16/28] x86/sgx: Add provisioning Jarkko Sakkinen
` (18 subsequent siblings)
33 siblings, 1 reply; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
Intel Software Guard eXtensions (SGX) is a set of CPU instructions that
can be used by applications to set aside private regions of code and
data. The code outside the enclave is disallowed to access the memory
inside the enclave by the CPU access control.
This commit adds the Linux SGX Enclave Driver that provides an ioctl API
to manage enclaves. The address range for an enclave, commonly referred
as ELRANGE in the documentation (e.g. Intel SDM), is reserved with
mmap() against /dev/sgx/enclave. After that a set ioctls is used to
build the enclave to the ELRANGE.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
Co-developed-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
Signed-off-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
---
Documentation/ioctl/ioctl-number.txt | 1 +
arch/x86/Kconfig | 15 +
arch/x86/include/uapi/asm/sgx.h | 57 ++
arch/x86/kernel/cpu/sgx/Makefile | 3 +-
arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
arch/x86/kernel/cpu/sgx/driver/driver.h | 38 ++
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 750 ++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/driver/main.c | 358 +++++++++++
arch/x86/kernel/cpu/sgx/encl.c | 349 +++++++++++
arch/x86/kernel/cpu/sgx/encl.h | 98 ++++
arch/x86/kernel/cpu/sgx/encls.c | 1 +
arch/x86/kernel/cpu/sgx/main.c | 3 +
arch/x86/kernel/cpu/sgx/sgx.h | 1 +
13 files changed, 1676 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/include/uapi/asm/sgx.h
create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index c9558146ac58..ef2694221cd0 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -312,6 +312,7 @@ Code Seq#(hex) Include File Comments
<mailto:tlewis@mindspring.com>
0xA3 90-9F linux/dtlk.h
0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
+0xA4 00-02 uapi/asm/sgx.h conflict!
0xAA 00-3F linux/uapi/linux/userfaultfd.h
0xAB 00-1F linux/nbd.h
0xAC 00-1F linux/raw.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 5d90a20621cb..adea370b331f 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1933,6 +1933,21 @@ config INTEL_SGX
If unsure, say N.
+config INTEL_SGX_DRIVER
+ tristate "Intel(R) SGX Driver"
+ default n
+ depends on X86_64 && CPU_SUP_INTEL && INTEL_SGX
+ select CRYPTO
+ select CRYPTO_SHA256
+ ---help---
+ This options enables the kernel SGX driver that allows to construct
+ enclaves to the process memory by using a device node (by default
+ /dev/sgx) and a set of ioctls. The driver requires that the MSRs
+ specifying the public key hash for the launch enclave are writable so
+ that Linux has the full control to run enclaves.
+
+ If unsure, say N.
+
config EFI
bool "EFI runtime service support"
depends on ACPI
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
new file mode 100644
index 000000000000..7bf627ac4958
--- /dev/null
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+#ifndef _UAPI_ASM_X86_SGX_H
+#define _UAPI_ASM_X86_SGX_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define SGX_MAGIC 0xA4
+
+#define SGX_IOC_ENCLAVE_CREATE \
+ _IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create)
+#define SGX_IOC_ENCLAVE_ADD_PAGE \
+ _IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page)
+#define SGX_IOC_ENCLAVE_INIT \
+ _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
+
+/* IOCTL return values */
+#define SGX_POWER_LOST_ENCLAVE 0x40000000
+
+/**
+ * struct sgx_enclave_create - parameter structure for the
+ * %SGX_IOC_ENCLAVE_CREATE ioctl
+ * @src: address for the SECS page data
+ */
+struct sgx_enclave_create {
+ __u64 src;
+};
+
+/**
+ * struct sgx_enclave_add_page - parameter structure for the
+ * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl
+ * @addr: address within the ELRANGE
+ * @src: address for the page data
+ * @secinfo: address for the SECINFO data
+ * @mrmask: bitmask for the measured 256 byte chunks
+ */
+struct sgx_enclave_add_page {
+ __u64 addr;
+ __u64 src;
+ __u64 secinfo;
+ __u16 mrmask;
+} __attribute__((__packed__));
+
+
+/**
+ * struct sgx_enclave_init - parameter structure for the
+ * %SGX_IOC_ENCLAVE_INIT ioctl
+ * @sigstruct: address for the SIGSTRUCT data
+ */
+struct sgx_enclave_init {
+ __u64 sigstruct;
+};
+
+#endif /* _UAPI_ASM_X86_SGX_H */
diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile
index fa930e292110..e5d1e862969c 100644
--- a/arch/x86/kernel/cpu/sgx/Makefile
+++ b/arch/x86/kernel/cpu/sgx/Makefile
@@ -1 +1,2 @@
-obj-y += encls.o main.o reclaim.o
+obj-y += encl.o encls.o main.o reclaim.o
+obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/
diff --git a/arch/x86/kernel/cpu/sgx/driver/Makefile b/arch/x86/kernel/cpu/sgx/driver/Makefile
new file mode 100644
index 000000000000..01ebbbb06a47
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_INTEL_SGX_DRIVER) += sgx.o
+sgx-$(CONFIG_INTEL_SGX_DRIVER) += ioctl.o
+sgx-$(CONFIG_INTEL_SGX_DRIVER) += main.o
diff --git a/arch/x86/kernel/cpu/sgx/driver/driver.h b/arch/x86/kernel/cpu/sgx/driver/driver.h
new file mode 100644
index 000000000000..507712eb0a68
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/driver.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-19 Intel Corporation.
+ */
+#ifndef __ARCH_INTEL_SGX_H__
+#define __ARCH_INTEL_SGX_H__
+
+#include <crypto/hash.h>
+#include <linux/kref.h>
+#include <linux/mmu_notifier.h>
+#include <linux/radix-tree.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <uapi/asm/sgx.h>
+#include "../arch.h"
+#include "../encl.h"
+#include "../encls.h"
+#include "../sgx.h"
+
+#define SGX_DRV_NR_DEVICES 2
+#define SGX_EINIT_SPIN_COUNT 20
+#define SGX_EINIT_SLEEP_COUNT 50
+#define SGX_EINIT_SLEEP_TIME 20
+
+extern struct workqueue_struct *sgx_encl_wq;
+extern u64 sgx_encl_size_max_32;
+extern u64 sgx_encl_size_max_64;
+extern u32 sgx_misc_reserved_mask;
+extern u64 sgx_attributes_reserved_mask;
+extern u64 sgx_xfrm_reserved_mask;
+extern u32 sgx_xsave_size_tbl[64];
+
+extern const struct file_operations sgx_fs_provision_fops;
+
+long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+
+#endif /* __ARCH_X86_INTEL_SGX_H__ */
diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
new file mode 100644
index 000000000000..f88226518b21
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-19 Intel Corporation.
+
+#include <asm/mman.h>
+#include <linux/delay.h>
+#include <linux/file.h>
+#include <linux/hashtable.h>
+#include <linux/highmem.h>
+#include <linux/ratelimit.h>
+#include <linux/sched/signal.h>
+#include <linux/shmem_fs.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include "driver.h"
+
+struct sgx_add_page_req {
+ struct sgx_encl *encl;
+ struct sgx_encl_page *encl_page;
+ struct sgx_secinfo secinfo;
+ unsigned long mrmask;
+ struct list_head list;
+};
+
+static bool sgx_process_add_page_req(struct sgx_add_page_req *req,
+ struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = req->encl_page;
+ struct sgx_encl *encl = req->encl;
+ unsigned long page_index = sgx_encl_get_index(encl, encl_page);
+ struct sgx_secinfo secinfo;
+ struct sgx_pageinfo pginfo;
+ struct page *backing;
+ unsigned long addr;
+ int ret;
+ int i;
+
+ if (encl->flags & (SGX_ENCL_SUSPEND | SGX_ENCL_DEAD))
+ return false;
+
+ addr = SGX_ENCL_PAGE_ADDR(encl_page);
+
+ backing = sgx_encl_get_backing_page(encl, page_index);
+ if (IS_ERR(backing))
+ return false;
+
+ /*
+ * The SECINFO field must be 64-byte aligned, copy it to a local
+ * variable that is guaranteed to be aligned as req->secinfo may
+ * or may not be 64-byte aligned, e.g. req may have been allocated
+ * via kzalloc which is not aware of __aligned attributes.
+ */
+ memcpy(&secinfo, &req->secinfo, sizeof(secinfo));
+
+ pginfo.secs = (unsigned long)sgx_epc_addr(encl->secs.epc_page);
+ pginfo.addr = addr;
+ pginfo.metadata = (unsigned long)&secinfo;
+ pginfo.contents = (unsigned long)kmap_atomic(backing);
+ ret = __eadd(&pginfo, sgx_epc_addr(epc_page));
+ kunmap_atomic((void *)(unsigned long)pginfo.contents);
+
+ put_page(backing);
+
+ if (ret) {
+ if (encls_failed(ret))
+ ENCLS_WARN(ret, "EADD");
+ return false;
+ }
+
+ for_each_set_bit(i, &req->mrmask, 16) {
+ ret = __eextend(sgx_epc_addr(encl->secs.epc_page),
+ sgx_epc_addr(epc_page) + (i * 0x100));
+ if (ret) {
+ if (encls_failed(ret))
+ ENCLS_WARN(ret, "EEXTEND");
+ return false;
+ }
+ }
+
+ encl_page->encl = encl;
+ encl_page->epc_page = epc_page;
+ encl->secs_child_cnt++;
+
+ return true;
+}
+
+static void sgx_add_page_worker(struct work_struct *work)
+{
+ struct sgx_add_page_req *req;
+ bool skip_rest = false;
+ bool is_empty = false;
+ struct sgx_encl *encl;
+ struct sgx_epc_page *epc_page;
+
+ encl = container_of(work, struct sgx_encl, work);
+
+ do {
+ schedule();
+
+ mutex_lock(&encl->lock);
+ if (encl->flags & SGX_ENCL_DEAD)
+ skip_rest = true;
+
+ req = list_first_entry(&encl->add_page_reqs,
+ struct sgx_add_page_req, list);
+ list_del(&req->list);
+ is_empty = list_empty(&encl->add_page_reqs);
+ mutex_unlock(&encl->lock);
+
+ if (skip_rest)
+ goto next;
+
+ epc_page = sgx_alloc_page();
+
+ mutex_lock(&encl->lock);
+
+ if (IS_ERR(epc_page)) {
+ sgx_encl_destroy(encl);
+ skip_rest = true;
+ } else if (!sgx_process_add_page_req(req, epc_page)) {
+ sgx_free_page(epc_page);
+ sgx_encl_destroy(encl);
+ skip_rest = true;
+ }
+
+ mutex_unlock(&encl->lock);
+
+next:
+ kfree(req);
+ } while (!kref_put(&encl->refcount, sgx_encl_release) && !is_empty);
+}
+
+static u32 sgx_calc_ssaframesize(u32 miscselect, u64 xfrm)
+{
+ u32 size_max = PAGE_SIZE;
+ u32 size;
+ int i;
+
+ for (i = 2; i < 64; i++) {
+ 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,
+ unsigned long ssaframesize)
+{
+ 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->attributes & SGX_ATTR_MODE64BIT) {
+ if (secs->size > sgx_encl_size_max_64)
+ return -EINVAL;
+ } else if (secs->size > sgx_encl_size_max_32)
+ 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 || ssaframesize > secs->ssa_frame_size)
+ return -EINVAL;
+
+ if (memchr_inv(secs->reserved1, 0, SGX_SECS_RESERVED1_SIZE) ||
+ memchr_inv(secs->reserved2, 0, SGX_SECS_RESERVED2_SIZE) ||
+ memchr_inv(secs->reserved3, 0, SGX_SECS_RESERVED3_SIZE) ||
+ memchr_inv(secs->reserved4, 0, SGX_SECS_RESERVED4_SIZE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct sgx_encl_page *sgx_encl_page_alloc(struct sgx_encl *encl,
+ unsigned long addr)
+{
+ struct sgx_encl_page *encl_page;
+ int ret;
+
+ if (radix_tree_lookup(&encl->page_tree, PFN_DOWN(addr)))
+ return ERR_PTR(-EEXIST);
+ encl_page = kzalloc(sizeof(*encl_page), GFP_KERNEL);
+ if (!encl_page)
+ return ERR_PTR(-ENOMEM);
+ encl_page->desc = addr;
+ encl_page->encl = encl;
+ ret = radix_tree_insert(&encl->page_tree, PFN_DOWN(encl_page->desc),
+ encl_page);
+ if (ret) {
+ kfree(encl_page);
+ return ERR_PTR(ret);
+ }
+ return encl_page;
+}
+
+static int sgx_encl_pm_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct sgx_encl *encl = container_of(nb, struct sgx_encl, pm_notifier);
+
+ if (action != PM_SUSPEND_PREPARE && action != PM_HIBERNATION_PREPARE)
+ return NOTIFY_DONE;
+
+ mutex_lock(&encl->lock);
+ sgx_encl_destroy(encl);
+ encl->flags |= SGX_ENCL_SUSPEND;
+ mutex_unlock(&encl->lock);
+ flush_work(&encl->work);
+ return NOTIFY_DONE;
+}
+
+static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
+{
+ unsigned long encl_size = secs->size + PAGE_SIZE;
+ struct sgx_epc_page *secs_epc;
+ struct sgx_encl_mm *encl_mm;
+ unsigned long ssaframesize;
+ struct sgx_pageinfo pginfo;
+ struct sgx_secinfo secinfo;
+ struct file *backing;
+ long ret;
+
+ mutex_lock(&encl->lock);
+
+ if (encl->flags & SGX_ENCL_CREATED) {
+ ret = -EFAULT;
+ goto err_out;
+ }
+
+ ssaframesize = sgx_calc_ssaframesize(secs->miscselect, secs->xfrm);
+ if (sgx_validate_secs(secs, ssaframesize)) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5),
+ VM_NORESERVE);
+ if (IS_ERR(backing)) {
+ ret = PTR_ERR(backing);
+ goto err_out;
+ }
+
+ encl->backing = backing;
+
+ INIT_WORK(&encl->work, sgx_add_page_worker);
+
+ encl_mm = sgx_encl_mm_add(encl, current->mm);
+ if (IS_ERR(encl_mm)) {
+ ret = PTR_ERR(encl_mm);
+ goto err_out;
+ }
+
+ secs_epc = sgx_alloc_page();
+ if (IS_ERR(secs_epc)) {
+ ret = PTR_ERR(secs_epc);
+ goto err_out;
+ }
+
+ 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_epc_addr(secs_epc));
+ if (ret) {
+ pr_debug("ECREATE returned %ld\n", ret);
+ goto err_out;
+ }
+
+ if (secs->attributes & SGX_ATTR_DEBUG)
+ encl->flags |= SGX_ENCL_DEBUG;
+
+ encl->pm_notifier.notifier_call = &sgx_encl_pm_notifier;
+ ret = register_pm_notifier(&encl->pm_notifier);
+ if (ret) {
+ encl->pm_notifier.notifier_call = NULL;
+ goto err_out;
+ }
+
+ 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;
+ encl->flags |= SGX_ENCL_CREATED;
+
+ mutex_unlock(&encl->lock);
+ return 0;
+
+err_out:
+ if (encl->secs.epc_page) {
+ sgx_free_page(encl->secs.epc_page);
+ encl->secs.epc_page = NULL;
+ }
+
+ if (encl->backing) {
+ fput(encl->backing);
+ encl->backing = NULL;
+ }
+
+ if (!list_empty(&encl->mm_list)) {
+ encl_mm = list_first_entry(&encl->mm_list, struct sgx_encl_mm,
+ list);
+ list_del(&encl_mm->list);
+ kfree(encl_mm);
+ }
+
+ mutex_unlock(&encl->lock);
+ return ret;
+}
+
+/**
+ * sgx_ioc_enclave_create - handler for %SGX_IOC_ENCLAVE_CREATE
+ * @filep: open file to /dev/sgx
+ * @cmd: the command value
+ * @arg: pointer to an &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().
+ *
+ * Return:
+ * 0 on success,
+ * -errno otherwise
+ */
+static long sgx_ioc_enclave_create(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_enclave_create *createp = (struct sgx_enclave_create *)arg;
+ struct sgx_encl *encl = filep->private_data;
+ struct page *secs_page;
+ struct sgx_secs *secs;
+ int ret;
+
+ secs_page = alloc_page(GFP_HIGHUSER);
+ if (!secs_page)
+ return -ENOMEM;
+
+ secs = kmap(secs_page);
+ if (copy_from_user(secs, (void __user *)createp->src, sizeof(*secs))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+
+ ret = sgx_encl_create(encl, secs);
+
+out:
+ kunmap(secs_page);
+ __free_page(secs_page);
+ return ret;
+}
+
+static int sgx_validate_secinfo(struct sgx_secinfo *secinfo)
+{
+ u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK;
+ u64 perm = secinfo->flags & SGX_SECINFO_PERMISSION_MASK;
+ int i;
+
+ if ((secinfo->flags & SGX_SECINFO_RESERVED_MASK) ||
+ ((perm & SGX_SECINFO_W) && !(perm & SGX_SECINFO_R)) ||
+ (page_type != SGX_SECINFO_TCS && page_type != SGX_SECINFO_TRIM &&
+ page_type != SGX_SECINFO_REG))
+ return -EINVAL;
+
+ for (i = 0; i < SGX_SECINFO_RESERVED_SIZE; i++)
+ if (secinfo->reserved[i])
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool sgx_validate_offset(struct sgx_encl *encl, unsigned long offset)
+{
+ if (offset & (PAGE_SIZE - 1))
+ return false;
+
+ if (offset >= encl->size)
+ return false;
+
+ return true;
+}
+
+static int sgx_validate_tcs(struct sgx_encl *encl, struct sgx_tcs *tcs)
+{
+ int i;
+
+ if (tcs->flags & SGX_TCS_RESERVED_MASK)
+ return -EINVAL;
+
+ if (tcs->flags & SGX_TCS_DBGOPTIN)
+ return -EINVAL;
+
+ if (!sgx_validate_offset(encl, tcs->ssa_offset))
+ return -EINVAL;
+
+ if (!sgx_validate_offset(encl, tcs->fs_offset))
+ return -EINVAL;
+
+ if (!sgx_validate_offset(encl, tcs->gs_offset))
+ return -EINVAL;
+
+ if ((tcs->fs_limit & 0xFFF) != 0xFFF)
+ return -EINVAL;
+
+ if ((tcs->gs_limit & 0xFFF) != 0xFFF)
+ return -EINVAL;
+
+ for (i = 0; i < SGX_TCS_RESERVED_SIZE; i++)
+ if (tcs->reserved[i])
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __sgx_encl_add_page(struct sgx_encl *encl,
+ struct sgx_encl_page *encl_page,
+ void *data,
+ struct sgx_secinfo *secinfo,
+ unsigned int mrmask)
+{
+ unsigned long page_index = sgx_encl_get_index(encl, encl_page);
+ u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK;
+ struct sgx_add_page_req *req = NULL;
+ struct page *backing;
+ void *backing_ptr;
+ int empty;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ backing = sgx_encl_get_backing_page(encl, page_index);
+ if (IS_ERR(backing)) {
+ kfree(req);
+ return PTR_ERR(backing);
+ }
+
+ backing_ptr = kmap(backing);
+ memcpy(backing_ptr, data, PAGE_SIZE);
+ kunmap(backing);
+ if (page_type == SGX_SECINFO_TCS)
+ encl_page->desc |= SGX_ENCL_PAGE_TCS;
+ memcpy(&req->secinfo, secinfo, sizeof(*secinfo));
+ req->encl = encl;
+ req->encl_page = encl_page;
+ req->mrmask = mrmask;
+ empty = list_empty(&encl->add_page_reqs);
+ kref_get(&encl->refcount);
+ list_add_tail(&req->list, &encl->add_page_reqs);
+ if (empty)
+ queue_work(sgx_encl_wq, &encl->work);
+ set_page_dirty(backing);
+ put_page(backing);
+ return 0;
+}
+
+static int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr,
+ void *data, struct sgx_secinfo *secinfo,
+ unsigned int mrmask)
+{
+ u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK;
+ struct sgx_encl_page *encl_page;
+ int ret;
+
+ if (sgx_validate_secinfo(secinfo))
+ return -EINVAL;
+ if (page_type == SGX_SECINFO_TCS) {
+ ret = sgx_validate_tcs(encl, data);
+ if (ret)
+ return ret;
+ }
+
+ mutex_lock(&encl->lock);
+
+ if (!(encl->flags & SGX_ENCL_CREATED) ||
+ (encl->flags & (SGX_ENCL_INITIALIZED | SGX_ENCL_DEAD))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ encl_page = sgx_encl_page_alloc(encl, addr);
+ if (IS_ERR(encl_page)) {
+ ret = PTR_ERR(encl_page);
+ goto out;
+ }
+
+ ret = __sgx_encl_add_page(encl, encl_page, data, secinfo, mrmask);
+ if (ret) {
+ radix_tree_delete(&encl_page->encl->page_tree,
+ PFN_DOWN(encl_page->desc));
+ kfree(encl_page);
+ }
+
+out:
+ mutex_unlock(&encl->lock);
+ return ret;
+}
+
+/**
+ * sgx_ioc_enclave_add_page - handler for %SGX_IOC_ENCLAVE_ADD_PAGE
+ *
+ * @filep: open file to /dev/sgx
+ * @cmd: the command value
+ * @arg: pointer to an &sgx_enclave_add_page instance
+ *
+ * Add a page to an uninitialized enclave (EADD), and optionally extend the
+ * enclave's measurement with the contents of the page (EEXTEND). EADD and
+ * EEXTEND are done asynchronously via worker threads. A successful
+ * sgx_ioc_enclave_add_page() only indicates the page has been added to the
+ * work queue, it does not guarantee adding the page to the enclave will
+ * succeed.
+ *
+ * Return:
+ * 0 on success,
+ * -errno otherwise
+ */
+static long sgx_ioc_enclave_add_page(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_enclave_add_page *addp = (void *)arg;
+ struct sgx_encl *encl = filep->private_data;
+ struct sgx_secinfo secinfo;
+ struct page *data_page;
+ void *data;
+ int ret;
+
+ if (copy_from_user(&secinfo, (void __user *)addp->secinfo,
+ sizeof(secinfo)))
+ return -EFAULT;
+
+ data_page = alloc_page(GFP_HIGHUSER);
+ if (!data_page)
+ return -ENOMEM;
+
+ data = kmap(data_page);
+
+ if (copy_from_user((void *)data, (void __user *)addp->src, PAGE_SIZE)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = sgx_encl_add_page(encl, addp->addr, data, &secinfo, addp->mrmask);
+ if (ret)
+ goto out;
+
+out:
+ kunmap(data_page);
+ __free_page(data_page);
+ return ret;
+}
+
+static int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus,
+ void *hash)
+{
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tfm;
+ shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash);
+}
+
+static int sgx_get_key_hash(const void *modulus, void *hash)
+{
+ struct crypto_shash *tfm;
+ int ret;
+
+ tfm = crypto_alloc_shash("sha256", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ ret = __sgx_get_key_hash(tfm, modulus, hash);
+
+ crypto_free_shash(tfm);
+ return ret;
+}
+
+static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
+ struct sgx_einittoken *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 -EINVAL;
+
+ ret = sgx_get_key_hash(sigstruct->modulus, mrsigner);
+ if (ret)
+ return ret;
+
+ flush_work(&encl->work);
+
+ mutex_lock(&encl->lock);
+
+ if (!(encl->flags & SGX_ENCL_CREATED) ||
+ (encl->flags & (SGX_ENCL_INITIALIZED | SGX_ENCL_DEAD))) {
+ ret = -EFAULT;
+ goto err_out;
+ }
+
+ for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) {
+ for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) {
+ 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 (encls_faulted(ret)) {
+ if (encls_failed(ret))
+ ENCLS_WARN(ret, "EINIT");
+
+ sgx_encl_destroy(encl);
+ ret = -EFAULT;
+ } else if (encls_returned_code(ret)) {
+ pr_debug("EINIT returned %d\n", ret);
+ } else {
+ encl->flags |= SGX_ENCL_INITIALIZED;
+ }
+
+err_out:
+ mutex_unlock(&encl->lock);
+ return ret;
+}
+
+/**
+ * sgx_ioc_enclave_init - handler for %SGX_IOC_ENCLAVE_INIT
+ *
+ * @filep: open file to /dev/sgx
+ * @cmd: the command value
+ * @arg: pointer to an &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 file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_enclave_init *initp = (struct sgx_enclave_init *)arg;
+ struct sgx_encl *encl = filep->private_data;
+ struct sgx_einittoken *einittoken;
+ struct sgx_sigstruct *sigstruct;
+ struct page *initp_page;
+ int ret;
+
+ initp_page = alloc_page(GFP_HIGHUSER);
+ if (!initp_page)
+ return -ENOMEM;
+
+ sigstruct = kmap(initp_page);
+ einittoken = (struct sgx_einittoken *)
+ ((unsigned long)sigstruct + PAGE_SIZE / 2);
+ memset(einittoken, 0, sizeof(*einittoken));
+
+ if (copy_from_user(sigstruct, (void __user *)initp->sigstruct,
+ sizeof(*sigstruct))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+
+ ret = sgx_encl_init(encl, sigstruct, einittoken);
+
+out:
+ kunmap(initp_page);
+ __free_page(initp_page);
+ return ret;
+}
+
+typedef long (*sgx_ioc_t)(struct file *filep, unsigned int cmd,
+ unsigned long arg);
+
+long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ char data[256];
+ sgx_ioc_t handler = NULL;
+ long ret;
+
+ switch (cmd) {
+ case SGX_IOC_ENCLAVE_CREATE:
+ handler = sgx_ioc_enclave_create;
+ break;
+ case SGX_IOC_ENCLAVE_ADD_PAGE:
+ handler = sgx_ioc_enclave_add_page;
+ break;
+ case SGX_IOC_ENCLAVE_INIT:
+ handler = sgx_ioc_enclave_init;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ if (copy_from_user(data, (void __user *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ ret = handler(filep, cmd, (unsigned long)((void *)data));
+ if (!ret && (cmd & IOC_OUT)) {
+ if (copy_to_user((void __user *)arg, data, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ }
+
+ return ret;
+}
diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c
new file mode 100644
index 000000000000..d371add399cd
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/main.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <linux/acpi.h>
+#include <linux/cdev.h>
+#include <linux/mman.h>
+#include <linux/platform_device.h>
+#include <linux/security.h>
+#include <linux/suspend.h>
+#include <asm/traps.h>
+#include "driver.h"
+
+MODULE_DESCRIPTION("Intel SGX Enclave Driver");
+MODULE_AUTHOR("Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct workqueue_struct *sgx_encl_wq;
+u64 sgx_encl_size_max_32;
+u64 sgx_encl_size_max_64;
+u32 sgx_misc_reserved_mask;
+u64 sgx_attributes_reserved_mask;
+u64 sgx_xfrm_reserved_mask = ~0x3;
+u32 sgx_xsave_size_tbl[64];
+
+static int sgx_open(struct inode *inode, struct file *file)
+{
+ struct sgx_encl *encl;
+
+ encl = kzalloc(sizeof(*encl), GFP_KERNEL);
+ if (!encl)
+ return -ENOMEM;
+
+ kref_init(&encl->refcount);
+ INIT_LIST_HEAD(&encl->add_page_reqs);
+ INIT_RADIX_TREE(&encl->page_tree, GFP_KERNEL);
+ mutex_init(&encl->lock);
+ INIT_LIST_HEAD(&encl->mm_list);
+ spin_lock_init(&encl->mm_lock);
+
+ file->private_data = encl;
+
+ return 0;
+}
+
+static int sgx_release(struct inode *inode, struct file *file)
+{
+ struct sgx_encl *encl = file->private_data;
+
+ kref_put(&encl->refcount, sgx_encl_release);
+
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static long sgx_compat_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ return sgx_ioctl(filep, cmd, arg);
+}
+#endif
+
+static int sgx_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct sgx_encl *encl = file->private_data;
+
+ vma->vm_ops = &sgx_vm_ops;
+ vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
+ vma->vm_private_data = encl;
+
+ kref_get(&encl->refcount);
+
+ return 0;
+}
+
+static unsigned long sgx_get_unmapped_area(struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ if (len < 2 * PAGE_SIZE || len & (len - 1) || flags & MAP_PRIVATE)
+ return -EINVAL;
+
+ addr = current->mm->get_unmapped_area(file, addr, 2 * len, pgoff,
+ flags);
+ if (IS_ERR_VALUE(addr))
+ return addr;
+
+ addr = (addr + (len - 1)) & ~(len - 1);
+
+ return addr;
+}
+
+static const struct file_operations sgx_encl_fops = {
+ .owner = THIS_MODULE,
+ .open = sgx_open,
+ .release = sgx_release,
+ .unlocked_ioctl = sgx_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = sgx_compat_ioctl,
+#endif
+ .mmap = sgx_mmap,
+ .get_unmapped_area = sgx_get_unmapped_area,
+};
+
+static const struct file_operations sgx_provision_fops = {
+ .owner = THIS_MODULE,
+};
+
+static struct bus_type sgx_bus_type = {
+ .name = "sgx",
+};
+
+struct sgx_dev_ctx {
+ struct device encl_dev;
+ struct cdev encl_cdev;
+ struct device provision_dev;
+ struct cdev provision_cdev;
+ struct kref refcount;
+};
+
+static dev_t sgx_devt;
+
+static void sgx_dev_ctx_free(struct kref *ref)
+{
+ struct sgx_dev_ctx *ctx = container_of(ref, struct sgx_dev_ctx,
+ refcount);
+
+ kfree(ctx);
+}
+
+static void sgx_dev_release(struct device *dev)
+{
+ struct sgx_dev_ctx *ctx = container_of(dev, struct sgx_dev_ctx,
+ encl_dev);
+
+ kref_put(&ctx->refcount, sgx_dev_ctx_free);
+}
+
+static int sgx_dev_populate(const char *name, struct device *dev,
+ struct cdev *cdev, struct device *parent,
+ const struct file_operations *fops,
+ int minor)
+{
+ int ret;
+
+ device_initialize(dev);
+
+ dev->bus = &sgx_bus_type;
+ dev->parent = parent;
+ dev->devt = MKDEV(MAJOR(sgx_devt), minor);
+ dev->release = sgx_dev_release;
+
+ ret = dev_set_name(dev, name);
+ if (ret) {
+ put_device(dev);
+ return ret;
+ }
+
+ cdev_init(cdev, fops);
+ cdev->owner = THIS_MODULE;
+ return 0;
+}
+
+static struct sgx_dev_ctx *sgx_dev_ctx_alloc(struct device *parent)
+{
+ struct sgx_dev_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ kref_init(&ctx->refcount);
+ kref_get(&ctx->refcount);
+
+ ret = sgx_dev_populate("sgx/enclave", &ctx->encl_dev, &ctx->encl_cdev,
+ parent, &sgx_encl_fops, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = sgx_dev_populate("sgx/provision", &ctx->provision_dev,
+ &ctx->provision_cdev, parent,
+ &sgx_provision_fops, 1);
+ if (ret) {
+ put_device(&ctx->encl_dev);
+ return ERR_PTR(ret);
+ }
+
+ dev_set_drvdata(parent, ctx);
+
+ return ctx;
+}
+
+static struct sgx_dev_ctx *sgxm_dev_ctx_alloc(struct device *parent)
+{
+ struct sgx_dev_ctx *ctx;
+ int rc;
+
+ ctx = sgx_dev_ctx_alloc(parent);
+ if (IS_ERR(ctx))
+ return ctx;
+
+ rc = devm_add_action_or_reset(parent, (void (*)(void *))put_device,
+ &ctx->encl_dev);
+ if (rc)
+ return ERR_PTR(rc);
+
+ rc = devm_add_action_or_reset(parent, (void (*)(void *))put_device,
+ &ctx->provision_dev);
+ if (rc)
+ return ERR_PTR(rc);
+
+ return ctx;
+}
+
+static int sgx_dev_init(struct device *parent)
+{
+ struct sgx_dev_ctx *sgx_dev;
+ unsigned int eax;
+ unsigned int ebx;
+ unsigned int ecx;
+ unsigned int edx;
+ u64 attr_mask;
+ u64 xfrm_mask;
+ int ret;
+ int i;
+
+ cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx);
+ sgx_misc_reserved_mask = ~ebx | SGX_MISC_RESERVED_MASK;
+ sgx_encl_size_max_64 = 1ULL << ((edx >> 8) & 0xFF);
+ sgx_encl_size_max_32 = 1ULL << (edx & 0xFF);
+
+ cpuid_count(SGX_CPUID, 1, &eax, &ebx, &ecx, &edx);
+
+ attr_mask = (((u64)ebx) << 32) + (u64)eax;
+ sgx_attributes_reserved_mask = ~attr_mask | SGX_ATTR_RESERVED_MASK;
+
+ if (boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+ xfrm_mask = (((u64)edx) << 32) + (u64)ecx;
+
+ for (i = 2; i < 64; i++) {
+ cpuid_count(0x0D, i, &eax, &ebx, &ecx, &edx);
+ if ((1 << i) & xfrm_mask)
+ sgx_xsave_size_tbl[i] = eax + ebx;
+ }
+
+ sgx_xfrm_reserved_mask = ~xfrm_mask;
+ }
+
+ sgx_dev = sgxm_dev_ctx_alloc(parent);
+ if (IS_ERR(sgx_dev))
+ return PTR_ERR(sgx_dev);
+
+ sgx_encl_wq = alloc_workqueue("sgx-encl-wq",
+ WQ_UNBOUND | WQ_FREEZABLE, 1);
+ if (!sgx_encl_wq)
+ return -ENOMEM;
+
+ ret = cdev_device_add(&sgx_dev->encl_cdev, &sgx_dev->encl_dev);
+ if (ret)
+ goto err_encl_dev_add;
+
+ return 0;
+
+err_encl_dev_add:
+ destroy_workqueue(sgx_encl_wq);
+
+ return ret;
+}
+
+static int sgx_drv_probe(struct platform_device *pdev)
+{
+ if (!sgx_enabled) {
+ pr_info("sgx: SGX is not enabled in the core\n");
+ return -ENODEV;
+ }
+
+ if (!boot_cpu_has(X86_FEATURE_SGX_LC)) {
+ pr_info("sgx: The public key MSRs are not writable\n");
+ return -ENODEV;
+ }
+
+ return sgx_dev_init(&pdev->dev);
+}
+
+static int sgx_drv_remove(struct platform_device *pdev)
+{
+ struct sgx_dev_ctx *ctx = dev_get_drvdata(&pdev->dev);
+
+ cdev_device_del(&ctx->encl_cdev, &ctx->encl_dev);
+ destroy_workqueue(sgx_encl_wq);
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id sgx_device_ids[] = {
+ {"INT0E0C", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, sgx_device_ids);
+#endif
+
+static struct platform_driver sgx_drv = {
+ .probe = sgx_drv_probe,
+ .remove = sgx_drv_remove,
+ .driver = {
+ .name = "sgx",
+ .acpi_match_table = ACPI_PTR(sgx_device_ids),
+ },
+};
+
+static int __init sgx_drv_subsys_init(void)
+{
+ int ret;
+
+ ret = bus_register(&sgx_bus_type);
+ if (ret)
+ return ret;
+
+ ret = alloc_chrdev_region(&sgx_devt, 0, SGX_DRV_NR_DEVICES, "sgx");
+ if (ret < 0) {
+ bus_unregister(&sgx_bus_type);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void sgx_drv_subsys_exit(void)
+{
+ bus_unregister(&sgx_bus_type);
+ unregister_chrdev_region(sgx_devt, SGX_DRV_NR_DEVICES);
+}
+
+static int __init sgx_drv_init(void)
+{
+ int ret;
+
+ ret = sgx_drv_subsys_init();
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&sgx_drv);
+ if (ret)
+ sgx_drv_subsys_exit();
+
+ return ret;
+}
+module_init(sgx_drv_init);
+
+static void __exit sgx_drv_exit(void)
+{
+ platform_driver_unregister(&sgx_drv);
+ sgx_drv_subsys_exit();
+}
+module_exit(sgx_drv_exit);
diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
new file mode 100644
index 000000000000..cc3bd4a4d82b
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/encl.c
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <linux/mm.h>
+#include <linux/shmem_fs.h>
+#include <linux/suspend.h>
+#include <linux/sched/mm.h>
+#include "arch.h"
+#include "encl.h"
+#include "sgx.h"
+
+static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
+ unsigned long addr)
+{
+ struct sgx_encl_page *entry;
+
+ /* If process was forked, VMA is still there but vm_private_data is set
+ * to NULL.
+ */
+ if (!encl)
+ return ERR_PTR(-EFAULT);
+
+ if ((encl->flags & SGX_ENCL_DEAD) ||
+ !(encl->flags & SGX_ENCL_INITIALIZED))
+ return ERR_PTR(-EFAULT);
+
+ entry = radix_tree_lookup(&encl->page_tree, addr >> PAGE_SHIFT);
+ if (!entry)
+ return ERR_PTR(-EFAULT);
+
+ /* Page is already resident in the EPC. */
+ if (entry->epc_page)
+ return entry;
+
+ return ERR_PTR(-EFAULT);
+}
+
+struct sgx_encl_mm *sgx_encl_mm_add(struct sgx_encl *encl,
+ struct mm_struct *mm)
+{
+ struct sgx_encl_mm *encl_mm;
+
+ encl_mm = kzalloc(sizeof(*encl_mm), GFP_KERNEL);
+ if (!encl_mm)
+ return ERR_PTR(-ENOMEM);
+
+ encl_mm->encl = encl;
+ encl_mm->mm = mm;
+ kref_init(&encl_mm->refcount);
+
+ spin_lock(&encl->mm_lock);
+ list_add(&encl_mm->list, &encl->mm_list);
+ spin_unlock(&encl->mm_lock);
+
+ return encl_mm;
+}
+EXPORT_SYMBOL_GPL(sgx_encl_mm_add);
+
+void sgx_encl_mm_release(struct kref *ref)
+{
+ struct sgx_encl_mm *encl_mm =
+ container_of(ref, struct sgx_encl_mm, refcount);
+
+ spin_lock(&encl_mm->encl->mm_lock);
+ list_del(&encl_mm->list);
+ spin_unlock(&encl_mm->encl->mm_lock);
+
+ kfree(encl_mm);
+}
+
+static struct sgx_encl_mm *sgx_encl_get_mm(struct sgx_encl *encl,
+ struct mm_struct *mm)
+{
+ struct sgx_encl_mm *encl_mm = NULL;
+ struct sgx_encl_mm *prev_mm = NULL;
+ int iter;
+
+ while (true) {
+ encl_mm = sgx_encl_next_mm(encl, prev_mm, &iter);
+ if (prev_mm)
+ kref_put(&prev_mm->refcount, sgx_encl_mm_release);
+ prev_mm = encl_mm;
+
+ if (iter == SGX_ENCL_MM_ITER_DONE)
+ break;
+
+ if (iter == SGX_ENCL_MM_ITER_RESTART)
+ continue;
+
+ if (mm == encl_mm->mm)
+ return encl_mm;
+ }
+
+ return NULL;
+}
+
+static void sgx_vma_open(struct vm_area_struct *vma)
+{
+ struct sgx_encl *encl = vma->vm_private_data;
+ struct sgx_encl_mm *encl_mm;
+
+ if (!encl)
+ return;
+
+ if (encl->flags & SGX_ENCL_DEAD)
+ goto error;
+
+ encl_mm = sgx_encl_get_mm(encl, vma->vm_mm);
+ if (!encl_mm) {
+ encl_mm = sgx_encl_mm_add(encl, vma->vm_mm);
+ if (IS_ERR(encl_mm))
+ goto error;
+ }
+
+ kref_get(&encl->refcount);
+ return;
+
+error:
+ vma->vm_private_data = NULL;
+}
+
+static void sgx_vma_close(struct vm_area_struct *vma)
+{
+ struct sgx_encl *encl = vma->vm_private_data;
+ struct sgx_encl_mm *encl_mm;
+
+ if (!encl)
+ return;
+
+ encl_mm = sgx_encl_get_mm(encl, vma->vm_mm);
+ if (encl_mm) {
+ kref_put(&encl_mm->refcount, sgx_encl_mm_release);
+
+ /* Release kref for the VMA. */
+ kref_put(&encl_mm->refcount, sgx_encl_mm_release);
+ }
+
+ kref_put(&encl->refcount, sgx_encl_release);
+}
+
+static unsigned int sgx_vma_fault(struct vm_fault *vmf)
+{
+ unsigned long addr = (unsigned long)vmf->address;
+ struct vm_area_struct *vma = vmf->vma;
+ struct sgx_encl *encl = vma->vm_private_data;
+ struct sgx_encl_page *entry;
+ int ret = VM_FAULT_NOPAGE;
+ unsigned long pfn;
+
+ if (!encl)
+ return VM_FAULT_SIGBUS;
+
+ mutex_lock(&encl->lock);
+
+ entry = sgx_encl_load_page(encl, addr);
+ if (IS_ERR(entry)) {
+ if (unlikely(PTR_ERR(entry) != -EBUSY))
+ ret = VM_FAULT_SIGBUS;
+
+ goto out;
+ }
+
+ if (!follow_pfn(vma, addr, &pfn))
+ goto out;
+
+ ret = vmf_insert_pfn(vma, addr, PFN_DOWN(entry->epc_page->desc));
+ if (ret != VM_FAULT_NOPAGE) {
+ ret = VM_FAULT_SIGBUS;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&encl->lock);
+ return ret;
+}
+
+const struct vm_operations_struct sgx_vm_ops = {
+ .close = sgx_vma_close,
+ .open = sgx_vma_open,
+ .fault = sgx_vma_fault,
+};
+EXPORT_SYMBOL_GPL(sgx_vm_ops);
+
+/**
+ * sgx_encl_find - find an enclave
+ * @mm: mm struct of the current process
+ * @addr: address in the ELRANGE
+ * @vma: the resulting VMA
+ *
+ * Find an enclave identified by the given address. Give back a VMA that is
+ * part of the enclave and located in that address. The VMA is given back if it
+ * is a proper enclave VMA even if an &sgx_encl instance does not exist yet
+ * (enclave creation has not been performed).
+ *
+ * Return:
+ * 0 on success,
+ * -EINVAL if an enclave was not found,
+ * -ENOENT if the enclave has not been created yet
+ */
+int sgx_encl_find(struct mm_struct *mm, unsigned long addr,
+ struct vm_area_struct **vma)
+{
+ struct vm_area_struct *result;
+ struct sgx_encl *encl;
+
+ result = find_vma(mm, addr);
+ if (!result || result->vm_ops != &sgx_vm_ops || addr < result->vm_start)
+ return -EINVAL;
+
+ encl = result->vm_private_data;
+ *vma = result;
+
+ return encl ? 0 : -ENOENT;
+}
+EXPORT_SYMBOL_GPL(sgx_encl_find);
+
+/**
+ * sgx_encl_destroy() - destroy enclave resources
+ * @encl: an &sgx_encl instance
+ */
+void sgx_encl_destroy(struct sgx_encl *encl)
+{
+ struct sgx_encl_page *entry;
+ struct radix_tree_iter iter;
+ void **slot;
+
+ encl->flags |= SGX_ENCL_DEAD;
+
+ radix_tree_for_each_slot(slot, &encl->page_tree, &iter, 0) {
+ entry = *slot;
+ if (entry->epc_page) {
+ if (!__sgx_free_page(entry->epc_page)) {
+ encl->secs_child_cnt--;
+ entry->epc_page = NULL;
+
+ }
+
+ radix_tree_delete(&entry->encl->page_tree,
+ PFN_DOWN(entry->desc));
+ }
+ }
+
+ if (!encl->secs_child_cnt && encl->secs.epc_page) {
+ sgx_free_page(encl->secs.epc_page);
+ encl->secs.epc_page = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(sgx_encl_destroy);
+
+/**
+ * sgx_encl_release - Destroy an enclave instance
+ * @kref: address of a kref inside &sgx_encl
+ *
+ * Used together with kref_put(). Frees all the resources associated with the
+ * enclave and the instance itself.
+ */
+void sgx_encl_release(struct kref *ref)
+{
+ struct sgx_encl *encl = container_of(ref, struct sgx_encl, refcount);
+ struct sgx_encl_mm *encl_mm;
+
+ if (encl->pm_notifier.notifier_call)
+ unregister_pm_notifier(&encl->pm_notifier);
+
+ sgx_encl_destroy(encl);
+
+ if (encl->backing)
+ fput(encl->backing);
+
+ WARN(!list_empty(&encl->mm_list), "sgx: mm_list non-empty");
+
+ kfree(encl);
+}
+EXPORT_SYMBOL_GPL(sgx_encl_release);
+
+/**
+ * sgx_encl_get_index() - Convert a page descriptor to a page index
+ * @encl: an enclave
+ * @page: an enclave page
+ *
+ * Given an enclave page descriptor, convert it to a page index used to access
+ * backing storage. The backing page for SECS is located after the enclave
+ * pages.
+ */
+pgoff_t sgx_encl_get_index(struct sgx_encl *encl, struct sgx_encl_page *page)
+{
+ if (!PFN_DOWN(page->desc))
+ return PFN_DOWN(encl->size);
+
+ return PFN_DOWN(page->desc - encl->base);
+}
+EXPORT_SYMBOL_GPL(sgx_encl_get_index);
+
+/**
+ * sgx_encl_encl_get_backing_page() - Pin the backing page
+ * @encl: an enclave
+ * @index: page index
+ *
+ * Return: the pinned backing page
+ */
+struct page *sgx_encl_get_backing_page(struct sgx_encl *encl, pgoff_t index)
+{
+ struct inode *inode = encl->backing->f_path.dentry->d_inode;
+ struct address_space *mapping = inode->i_mapping;
+ gfp_t gfpmask = mapping_gfp_mask(mapping);
+
+ return shmem_read_mapping_page_gfp(mapping, index, gfpmask);
+}
+EXPORT_SYMBOL_GPL(sgx_encl_get_backing_page);
+
+/**
+ * sgx_encl_next_mm() - Iterate to the next mm
+ * @encl: an enclave
+ * @mm: an mm list entry
+ * @iter: iterator status
+ *
+ * Return: the enclave mm or NULL
+ */
+struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl,
+ struct sgx_encl_mm *encl_mm, int *iter)
+{
+ struct list_head *entry;
+
+ WARN(!encl, "%s: encl is NULL", __func__);
+ WARN(!iter, "%s: iter is NULL", __func__);
+
+ spin_lock(&encl->mm_lock);
+
+ entry = encl_mm ? encl_mm->list.next : encl->mm_list.next;
+ WARN(!entry, "%s: entry is NULL", __func__);
+
+ if (entry == &encl->mm_list) {
+ spin_unlock(&encl->mm_lock);
+ *iter = SGX_ENCL_MM_ITER_DONE;
+ return NULL;
+ }
+
+ encl_mm = list_entry(entry, struct sgx_encl_mm, list);
+
+ if (!kref_get_unless_zero(&encl_mm->refcount)) {
+ spin_unlock(&encl->mm_lock);
+ *iter = SGX_ENCL_MM_ITER_RESTART;
+ return NULL;
+ }
+
+ spin_unlock(&encl->mm_lock);
+ *iter = SGX_ENCL_MM_ITER_NEXT;
+ return encl_mm;
+}
diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h
new file mode 100644
index 000000000000..1f96991839ad
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/encl.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-19 Intel Corporation.
+ */
+#ifndef _X86_ENCL_H
+#define _X86_ENCL_H
+
+#include <linux/cpumask.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/mm_types.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/radix-tree.h>
+#include <linux/workqueue.h>
+
+/**
+ * enum sgx_encl_page_desc - defines bits for an enclave page's descriptor
+ * %SGX_ENCL_PAGE_TCS: The page is a TCS page.
+ * %SGX_ENCL_PAGE_ADDR_MASK: Holds the virtual address of the page.
+ *
+ * The page address for SECS is zero and is used by the subsystem to recognize
+ * the SECS page.
+ */
+enum sgx_encl_page_desc {
+ SGX_ENCL_PAGE_TCS = BIT(0),
+ /* Bits 11:3 are available when the page is not swapped. */
+ SGX_ENCL_PAGE_ADDR_MASK = PAGE_MASK,
+};
+
+#define SGX_ENCL_PAGE_ADDR(encl_page) \
+ ((encl_page)->desc & SGX_ENCL_PAGE_ADDR_MASK)
+#define SGX_ENCL_PAGE_VA_OFFSET(encl_page) \
+ ((encl_page)->desc & SGX_ENCL_PAGE_VA_OFFSET_MASK)
+
+struct sgx_encl_page {
+ unsigned long desc;
+ struct sgx_epc_page *epc_page;
+ struct sgx_encl *encl;
+};
+
+enum sgx_encl_flags {
+ SGX_ENCL_CREATED = BIT(0),
+ SGX_ENCL_INITIALIZED = BIT(1),
+ SGX_ENCL_DEBUG = BIT(2),
+ SGX_ENCL_SUSPEND = BIT(3),
+ SGX_ENCL_DEAD = BIT(4),
+};
+
+struct sgx_encl_mm {
+ struct sgx_encl *encl;
+ struct mm_struct *mm;
+ struct kref refcount;
+ struct list_head list;
+};
+
+struct sgx_encl {
+ unsigned int flags;
+ u64 secs_attributes;
+ u64 allowed_attributes;
+ unsigned int page_cnt;
+ unsigned int secs_child_cnt;
+ struct mutex lock;
+ struct list_head mm_list;
+ spinlock_t mm_lock;
+ struct file *backing;
+ struct kref refcount;
+ unsigned long base;
+ unsigned long size;
+ unsigned long ssaframesize;
+ struct radix_tree_root page_tree;
+ struct list_head add_page_reqs;
+ struct work_struct work;
+ struct sgx_encl_page secs;
+ struct notifier_block pm_notifier;
+};
+
+extern const struct vm_operations_struct sgx_vm_ops;
+
+enum sgx_encl_mm_iter {
+ SGX_ENCL_MM_ITER_DONE = 0,
+ SGX_ENCL_MM_ITER_NEXT = 1,
+ SGX_ENCL_MM_ITER_RESTART = 2,
+};
+
+int sgx_encl_find(struct mm_struct *mm, unsigned long addr,
+ struct vm_area_struct **vma);
+void sgx_encl_destroy(struct sgx_encl *encl);
+void sgx_encl_release(struct kref *ref);
+pgoff_t sgx_encl_get_index(struct sgx_encl *encl, struct sgx_encl_page *page);
+struct page *sgx_encl_get_backing_page(struct sgx_encl *encl, pgoff_t index);
+struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl,
+ struct sgx_encl_mm *encl_mm, int *iter);
+struct sgx_encl_mm *sgx_encl_mm_add(struct sgx_encl *encl,
+ struct mm_struct *mm);
+void sgx_encl_mm_release(struct kref *ref);
+
+#endif /* _X86_ENCL_H */
diff --git a/arch/x86/kernel/cpu/sgx/encls.c b/arch/x86/kernel/cpu/sgx/encls.c
index 5045f1365e07..698cc526bfbf 100644
--- a/arch/x86/kernel/cpu/sgx/encls.c
+++ b/arch/x86/kernel/cpu/sgx/encls.c
@@ -19,3 +19,4 @@ bool encls_failed(int ret)
return encls_faulted(ret) && ENCLS_TRAPNR(ret) != epcm_trapnr;
}
+EXPORT_SYMBOL_GPL(encls_failed);
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index d3ed742e90fe..d911a1038712 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -14,6 +14,8 @@
struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
EXPORT_SYMBOL_GPL(sgx_epc_sections);
+bool sgx_enabled;
+EXPORT_SYMBOL_GPL(sgx_enabled);
int sgx_nr_epc_sections;
@@ -293,6 +295,7 @@ static __init int sgx_init(void)
return ret;
}
+ sgx_enabled = true;
return 0;
}
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index 41d4130c33a2..62a574ed230a 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -35,6 +35,7 @@ struct sgx_epc_section {
#define SGX_MAX_EPC_SECTIONS 8
extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
+extern bool sgx_enabled;
/**
* enum sgx_epc_page_desc - bits and masks for an EPC page's descriptor
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 16/28] x86/sgx: Add provisioning
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (14 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-19 3:06 ` Huang, Kai
2019-04-24 1:34 ` Jethro Beekman
2019-04-17 10:39 ` [PATCH v20 17/28] x86/sgx: Add swapping code to the core and SGX driver Jarkko Sakkinen
` (17 subsequent siblings)
33 siblings, 2 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen, James Morris, Serge E . Hallyn,
linux-security-module
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 6250 bytes --]
In order to provide a mechanism for devilering provisoning rights:
1. Add a new device file /dev/sgx/provision that works as a token for
allowing an enclave to have the provisioning privileges.
2. Add a new ioctl called SGX_IOC_ENCLAVE_SET_ATTRIBUTE that accepts the
following data structure:
struct sgx_enclave_set_attribute {
__u64 addr;
__u64 attribute_fd;
};
A daemon could sit on top of /dev/sgx/provision and send a file
descriptor of this file to a process that needs to be able to provision
enclaves.
The way this API is used is straight-forward. Lets assume that dev_fd is
a handle to /dev/sgx/enclave and prov_fd is a handle to
/dev/sgx/provision. You would allow SGX_IOC_ENCLAVE_CREATE to
initialize an enclave with the PROVISIONKEY attribute by
params.addr = <enclave address>;
params.token_fd = prov_fd;
ioctl(dev_fd, SGX_IOC_ENCLAVE_SET_ATTRIBUTE, ¶ms);
Cc: James Morris <jmorris@namei.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: linux-security-module@vger.kernel.org
Suggested-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/include/uapi/asm/sgx.h | 11 ++++++
arch/x86/kernel/cpu/sgx/driver/driver.h | 2 +-
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 51 +++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/driver/main.c | 11 +++++-
4 files changed, 73 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index 7bf627ac4958..3b80acde8671 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -16,6 +16,8 @@
_IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page)
#define SGX_IOC_ENCLAVE_INIT \
_IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
+#define SGX_IOC_ENCLAVE_SET_ATTRIBUTE \
+ _IOW(SGX_MAGIC, 0x03, struct sgx_enclave_set_attribute)
/* IOCTL return values */
#define SGX_POWER_LOST_ENCLAVE 0x40000000
@@ -54,4 +56,13 @@ struct sgx_enclave_init {
__u64 sigstruct;
};
+/**
+ * struct sgx_enclave_set_attribute - parameter structure for the
+ * %SGX_IOC_ENCLAVE_SET_ATTRIBUTE ioctl
+ * @attribute_fd: file handle of the attribute file in the securityfs
+ */
+struct sgx_enclave_set_attribute {
+ __u64 attribute_fd;
+};
+
#endif /* _UAPI_ASM_X86_SGX_H */
diff --git a/arch/x86/kernel/cpu/sgx/driver/driver.h b/arch/x86/kernel/cpu/sgx/driver/driver.h
index 507712eb0a68..153b4a48aa6f 100644
--- a/arch/x86/kernel/cpu/sgx/driver/driver.h
+++ b/arch/x86/kernel/cpu/sgx/driver/driver.h
@@ -31,7 +31,7 @@ extern u64 sgx_attributes_reserved_mask;
extern u64 sgx_xfrm_reserved_mask;
extern u32 sgx_xsave_size_tbl[64];
-extern const struct file_operations sgx_fs_provision_fops;
+extern const struct file_operations sgx_provision_fops;
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
index f88226518b21..65c9fb7b2a95 100644
--- a/arch/x86/kernel/cpu/sgx/driver/ioctl.c
+++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
@@ -714,6 +714,54 @@ static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd,
return ret;
}
+/**
+ * sgx_ioc_enclave_set_attribute - handler for %SGX_IOC_ENCLAVE_SET_ATTRIBUTE
+ * @filep: open file to /dev/sgx
+ * @cmd: the command value
+ * @arg: pointer to a struct sgx_enclave_set_attribute instance
+ *
+ * Mark the enclave as being allowed to access a restricted attribute bit.
+ * The requested attribute is specified via the attribute_fd field in the
+ * provided struct sgx_enclave_set_attribute. The attribute_fd must be a
+ * handle to an SGX attribute file, e.g. “/dev/sgx/provision".
+ *
+ * Failure to explicitly request access to a restricted attribute will cause
+ * sgx_ioc_enclave_init() to fail. Currently, the only restricted attribute
+ * is access to the PROVISION_KEY.
+ *
+ * Note, access to the EINITTOKEN_KEY is disallowed entirely.
+ *
+ * Return: 0 on success, -errno otherwise
+ */
+static long sgx_ioc_enclave_set_attribute(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_enclave_set_attribute *params = (void *)arg;
+ struct file *attribute_file;
+ struct sgx_encl *encl;
+ int ret;
+
+ attribute_file = fget(params->attribute_fd);
+ if (!attribute_file->f_op)
+ return -EINVAL;
+
+ if (attribute_file->f_op != &sgx_provision_fops) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = sgx_encl_get(params->addr, &encl);
+ if (ret)
+ goto out;
+
+ encl->allowed_attributes |= SGX_ATTR_PROVISIONKEY;
+ kref_put(&encl->refcount, sgx_encl_release);
+
+out:
+ fput(attribute_file);
+ return ret;
+}
+
typedef long (*sgx_ioc_t)(struct file *filep, unsigned int cmd,
unsigned long arg);
@@ -733,6 +781,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
case SGX_IOC_ENCLAVE_INIT:
handler = sgx_ioc_enclave_init;
break;
+ case SGX_IOC_ENCLAVE_SET_ATTRIBUTE:
+ handler = sgx_ioc_enclave_set_attribute;
+ break;
default:
return -ENOIOCTLCMD;
}
diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c
index d371add399cd..8b79c4a60037 100644
--- a/arch/x86/kernel/cpu/sgx/driver/main.c
+++ b/arch/x86/kernel/cpu/sgx/driver/main.c
@@ -103,7 +103,7 @@ static const struct file_operations sgx_encl_fops = {
.get_unmapped_area = sgx_get_unmapped_area,
};
-static const struct file_operations sgx_provision_fops = {
+const struct file_operations sgx_provision_fops = {
.owner = THIS_MODULE,
};
@@ -261,8 +261,16 @@ static int sgx_dev_init(struct device *parent)
if (ret)
goto err_encl_dev_add;
+ ret = cdev_device_add(&sgx_dev->provision_cdev,
+ &sgx_dev->provision_dev);
+ if (ret)
+ goto err_provision_dev_add;
+
return 0;
+err_provision_dev_add:
+ cdev_device_del(&sgx_dev->encl_cdev, &sgx_dev->encl_dev);
+
err_encl_dev_add:
destroy_workqueue(sgx_encl_wq);
@@ -289,6 +297,7 @@ static int sgx_drv_remove(struct platform_device *pdev)
struct sgx_dev_ctx *ctx = dev_get_drvdata(&pdev->dev);
cdev_device_del(&ctx->encl_cdev, &ctx->encl_dev);
+ cdev_device_del(&ctx->provision_cdev, &ctx->provision_dev);
destroy_workqueue(sgx_encl_wq);
return 0;
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 17/28] x86/sgx: Add swapping code to the core and SGX driver
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (15 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 16/28] x86/sgx: Add provisioning Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 18/28] x86/sgx: ptrace() support for the " Jarkko Sakkinen
` (16 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
Because the kernel is untrusted, swapping pages in/out of the Enclave
Page Cache (EPC) has specialized requirements:
* The kernel cannot directly access EPC memory, i.e. cannot copy data
to/from the EPC.
* To evict a page from the EPC, the kernel must "prove" to hardware that
are no valid TLB entries for said page since a stale TLB entry would
allow an attacker to bypass SGX access controls.
* When loading a page back into the EPC, hardware must be able to verify
the integrity and freshness of the data.
* When loading an enclave page, e.g. regular pages and Thread Control
Structures (TCS), hardware must be able to associate the page with a
Secure Enclave Control Structure (SECS).
To satisfy the above requirements, the CPU provides dedicated ENCLS
functions to support paging data in/out of the EPC:
* EBLOCK: Mark a page as blocked in the EPC Map (EPCM). Attempting
to access a blocked page that misses the TLB will fault.
* ETRACK: Activate blocking tracking. Hardware verifies that all
translations for pages marked as "blocked" have been flushed
from the TLB.
* EPA: Add version array page to the EPC. As the name suggests, a
VA page is an 512-entry array of version numbers that are
used to uniquely identify pages evicted from the EPC.
* EWB: Write back a page from EPC to memory, e.g. RAM. Software
must supply a VA slot, memory to hold the a Paging Crypto
Metadata (PCMD) of the page and obviously backing for the
evicted page.
* ELD{B,U}: Load a page in {un}blocked state from memory to EPC. The
driver only uses the ELDU variant as there is no use case
for loading a page as "blocked" in a bare metal environment.
To top things off, all of the above ENCLS functions are subject to
strict concurrency rules, e.g. many operations will #GP fault if two
or more operations attempt to access common pages/structures.
To put it succinctly, paging in/out of the EPC requires coordinating
with the SGX driver where all of an enclave's tracking resides. But,
simply shoving all reclaim logic into the driver is not desirable as
doing so has unwanted long term implications:
* Oversubscribing EPC to KVM guests, i.e. virtualizing SGX in KVM and
swapping a guest's EPC pages (without the guest's cooperation) needs
the same high level flows for reclaim but has painfully different
semantics in the details.
* Accounting EPC, i.e. adding an EPC cgroup controller, is desirable
as EPC is effectively a specialized memory type and even more scarce
than system memory. Providing a single touchpoint for EPC accounting
regardless of end consumer greatly simplifies the EPC controller.
* Allowing the userspace-facing driver to be built as a loaded module
is desirable, e.g. for debug, testing and development. The cgroup
infrastructure does not support dependencies on loadable modules.
* Separating EPC swapping from the driver once it has been tightly
coupled to the driver is non-trivial (speaking from experience).
So, although the SGX driver is currently the sole consumer of EPC,
encapsulate EPC swapping in the driver to minimize the dependencies
between the core SGX code and driver, and do so in a way that can be
extended to an abstracted interface with minimal effort.
To that end, add functions to swap EPC pages to the driver. The user
of these functions will be the core SGX subsystem, which will be enabled
in a future patch.
* sgx_encl_page_{get,put}() - Attempt to pin/unpin (the owner of) an EPC
page so that it can be operated on by a reclaimer.
* sgx_encl_page_reclaim() - Mark a page as being reclaimed. The
page is considered reclaimable if it hasn't been accessed recently and
it isn't reserved by the driver for other use.
* sgx_encl_page_block() - EBLOCK an EPC page
* sgx_encl_page_write() - Evict an EPC page to the regular memory via
EWB. Activates ETRACK (via sgx_encl_track()) if necessary.
Since we also need to be able to fault pages back into the EPC, add a
page fault handler to allocate an EPC page and ELDU a previously evicted
page.
Wire up the EPC manager's reclaim flow to the SGX driver's swapping
functionality. In the long term there will be multiple users of the
EPC manager, e.g. SGX driver and KVM, thus the interface between the
EPC manager and the driver is fairly genericized and decoupled. But
to avoid adding unusued infrastructure, do not add any indirection
between the EPC manager and the SGX driver. This has the unfortunate
and odd side effect of preventing the SGX driver from being compiled
as a loadable module. However, this should be a temporary situation
that is remedied when a second user of EPC is added, i.e. KVM.
The swapper thread ksgxswapd reclaims pages on the event when the number
of free EPC pages goes below %SGX_NR_LOW_PAGES up until it reaches
%SGX_NR_HIGH_PAGES.
Pages are reclaimed in LRU fashion from a global list. The consumers
take care of calling EBLOCK (block page from new accesses), ETRACK
(restart counting the entering hardware threads) and EWB (write page to
the regular memory) because executing these operations usually (if not
always) requires to do some subsystem-internal locking operations.
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
Co-developed-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
Signed-off-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
---
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 65 +++-
arch/x86/kernel/cpu/sgx/driver/main.c | 1 +
arch/x86/kernel/cpu/sgx/encl.c | 267 ++++++++++++++++-
arch/x86/kernel/cpu/sgx/encl.h | 38 +++
arch/x86/kernel/cpu/sgx/main.c | 92 ++++--
arch/x86/kernel/cpu/sgx/reclaim.c | 400 ++++++++++++++++++++++++-
arch/x86/kernel/cpu/sgx/sgx.h | 18 +-
7 files changed, 852 insertions(+), 29 deletions(-)
diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
index 65c9fb7b2a95..3a01c3dd579d 100644
--- a/arch/x86/kernel/cpu/sgx/driver/ioctl.c
+++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
@@ -21,6 +21,51 @@ struct sgx_add_page_req {
struct list_head list;
};
+static int sgx_encl_grow(struct sgx_encl *encl)
+{
+ struct sgx_va_page *va_page;
+ int ret;
+
+ BUILD_BUG_ON(SGX_VA_SLOT_COUNT !=
+ (SGX_ENCL_PAGE_VA_OFFSET_MASK >> 3) + 1);
+
+ mutex_lock(&encl->lock);
+ if (encl->flags & SGX_ENCL_DEAD) {
+ mutex_unlock(&encl->lock);
+ return -EFAULT;
+ }
+
+ if (!(encl->page_cnt % SGX_VA_SLOT_COUNT)) {
+ mutex_unlock(&encl->lock);
+
+ va_page = kzalloc(sizeof(*va_page), GFP_KERNEL);
+ if (!va_page)
+ return -ENOMEM;
+ va_page->epc_page = sgx_alloc_va_page();
+ if (IS_ERR(va_page->epc_page)) {
+ ret = PTR_ERR(va_page->epc_page);
+ kfree(va_page);
+ return ret;
+ }
+
+ mutex_lock(&encl->lock);
+ if (encl->flags & SGX_ENCL_DEAD) {
+ sgx_free_page(va_page->epc_page);
+ kfree(va_page);
+ mutex_unlock(&encl->lock);
+ return -EFAULT;
+ } else if (encl->page_cnt % SGX_VA_SLOT_COUNT) {
+ sgx_free_page(va_page->epc_page);
+ kfree(va_page);
+ } else {
+ list_add(&va_page->list, &encl->va_pages);
+ }
+ }
+ encl->page_cnt++;
+ mutex_unlock(&encl->lock);
+ return 0;
+}
+
static bool sgx_process_add_page_req(struct sgx_add_page_req *req,
struct sgx_epc_page *epc_page)
{
@@ -79,6 +124,7 @@ static bool sgx_process_add_page_req(struct sgx_add_page_req *req,
encl_page->encl = encl;
encl_page->epc_page = epc_page;
encl->secs_child_cnt++;
+ sgx_mark_page_reclaimable(encl_page->epc_page);
return true;
}
@@ -109,7 +155,7 @@ static void sgx_add_page_worker(struct work_struct *work)
if (skip_rest)
goto next;
- epc_page = sgx_alloc_page();
+ epc_page = sgx_alloc_page(req->encl_page, true);
mutex_lock(&encl->lock);
@@ -237,6 +283,10 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
struct file *backing;
long ret;
+ ret = sgx_encl_grow(encl);
+ if (ret)
+ return ret;
+
mutex_lock(&encl->lock);
if (encl->flags & SGX_ENCL_CREATED) {
@@ -267,7 +317,7 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
goto err_out;
}
- secs_epc = sgx_alloc_page();
+ secs_epc = sgx_alloc_page(&encl->secs, true);
if (IS_ERR(secs_epc)) {
ret = PTR_ERR(secs_epc);
goto err_out;
@@ -495,6 +545,10 @@ static int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr,
return ret;
}
+ ret = sgx_encl_grow(encl);
+ if (ret)
+ return ret;
+
mutex_lock(&encl->lock);
if (!(encl->flags & SGX_ENCL_CREATED) ||
@@ -737,8 +791,8 @@ static long sgx_ioc_enclave_set_attribute(struct file *filep, unsigned int cmd,
unsigned long arg)
{
struct sgx_enclave_set_attribute *params = (void *)arg;
+ struct sgx_encl *encl = filep->private_data;
struct file *attribute_file;
- struct sgx_encl *encl;
int ret;
attribute_file = fget(params->attribute_fd);
@@ -750,12 +804,7 @@ static long sgx_ioc_enclave_set_attribute(struct file *filep, unsigned int cmd,
goto out;
}
- ret = sgx_encl_get(params->addr, &encl);
- if (ret)
- goto out;
-
encl->allowed_attributes |= SGX_ATTR_PROVISIONKEY;
- kref_put(&encl->refcount, sgx_encl_release);
out:
fput(attribute_file);
diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c
index 8b79c4a60037..afe844aa81d6 100644
--- a/arch/x86/kernel/cpu/sgx/driver/main.c
+++ b/arch/x86/kernel/cpu/sgx/driver/main.c
@@ -32,6 +32,7 @@ static int sgx_open(struct inode *inode, struct file *file)
kref_init(&encl->refcount);
INIT_LIST_HEAD(&encl->add_page_reqs);
+ INIT_LIST_HEAD(&encl->va_pages);
INIT_RADIX_TREE(&encl->page_tree, GFP_KERNEL);
mutex_init(&encl->lock);
INIT_LIST_HEAD(&encl->mm_list);
diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
index cc3bd4a4d82b..16e8524687c1 100644
--- a/arch/x86/kernel/cpu/sgx/encl.c
+++ b/arch/x86/kernel/cpu/sgx/encl.c
@@ -7,11 +7,91 @@
#include <linux/sched/mm.h>
#include "arch.h"
#include "encl.h"
+#include "encls.h"
#include "sgx.h"
+static int __sgx_encl_eldu(struct sgx_encl_page *encl_page,
+ struct sgx_epc_page *epc_page)
+{
+ unsigned long addr = SGX_ENCL_PAGE_ADDR(encl_page);
+ unsigned long va_offset = SGX_ENCL_PAGE_VA_OFFSET(encl_page);
+ struct sgx_encl *encl = encl_page->encl;
+ pgoff_t page_index = sgx_encl_get_index(encl, encl_page);
+ pgoff_t pcmd_index = sgx_pcmd_index(encl, page_index);
+ unsigned long pcmd_offset = sgx_pcmd_offset(page_index);
+ struct sgx_pageinfo pginfo;
+ struct page *backing;
+ struct page *pcmd;
+ int ret;
+
+ backing = sgx_encl_get_backing_page(encl, page_index);
+ if (IS_ERR(backing)) {
+ ret = PTR_ERR(backing);
+ goto err_backing;
+ }
+
+ pcmd = sgx_encl_get_backing_page(encl, pcmd_index);
+ if (IS_ERR(pcmd)) {
+ ret = PTR_ERR(pcmd);
+ goto err_pcmd;
+ }
+
+ pginfo.addr = addr;
+ pginfo.contents = (unsigned long)kmap_atomic(backing);
+ pginfo.metadata = (unsigned long)kmap_atomic(pcmd) + pcmd_offset;
+ pginfo.secs = addr ? (unsigned long)sgx_epc_addr(encl->secs.epc_page) :
+ 0;
+
+ ret = __eldu(&pginfo, sgx_epc_addr(epc_page),
+ sgx_epc_addr(encl_page->va_page->epc_page) + va_offset);
+ if (ret) {
+ if (encls_failed(ret) || encls_returned_code(ret))
+ ENCLS_WARN(ret, "ELDU");
+
+ ret = -EFAULT;
+ }
+
+ kunmap_atomic((void *)(unsigned long)(pginfo.metadata - pcmd_offset));
+ kunmap_atomic((void *)(unsigned long)pginfo.contents);
+
+ put_page(pcmd);
+
+err_pcmd:
+ put_page(backing);
+
+err_backing:
+ return ret;
+}
+
+static struct sgx_epc_page *sgx_encl_eldu(struct sgx_encl_page *encl_page)
+{
+ unsigned long va_offset = SGX_ENCL_PAGE_VA_OFFSET(encl_page);
+ struct sgx_encl *encl = encl_page->encl;
+ struct sgx_epc_page *epc_page;
+ int ret;
+
+ epc_page = sgx_alloc_page(encl_page, false);
+ if (IS_ERR(epc_page))
+ return epc_page;
+
+ ret = __sgx_encl_eldu(encl_page, epc_page);
+ if (ret) {
+ sgx_free_page(epc_page);
+ return ERR_PTR(ret);
+ }
+
+ sgx_free_va_slot(encl_page->va_page, va_offset);
+ list_move(&encl_page->va_page->list, &encl->va_pages);
+ encl_page->desc &= ~SGX_ENCL_PAGE_VA_OFFSET_MASK;
+ encl_page->epc_page = epc_page;
+
+ return epc_page;
+}
+
static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
unsigned long addr)
{
+ struct sgx_epc_page *epc_page;
struct sgx_encl_page *entry;
/* If process was forked, VMA is still there but vm_private_data is set
@@ -29,10 +109,27 @@ static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
return ERR_PTR(-EFAULT);
/* Page is already resident in the EPC. */
- if (entry->epc_page)
+ if (entry->epc_page) {
+ if (entry->desc & SGX_ENCL_PAGE_RECLAIMED)
+ return ERR_PTR(-EBUSY);
+
return entry;
+ }
+
+ if (!(encl->secs.epc_page)) {
+ epc_page = sgx_encl_eldu(&encl->secs);
+ if (IS_ERR(epc_page))
+ return ERR_CAST(epc_page);
+ }
+
+ epc_page = entry->epc_page ? entry->epc_page : sgx_encl_eldu(entry);
+ if (IS_ERR(epc_page))
+ return ERR_CAST(epc_page);
- return ERR_PTR(-EFAULT);
+ encl->secs_child_cnt++;
+ sgx_mark_page_reclaimable(entry->epc_page);
+
+ return entry;
}
struct sgx_encl_mm *sgx_encl_mm_add(struct sgx_encl *encl,
@@ -169,6 +266,8 @@ static unsigned int sgx_vma_fault(struct vm_fault *vmf)
goto out;
}
+ sgx_encl_test_and_clear_young(vma->vm_mm, entry);
+
out:
mutex_unlock(&encl->lock);
return ret;
@@ -220,6 +319,7 @@ EXPORT_SYMBOL_GPL(sgx_encl_find);
*/
void sgx_encl_destroy(struct sgx_encl *encl)
{
+ struct sgx_va_page *va_page;
struct sgx_encl_page *entry;
struct radix_tree_iter iter;
void **slot;
@@ -244,6 +344,15 @@ void sgx_encl_destroy(struct sgx_encl *encl)
sgx_free_page(encl->secs.epc_page);
encl->secs.epc_page = NULL;
}
+
+
+ while (!list_empty(&encl->va_pages)) {
+ va_page = list_first_entry(&encl->va_pages, struct sgx_va_page,
+ list);
+ list_del(&va_page->list);
+ sgx_free_page(va_page->epc_page);
+ kfree(va_page);
+ }
}
EXPORT_SYMBOL_GPL(sgx_encl_destroy);
@@ -347,3 +456,157 @@ struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl,
*iter = SGX_ENCL_MM_ITER_NEXT;
return encl_mm;
}
+
+static int sgx_encl_test_and_clear_young_cb(pte_t *ptep, pgtable_t token,
+ unsigned long addr, void *data)
+{
+ pte_t pte;
+ int ret;
+
+ ret = pte_young(*ptep);
+ if (ret) {
+ pte = pte_mkold(*ptep);
+ set_pte_at((struct mm_struct *)data, addr, ptep, pte);
+ }
+
+ return ret;
+}
+
+/**
+ * sgx_encl_test_and_clear_young() - Test and reset the accessed bit
+ * @mm: mm_struct that is checked
+ * @page: enclave page to be tested for recent access
+ *
+ * Checks the Access (A) bit from the PTE corresponding to the enclave page and
+ * clears it.
+ *
+ * Return: 1 if the page has been recently accessed and 0 if not.
+ */
+int sgx_encl_test_and_clear_young(struct mm_struct *mm,
+ struct sgx_encl_page *page)
+{
+ unsigned long addr = SGX_ENCL_PAGE_ADDR(page);
+ struct sgx_encl *encl = page->encl;
+ struct vm_area_struct *vma;
+ int ret;
+
+ ret = sgx_encl_find(mm, addr, &vma);
+ if (ret)
+ return 0;
+
+ if (encl != vma->vm_private_data)
+ return 0;
+
+ ret = apply_to_page_range(vma->vm_mm, addr, PAGE_SIZE,
+ sgx_encl_test_and_clear_young_cb, vma->vm_mm);
+ if (ret < 0)
+ return 0;
+
+ return ret;
+}
+
+/**
+ * sgx_encl_reserve_page() - Reserve an enclave page
+ * @encl: an enclave
+ * @addr: a page address
+ *
+ * Load an enclave page and lock the enclave so that the page can be used by
+ * EDBG* and EMOD*.
+ *
+ * Return:
+ * an enclave page on success
+ * -EFAULT if the load fails
+ */
+struct sgx_encl_page *sgx_encl_reserve_page(struct sgx_encl *encl,
+ unsigned long addr)
+{
+ struct sgx_encl_page *entry;
+
+ for ( ; ; ) {
+ mutex_lock(&encl->lock);
+
+ entry = sgx_encl_load_page(encl, addr);
+ if (PTR_ERR(entry) != -EBUSY)
+ break;
+
+ mutex_unlock(&encl->lock);
+ }
+
+ if (IS_ERR(entry))
+ mutex_unlock(&encl->lock);
+
+ return entry;
+}
+EXPORT_SYMBOL(sgx_encl_reserve_page);
+
+/**
+ * sgx_alloc_page - allocate a VA page
+ *
+ * Allocates an &sgx_epc_page instance and converts it to a VA page.
+ *
+ * Return:
+ * a &struct sgx_va_page instance,
+ * -errno otherwise
+ */
+struct sgx_epc_page *sgx_alloc_va_page(void)
+{
+ struct sgx_epc_page *epc_page;
+ int ret;
+
+ epc_page = sgx_alloc_page(NULL, true);
+ if (IS_ERR(epc_page))
+ return ERR_CAST(epc_page);
+
+ ret = __epa(sgx_epc_addr(epc_page));
+ if (ret) {
+ WARN_ONCE(1, "sgx: EPA returned %d (0x%x)", ret, ret);
+ sgx_free_page(epc_page);
+ return ERR_PTR(-EFAULT);
+ }
+
+ return epc_page;
+}
+EXPORT_SYMBOL_GPL(sgx_alloc_va_page);
+
+/**
+ * sgx_alloc_va_slot - allocate a VA slot
+ * @va_page: a &struct sgx_va_page instance
+ *
+ * Allocates a slot from a &struct sgx_va_page instance.
+ *
+ * Return: offset of the slot inside the VA page
+ */
+unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page)
+{
+ int slot = find_first_zero_bit(va_page->slots, SGX_VA_SLOT_COUNT);
+
+ if (slot < SGX_VA_SLOT_COUNT)
+ set_bit(slot, va_page->slots);
+
+ return slot << 3;
+}
+
+/**
+ * sgx_free_va_slot - free a VA slot
+ * @va_page: a &struct sgx_va_page instance
+ * @offset: offset of the slot inside the VA page
+ *
+ * Frees a slot from a &struct sgx_va_page instance.
+ */
+void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset)
+{
+ clear_bit(offset >> 3, va_page->slots);
+}
+
+/**
+ * sgx_va_page_full - is the VA page full?
+ * @va_page: a &struct sgx_va_page instance
+ *
+ * Return: true if all slots have been taken
+ */
+bool sgx_va_page_full(struct sgx_va_page *va_page)
+{
+ int slot = find_first_zero_bit(va_page->slots, SGX_VA_SLOT_COUNT);
+
+ return slot == SGX_VA_SLOT_COUNT;
+}
diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h
index 1f96991839ad..c557f0374d74 100644
--- a/arch/x86/kernel/cpu/sgx/encl.h
+++ b/arch/x86/kernel/cpu/sgx/encl.h
@@ -17,6 +17,10 @@
/**
* enum sgx_encl_page_desc - defines bits for an enclave page's descriptor
* %SGX_ENCL_PAGE_TCS: The page is a TCS page.
+ * %SGX_ENCL_PAGE_RECLAIMED: The page is in the process of being
+ * reclaimed.
+ * %SGX_ENCL_PAGE_VA_OFFSET_MASK: Holds the offset in the Version Array
+ * (VA) page for a swapped page.
* %SGX_ENCL_PAGE_ADDR_MASK: Holds the virtual address of the page.
*
* The page address for SECS is zero and is used by the subsystem to recognize
@@ -25,6 +29,8 @@
enum sgx_encl_page_desc {
SGX_ENCL_PAGE_TCS = BIT(0),
/* Bits 11:3 are available when the page is not swapped. */
+ SGX_ENCL_PAGE_RECLAIMED = BIT(3),
+ SGX_ENCL_PAGE_VA_OFFSET_MASK = GENMASK_ULL(11, 3),
SGX_ENCL_PAGE_ADDR_MASK = PAGE_MASK,
};
@@ -36,6 +42,7 @@ enum sgx_encl_page_desc {
struct sgx_encl_page {
unsigned long desc;
struct sgx_epc_page *epc_page;
+ struct sgx_va_page *va_page;
struct sgx_encl *encl;
};
@@ -68,15 +75,37 @@ struct sgx_encl {
unsigned long base;
unsigned long size;
unsigned long ssaframesize;
+ struct list_head va_pages;
struct radix_tree_root page_tree;
struct list_head add_page_reqs;
struct work_struct work;
struct sgx_encl_page secs;
struct notifier_block pm_notifier;
+ cpumask_t cpumask;
+};
+
+#define SGX_VA_SLOT_COUNT 512
+
+struct sgx_va_page {
+ struct sgx_epc_page *epc_page;
+ DECLARE_BITMAP(slots, SGX_VA_SLOT_COUNT);
+ struct list_head list;
};
extern const struct vm_operations_struct sgx_vm_ops;
+static inline pgoff_t sgx_pcmd_index(struct sgx_encl *encl,
+ pgoff_t page_index)
+{
+ return PFN_DOWN(encl->size) + 1 + (page_index >> 5);
+}
+
+static inline unsigned long sgx_pcmd_offset(pgoff_t page_index)
+{
+ return (page_index & (PAGE_SIZE / sizeof(struct sgx_pcmd) - 1)) *
+ sizeof(struct sgx_pcmd);
+}
+
enum sgx_encl_mm_iter {
SGX_ENCL_MM_ITER_DONE = 0,
SGX_ENCL_MM_ITER_NEXT = 1,
@@ -94,5 +123,14 @@ struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl,
struct sgx_encl_mm *sgx_encl_mm_add(struct sgx_encl *encl,
struct mm_struct *mm);
void sgx_encl_mm_release(struct kref *ref);
+int sgx_encl_test_and_clear_young(struct mm_struct *mm,
+ struct sgx_encl_page *page);
+struct sgx_encl_page *sgx_encl_reserve_page(struct sgx_encl *encl,
+ unsigned long addr);
+
+struct sgx_epc_page *sgx_alloc_va_page(void);
+unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page);
+void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset);
+bool sgx_va_page_full(struct sgx_va_page *va_page);
#endif /* _X86_ENCL_H */
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index d911a1038712..07adb35c260b 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -22,7 +22,7 @@ int sgx_nr_epc_sections;
/* A per-cpu cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs. */
static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache);
-static struct sgx_epc_page *sgx_section_get_page(
+static struct sgx_epc_page *sgx_section_try_take_page(
struct sgx_epc_section *section)
{
struct sgx_epc_page *page;
@@ -30,23 +30,14 @@ static struct sgx_epc_page *sgx_section_get_page(
if (!section->free_cnt)
return NULL;
- page = list_first_entry(§ion->page_list,
- struct sgx_epc_page, list);
+ page = list_first_entry(§ion->page_list, struct sgx_epc_page,
+ list);
list_del_init(&page->list);
section->free_cnt--;
return page;
}
-/**
- * sgx_alloc_page - Allocate an EPC page
- *
- * Try to grab a page from the free EPC page list.
- *
- * Return:
- * a pointer to a &struct sgx_epc_page instance,
- * -errno on error
- */
-struct sgx_epc_page *sgx_alloc_page(void)
+static struct sgx_epc_page *sgx_try_alloc_page(void *owner)
{
struct sgx_epc_section *section;
struct sgx_epc_page *page;
@@ -55,14 +46,61 @@ struct sgx_epc_page *sgx_alloc_page(void)
for (i = 0; i < sgx_nr_epc_sections; i++) {
section = &sgx_epc_sections[i];
spin_lock(§ion->lock);
- page = sgx_section_get_page(section);
+ page = sgx_section_try_take_page(section);
spin_unlock(§ion->lock);
- if (page)
+ if (page) {
+ page->owner = owner;
return page;
+ }
}
- return ERR_PTR(-ENOMEM);
+ return NULL;
+}
+
+/**
+ * sgx_alloc_page - Allocate an EPC page
+ * @owner: the owner of the EPC page
+ * @reclaim: reclaim pages if necessary
+ *
+ * Try to grab a page from the free EPC page list. If there is a free page
+ * available, it is returned to the caller. The @reclaim parameter hints
+ * the EPC memory manager to swap pages when required.
+ *
+ * Return:
+ * a pointer to a &struct sgx_epc_page instance,
+ * -errno on error
+ */
+struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim)
+{
+ struct sgx_epc_page *entry;
+
+ for ( ; ; ) {
+ entry = sgx_try_alloc_page(owner);
+ if (entry)
+ break;
+
+ if (list_empty(&sgx_active_page_list))
+ return ERR_PTR(-ENOMEM);
+
+ if (!reclaim) {
+ entry = ERR_PTR(-EBUSY);
+ break;
+ }
+
+ if (signal_pending(current)) {
+ entry = ERR_PTR(-ERESTARTSYS);
+ break;
+ }
+
+ sgx_reclaim_pages();
+ schedule();
+ }
+
+ if (sgx_calc_free_cnt() < SGX_NR_LOW_PAGES)
+ wake_up(&ksgxswapd_waitq);
+
+ return entry;
}
EXPORT_SYMBOL_GPL(sgx_alloc_page);
@@ -70,10 +108,12 @@ EXPORT_SYMBOL_GPL(sgx_alloc_page);
* __sgx_free_page - Free an EPC page
* @page: pointer a previously allocated EPC page
*
- * EREMOVE an EPC page and insert it back to the list of free pages.
+ * EREMOVE an EPC page and insert it back to the list of free pages. If the
+ * page is reclaimable, delete it from the active page list.
*
* Return:
* 0 on success
+ * -EBUSY if the page cannot be removed from the active list
* SGX error code if EREMOVE fails
*/
int __sgx_free_page(struct sgx_epc_page *page)
@@ -81,6 +121,23 @@ int __sgx_free_page(struct sgx_epc_page *page)
struct sgx_epc_section *section = sgx_epc_section(page);
int ret;
+ /*
+ * Remove the page from the active list if necessary. If the page
+ * is actively being reclaimed, i.e. RECLAIMABLE is set but the
+ * page isn't on the active list, return -EBUSY as we can't free
+ * the page at this time since it is "owned" by the reclaimer.
+ */
+ spin_lock(&sgx_active_page_list_lock);
+ if (page->desc & SGX_EPC_PAGE_RECLAIMABLE) {
+ if (list_empty(&page->list)) {
+ spin_unlock(&sgx_active_page_list_lock);
+ return -EBUSY;
+ }
+ list_del(&page->list);
+ page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE;
+ }
+ spin_unlock(&sgx_active_page_list_lock);
+
ret = __eremove(sgx_epc_addr(page));
if (ret)
return ret;
@@ -107,6 +164,7 @@ void sgx_free_page(struct sgx_epc_page *page)
int ret;
ret = __sgx_free_page(page);
+ WARN(ret < 0, "sgx: cannot free page, reclaim in-progress");
WARN(ret > 0, "sgx: EREMOVE returned %d (0x%x)", ret, ret);
}
EXPORT_SYMBOL_GPL(sgx_free_page);
diff --git a/arch/x86/kernel/cpu/sgx/reclaim.c b/arch/x86/kernel/cpu/sgx/reclaim.c
index 042769f03be9..219d31495838 100644
--- a/arch/x86/kernel/cpu/sgx/reclaim.c
+++ b/arch/x86/kernel/cpu/sgx/reclaim.c
@@ -9,9 +9,13 @@
#include <linux/slab.h>
#include <linux/sched/mm.h>
#include <linux/sched/signal.h>
-#include "encls.h"
+#include "driver/driver.h"
#include "sgx.h"
+LIST_HEAD(sgx_active_page_list);
+DEFINE_SPINLOCK(sgx_active_page_list_lock);
+DECLARE_WAIT_QUEUE_HEAD(ksgxswapd_waitq);
+
static struct task_struct *ksgxswapd_tsk;
static void sgx_sanitize_section(struct sgx_epc_section *section)
@@ -58,6 +62,12 @@ static void sgx_sanitize_section(struct sgx_epc_section *section)
}
}
+static inline bool sgx_should_reclaim(void)
+{
+ return sgx_calc_free_cnt() < SGX_NR_HIGH_PAGES &&
+ !list_empty(&sgx_active_page_list);
+}
+
static int ksgxswapd(void *p)
{
int i;
@@ -67,6 +77,19 @@ static int ksgxswapd(void *p)
for (i = 0; i < sgx_nr_epc_sections; i++)
sgx_sanitize_section(&sgx_epc_sections[i]);
+ while (!kthread_should_stop()) {
+ if (try_to_freeze())
+ continue;
+
+ wait_event_freezable(ksgxswapd_waitq, kthread_should_stop() ||
+ sgx_should_reclaim());
+
+ if (sgx_should_reclaim())
+ sgx_reclaim_pages();
+
+ cond_resched();
+ }
+
return 0;
}
@@ -82,3 +105,378 @@ int sgx_page_reclaimer_init(void)
return 0;
}
+
+/**
+ * sgx_mark_page_reclaimable() - Mark a page as reclaimable
+ * @page: EPC page
+ *
+ * Mark a page as reclaimable and add it to the active page list. Pages
+ * are automatically removed from the active list when freed.
+ */
+void sgx_mark_page_reclaimable(struct sgx_epc_page *page)
+{
+ spin_lock(&sgx_active_page_list_lock);
+ page->desc |= SGX_EPC_PAGE_RECLAIMABLE;
+ list_add_tail(&page->list, &sgx_active_page_list);
+ spin_unlock(&sgx_active_page_list_lock);
+}
+EXPORT_SYMBOL_GPL(sgx_mark_page_reclaimable);
+
+bool sgx_reclaimer_get(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ struct sgx_encl *encl = encl_page->encl;
+
+ return kref_get_unless_zero(&encl->refcount) != 0;
+}
+
+void sgx_reclaimer_put(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ struct sgx_encl *encl = encl_page->encl;
+
+ kref_put(&encl->refcount, sgx_encl_release);
+}
+
+static bool sgx_reclaimer_evict(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *page = epc_page->owner;
+ struct sgx_encl *encl = page->encl;
+ struct sgx_encl_mm *encl_mm = NULL;
+ struct sgx_encl_mm *prev_mm = NULL;
+ bool ret = true;
+ int iter;
+
+ while (true) {
+ encl_mm = sgx_encl_next_mm(encl, prev_mm, &iter);
+ if (prev_mm)
+ kref_put(&prev_mm->refcount, sgx_encl_mm_release);
+ prev_mm = encl_mm;
+
+ if (iter == SGX_ENCL_MM_ITER_DONE)
+ break;
+
+ if (iter == SGX_ENCL_MM_ITER_RESTART)
+ continue;
+
+ if (!mmget_not_zero(encl_mm->mm))
+ continue;
+
+ down_read(&encl_mm->mm->mmap_sem);
+ ret = !sgx_encl_test_and_clear_young(encl_mm->mm, page);
+ up_read(&encl_mm->mm->mmap_sem);
+
+ mmput(encl_mm->mm);
+
+ if (!ret || (encl->flags & SGX_ENCL_DEAD)) {
+ kref_put(&encl_mm->refcount, sgx_encl_mm_release);
+ break;
+ }
+ }
+
+ /*
+ * Do not reclaim this page if it has been recently accessed by any
+ * mm_struct *and* if the enclave is still alive. No need to take
+ * the enclave's lock, worst case scenario reclaiming pages from a
+ * dead enclave is delayed slightly. A live enclave with a recently
+ * accessed page is more common and avoiding lock contention in that
+ * case is a boon to performance.
+ */
+ if (!ret && !(encl->flags & SGX_ENCL_DEAD))
+ return false;
+
+ mutex_lock(&encl->lock);
+ page->desc |= SGX_ENCL_PAGE_RECLAIMED;
+ mutex_unlock(&encl->lock);
+
+ return true;
+}
+
+static void sgx_reclaimer_block(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *page = epc_page->owner;
+ unsigned long addr = SGX_ENCL_PAGE_ADDR(page);
+ struct sgx_encl *encl = page->encl;
+ struct sgx_encl_mm *encl_mm = NULL;
+ struct sgx_encl_mm *prev_mm = NULL;
+ struct vm_area_struct *vma;
+ int iter;
+ int ret;
+
+ while (true) {
+ encl_mm = sgx_encl_next_mm(encl, prev_mm, &iter);
+ if (prev_mm)
+ kref_put(&prev_mm->refcount, sgx_encl_mm_release);
+ prev_mm = encl_mm;
+
+ if (iter == SGX_ENCL_MM_ITER_DONE)
+ break;
+
+ if (iter == SGX_ENCL_MM_ITER_RESTART)
+ continue;
+
+ if (!mmget_not_zero(encl_mm->mm))
+ continue;
+
+ down_read(&encl_mm->mm->mmap_sem);
+
+ ret = sgx_encl_find(encl_mm->mm, addr, &vma);
+ if (!ret && encl == vma->vm_private_data)
+ zap_vma_ptes(vma, addr, PAGE_SIZE);
+
+ up_read(&encl_mm->mm->mmap_sem);
+
+ mmput(encl_mm->mm);
+ }
+
+ mutex_lock(&encl->lock);
+
+ if (!(encl->flags & SGX_ENCL_DEAD)) {
+ ret = __eblock(sgx_epc_addr(epc_page));
+ if (encls_failed(ret))
+ ENCLS_WARN(ret, "EBLOCK");
+ }
+
+ mutex_unlock(&encl->lock);
+}
+
+static int __sgx_encl_ewb(struct sgx_encl *encl, struct sgx_epc_page *epc_page,
+ struct sgx_va_page *va_page, unsigned int va_offset)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ pgoff_t page_index = sgx_encl_get_index(encl, encl_page);
+ pgoff_t pcmd_index = sgx_pcmd_index(encl, page_index);
+ unsigned long pcmd_offset = sgx_pcmd_offset(page_index);
+ struct sgx_pageinfo pginfo;
+ struct page *backing;
+ struct page *pcmd;
+ int ret;
+
+ backing = sgx_encl_get_backing_page(encl, page_index);
+ if (IS_ERR(backing)) {
+ ret = PTR_ERR(backing);
+ goto err_backing;
+ }
+
+ pcmd = sgx_encl_get_backing_page(encl, pcmd_index);
+ if (IS_ERR(pcmd)) {
+ ret = PTR_ERR(pcmd);
+ goto err_pcmd;
+ }
+
+ pginfo.addr = 0;
+ pginfo.contents = (unsigned long)kmap_atomic(backing);
+ pginfo.metadata = (unsigned long)kmap_atomic(pcmd) + pcmd_offset;
+ pginfo.secs = 0;
+ ret = __ewb(&pginfo, sgx_epc_addr(epc_page),
+ sgx_epc_addr(va_page->epc_page) + va_offset);
+ kunmap_atomic((void *)(unsigned long)(pginfo.metadata - pcmd_offset));
+ kunmap_atomic((void *)(unsigned long)pginfo.contents);
+
+ set_page_dirty(pcmd);
+ put_page(pcmd);
+ set_page_dirty(backing);
+
+err_pcmd:
+ put_page(backing);
+
+err_backing:
+ return ret;
+}
+
+static void sgx_ipi_cb(void *info)
+{
+}
+
+static const cpumask_t *sgx_encl_ewb_cpumask(struct sgx_encl *encl)
+{
+ cpumask_t *cpumask = &encl->cpumask;
+ struct sgx_encl_mm *encl_mm = NULL;
+ struct sgx_encl_mm *prev_mm = NULL;
+ int iter;
+
+ cpumask_clear(cpumask);
+
+ while (true) {
+ encl_mm = sgx_encl_next_mm(encl, prev_mm, &iter);
+ if (prev_mm)
+ kref_put(&prev_mm->refcount, sgx_encl_mm_release);
+ prev_mm = encl_mm;
+
+ if (iter == SGX_ENCL_MM_ITER_DONE)
+ break;
+
+ if (iter == SGX_ENCL_MM_ITER_RESTART)
+ continue;
+
+ if (!mmget_not_zero(encl_mm->mm))
+ continue;
+
+ cpumask_or(cpumask, cpumask, mm_cpumask(encl_mm->mm));
+
+ mmput(encl_mm->mm);
+ }
+
+ return cpumask;
+}
+
+static void sgx_encl_ewb(struct sgx_epc_page *epc_page, bool do_free)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ struct sgx_encl *encl = encl_page->encl;
+ struct sgx_va_page *va_page;
+ unsigned int va_offset;
+ int ret;
+
+ encl_page->desc &= ~SGX_ENCL_PAGE_RECLAIMED;
+
+ if (!(encl->flags & SGX_ENCL_DEAD)) {
+ va_page = list_first_entry(&encl->va_pages, struct sgx_va_page,
+ list);
+ va_offset = sgx_alloc_va_slot(va_page);
+ if (sgx_va_page_full(va_page))
+ list_move_tail(&va_page->list, &encl->va_pages);
+
+ ret = __sgx_encl_ewb(encl, epc_page, va_page, va_offset);
+ if (ret == SGX_NOT_TRACKED) {
+ ret = __etrack(sgx_epc_addr(encl->secs.epc_page));
+ if (ret) {
+ if (encls_failed(ret) ||
+ encls_returned_code(ret))
+ ENCLS_WARN(ret, "ETRACK");
+ }
+
+ ret = __sgx_encl_ewb(encl, epc_page, va_page,
+ va_offset);
+ if (ret == SGX_NOT_TRACKED) {
+ /*
+ * Slow path, send IPIs to kick cpus out of the
+ * enclave. Note, it's imperative that the cpu
+ * mask is generated *after* ETRACK, else we'll
+ * miss cpus that entered the enclave between
+ * generating the mask and incrementing epoch.
+ */
+ on_each_cpu_mask(sgx_encl_ewb_cpumask(encl),
+ sgx_ipi_cb, NULL, 1);
+ ret = __sgx_encl_ewb(encl, epc_page, va_page,
+ va_offset);
+ }
+ }
+
+ if (ret)
+ if (encls_failed(ret) || encls_returned_code(ret))
+ ENCLS_WARN(ret, "EWB");
+
+ encl_page->desc |= va_offset;
+ encl_page->va_page = va_page;
+ } else if (!do_free) {
+ ret = __eremove(sgx_epc_addr(epc_page));
+ WARN(ret, "EREMOVE returned %d\n", ret);
+ }
+
+ if (do_free)
+ sgx_free_page(epc_page);
+
+ encl_page->epc_page = NULL;
+}
+
+static void sgx_reclaimer_write(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ struct sgx_encl *encl = encl_page->encl;
+
+ mutex_lock(&encl->lock);
+
+ sgx_encl_ewb(epc_page, false);
+ encl->secs_child_cnt--;
+ if (!encl->secs_child_cnt &&
+ (encl->flags & (SGX_ENCL_DEAD | SGX_ENCL_INITIALIZED))) {
+ sgx_encl_ewb(encl->secs.epc_page, true);
+ }
+
+ mutex_unlock(&encl->lock);
+}
+
+/**
+ * sgx_reclaim_pages() - Reclaim EPC pages from the consumers
+ * Takes a fixed chunk of pages from the global list of consumed EPC pages and
+ * tries to swap them. Only the pages that are either being freed by the
+ * consumer or actively used are skipped.
+ */
+void sgx_reclaim_pages(void)
+{
+ struct sgx_epc_page *chunk[SGX_NR_TO_SCAN + 1];
+ struct sgx_epc_page *epc_page;
+ struct sgx_epc_section *section;
+ int i, j;
+
+ spin_lock(&sgx_active_page_list_lock);
+ for (i = 0, j = 0; i < SGX_NR_TO_SCAN; i++) {
+ if (list_empty(&sgx_active_page_list))
+ break;
+
+ epc_page = list_first_entry(&sgx_active_page_list,
+ struct sgx_epc_page, list);
+ list_del_init(&epc_page->list);
+
+ if (sgx_reclaimer_get(epc_page))
+ chunk[j++] = epc_page;
+ else
+ /* The owner is freeing the page. No need to add the
+ * page back to the list of reclaimable pages.
+ */
+ epc_page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE;
+ }
+ spin_unlock(&sgx_active_page_list_lock);
+
+ for (i = 0; i < j; i++) {
+ epc_page = chunk[i];
+ if (sgx_reclaimer_evict(epc_page))
+ continue;
+
+ sgx_reclaimer_put(epc_page);
+
+ spin_lock(&sgx_active_page_list_lock);
+ list_add_tail(&epc_page->list, &sgx_active_page_list);
+ spin_unlock(&sgx_active_page_list_lock);
+
+ chunk[i] = NULL;
+ }
+
+ for (i = 0; i < j; i++) {
+ epc_page = chunk[i];
+ if (epc_page)
+ sgx_reclaimer_block(epc_page);
+ }
+
+ for (i = 0; i < j; i++) {
+ epc_page = chunk[i];
+ if (epc_page) {
+ sgx_reclaimer_write(epc_page);
+ sgx_reclaimer_put(epc_page);
+ epc_page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE;
+
+ section = sgx_epc_section(epc_page);
+
+ spin_lock(§ion->lock);
+ list_add_tail(&epc_page->list,
+ §ion->page_list);
+ section->free_cnt++;
+ spin_unlock(§ion->lock);
+ }
+ }
+}
+
+unsigned long sgx_calc_free_cnt(void)
+{
+ struct sgx_epc_section *section;
+ unsigned long free_cnt = 0;
+ int i;
+
+ for (i = 0; i < sgx_nr_epc_sections; i++) {
+ section = &sgx_epc_sections[i];
+ free_cnt += section->free_cnt;
+ }
+
+ return free_cnt;
+}
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index 62a574ed230a..8a1dff1e5e8a 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -12,6 +12,7 @@
struct sgx_epc_page {
unsigned long desc;
+ struct sgx_encl_page *owner;
struct list_head list;
};
@@ -43,9 +44,14 @@ extern bool sgx_enabled;
* physical memory. The existing and near-future
* hardware defines at most eight sections, hence
* three bits to hold a section.
+ * %SGX_EPC_PAGE_RECLAIMABLE: The page has been been marked as reclaimable.
+ * Pages need to be colored this way because a page
+ * can be out of the active page list in the
+ * process of being swapped out.
*/
enum sgx_epc_page_desc {
SGX_EPC_SECTION_MASK = GENMASK_ULL(3, 0),
+ SGX_EPC_PAGE_RECLAIMABLE = BIT(4),
/* bits 12-63 are reserved for the physical page address of the page */
};
@@ -61,11 +67,21 @@ static inline void *sgx_epc_addr(struct sgx_epc_page *page)
return section->va + (page->desc & PAGE_MASK) - section->pa;
}
+#define SGX_NR_TO_SCAN 16
+#define SGX_NR_LOW_PAGES 32
+#define SGX_NR_HIGH_PAGES 64
+
extern int sgx_nr_epc_sections;
+extern struct list_head sgx_active_page_list;
+extern spinlock_t sgx_active_page_list_lock;
+extern struct wait_queue_head(ksgxswapd_waitq);
int sgx_page_reclaimer_init(void);
+void sgx_mark_page_reclaimable(struct sgx_epc_page *page);
+unsigned long sgx_calc_free_cnt(void);
+void sgx_reclaim_pages(void);
-struct sgx_epc_page *sgx_alloc_page(void);
+struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim);
int __sgx_free_page(struct sgx_epc_page *page);
void sgx_free_page(struct sgx_epc_page *page);
int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 18/28] x86/sgx: ptrace() support for the SGX driver
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (16 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 17/28] x86/sgx: Add swapping code to the core and SGX driver Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 19/28] x86/vdso: Add support for exception fixup in vDSO functions Jarkko Sakkinen
` (15 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
Add VMA callbacks for ptrace() that can be used with debug enclaves.
With debug enclaves data can be read and write the memory word at a time
by using ENCLS(EDBGRD) and ENCLS(EDBGWR) leaf instructions.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
arch/x86/kernel/cpu/sgx/encl.c | 97 ++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
index 16e8524687c1..7216bdf07bd0 100644
--- a/arch/x86/kernel/cpu/sgx/encl.c
+++ b/arch/x86/kernel/cpu/sgx/encl.c
@@ -273,10 +273,107 @@ static unsigned int sgx_vma_fault(struct vm_fault *vmf)
return ret;
}
+static int sgx_edbgrd(struct sgx_encl *encl, struct sgx_encl_page *page,
+ unsigned long addr, void *data)
+{
+ unsigned long offset;
+ int ret;
+
+ offset = addr & ~PAGE_MASK;
+
+ if ((page->desc & SGX_ENCL_PAGE_TCS) &&
+ offset > offsetof(struct sgx_tcs, gs_limit))
+ return -ECANCELED;
+
+ ret = __edbgrd(sgx_epc_addr(page->epc_page) + offset, data);
+ if (ret)
+ return -EIO;
+
+ return 0;
+}
+
+static int sgx_edbgwr(struct sgx_encl *encl, struct sgx_encl_page *page,
+ unsigned long addr, void *data)
+{
+ unsigned long offset;
+ int ret;
+
+ offset = addr & ~PAGE_MASK;
+
+ /* Writing anything else than flags will cause #GP */
+ if ((page->desc & SGX_ENCL_PAGE_TCS) &&
+ offset != offsetof(struct sgx_tcs, flags))
+ return -ECANCELED;
+
+ ret = __edbgwr(sgx_epc_addr(page->epc_page) + offset, data);
+ if (ret)
+ return -EIO;
+
+ return 0;
+}
+
+static int sgx_vma_access(struct vm_area_struct *vma, unsigned long addr,
+ void *buf, int len, int write)
+{
+ struct sgx_encl *encl = vma->vm_private_data;
+ struct sgx_encl_page *entry = NULL;
+ unsigned long align;
+ char data[sizeof(unsigned long)];
+ int offset;
+ int cnt;
+ int ret = 0;
+ int i;
+
+ /* If process was forked, VMA is still there but vm_private_data is set
+ * to NULL.
+ */
+ if (!encl)
+ return -EFAULT;
+
+ if (!(encl->flags & SGX_ENCL_DEBUG) ||
+ !(encl->flags & SGX_ENCL_INITIALIZED) ||
+ (encl->flags & SGX_ENCL_DEAD))
+ return -EFAULT;
+
+ for (i = 0; i < len; i += cnt) {
+ entry = sgx_encl_reserve_page(encl, (addr + i) & PAGE_MASK);
+ if (IS_ERR(entry)) {
+ ret = PTR_ERR(entry);
+ break;
+ }
+
+ align = ALIGN_DOWN(addr + i, sizeof(unsigned long));
+ offset = (addr + i) & (sizeof(unsigned long) - 1);
+ cnt = sizeof(unsigned long) - offset;
+ cnt = min(cnt, len - i);
+
+ ret = sgx_edbgrd(encl, entry, align, data);
+ if (ret)
+ goto out;
+
+ if (write) {
+ memcpy(data + offset, buf + i, cnt);
+ ret = sgx_edbgwr(encl, entry, align, data);
+ if (ret)
+ goto out;
+ } else
+ memcpy(buf + i, data + offset, cnt);
+
+out:
+ mutex_unlock(&encl->lock);
+
+ if (ret)
+ break;
+ }
+
+ return ret < 0 ? ret : i;
+}
+
const struct vm_operations_struct sgx_vm_ops = {
.close = sgx_vma_close,
.open = sgx_vma_open,
.fault = sgx_vma_fault,
+ .access = sgx_vma_access,
};
EXPORT_SYMBOL_GPL(sgx_vm_ops);
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 19/28] x86/vdso: Add support for exception fixup in vDSO functions
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (17 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 18/28] x86/sgx: ptrace() support for the " Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 20/28] x86/fault: Add helper function to sanitize error code Jarkko Sakkinen
` (14 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Andy Lutomirski, Jarkko Sakkinen, Dave Hansen
From: Sean Christopherson <sean.j.christopherson@intel.com>
The basic concept and implementation is very similar to the kernel's
exception fixup mechanism. The key differences are that the kernel
handler is hardcoded and the fixup entry addresses are relative to
the overall table as opposed to individual entries.
Hardcoding the kernel handler avoids the need to figure out how to
get userspace code to point at a kernel function. Given that the
expected usage is to propagate information to userspace, dumping all
fault information into registers is likely the desired behavior for
the vast majority of yet-to-be-created functions. Use registers
DI, SI and DX to communicate fault information, which follows Linux's
ABI for register consumption and hopefully avoids conflict with
hardware features that might leverage the fixup capabilities, e.g.
register usage for SGX instructions was at least partially designed
with calling conventions in mind.
Making fixup addresses relative to the overall table allows the table
to be stripped from the final vDSO image (it's a kernel construct)
without complicating the offset logic, e.g. entry-relative addressing
would also need to account for the table's location relative to the
image.
Regarding stripping the table, modify vdso2c to extract the table from
the raw, a.k.a. unstripped, data and dump it as a standalone byte array
in the resulting .c file. The original base of the table, its length
and a pointer to the byte array are captured in struct vdso_image.
Alternatively, the table could be dumped directly into the struct,
but because the number of entries can vary per image, that would
require either hardcoding a max sized table into the struct definition
or defining the table as a flexible length array. The flexible length
array approach has zero benefits, e.g. the base/size are still needed,
and prevents reusing the extraction code, while hardcoding the max size
adds ongoing maintenance just to avoid exporting the explicit size.
The immediate use case is for Intel Software Guard Extensions (SGX).
SGX introduces a new CPL3-only "enclave" mode that runs as a sort of
black box shared object that is hosted by an untrusted "normal" CPl3
process.
Entering an enclave can only be done through SGX-specific instructions,
EENTER and ERESUME, and is a non-trivial process. Because of the
complexity of transitioning to/from an enclave, the vast majority of
enclaves are expected to utilize a library to handle the actual
transitions. This is roughly analogous to how e.g. libc implementations
are used by most applications.
Another crucial characteristic of SGX enclaves is that they can generate
exceptions as part of their normal (at least as "normal" as SGX can be)
operation that need to be handled *in* the enclave and/or are unique
to SGX.
And because they are essentially fancy shared objects, a process can
host any number of enclaves, each of which can execute multiple threads
simultaneously.
Putting everything together, userspace enclaves will utilize a library
that must be prepared to handle any and (almost) all exceptions any time
at least one thread may be executing in an enclave. Leveraging signals
to handle the enclave exceptions is unpleasant, to put it mildly, e.g.
the SGX library must constantly (un)register its signal handler based
on whether or not at least one thread is executing in an enclave, and
filter and forward exceptions that aren't related to its enclaves. This
becomes particularly nasty when using multiple levels of libraries that
register signal handlers, e.g. running an enclave via cgo inside of the
Go runtime.
Enabling exception fixup in vDSO allows the kernel to provide a vDSO
function that wraps the low-level transitions to/from the enclave, i.e.
the EENTER and ERESUME instructions. The vDSO function can intercept
exceptions that would otherwise generate a signal and return the fault
information directly to its caller, thus avoiding the need to juggle
signal handlers.
Note that unlike the kernel's _ASM_EXTABLE_HANDLE implementation, the
'C' version of _ASM_VDSO_EXTABLE_HANDLE doesn't use a pre-compiled
assembly macro. Duplicating four lines of code is simpler than adding
the necessary infrastructure to generate pre-compiled assembly and the
intended benefit of massaging GCC's inlining algorithm is unlikely to
realized in the vDSO any time soon, if ever.
Suggested-by: Andy Lutomirski <luto@amacapital.net>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/entry/vdso/Makefile | 4 +-
arch/x86/entry/vdso/extable.c | 37 +++++++++++++++++
arch/x86/entry/vdso/extable.h | 29 ++++++++++++++
arch/x86/entry/vdso/vdso-layout.lds.S | 9 ++++-
arch/x86/entry/vdso/vdso2c.h | 58 +++++++++++++++++++++++----
arch/x86/include/asm/vdso.h | 5 +++
6 files changed, 131 insertions(+), 11 deletions(-)
create mode 100644 arch/x86/entry/vdso/extable.c
create mode 100644 arch/x86/entry/vdso/extable.h
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 5bfe2243a08f..34bcf87d358c 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -20,7 +20,7 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y
vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
# files to link into kernel
-obj-y += vma.o
+obj-y += vma.o extable.o
OBJECT_FILES_NON_STANDARD_vma.o := n
# vDSO images to build
@@ -115,7 +115,7 @@ $(obj)/%-x32.o: $(obj)/%.o FORCE
targets += vdsox32.lds $(vobjx32s-y)
-$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: OBJCOPYFLAGS := -S --remove-section __ex_table
$(obj)/%.so: $(obj)/%.so.dbg
$(call if_changed,objcopy)
diff --git a/arch/x86/entry/vdso/extable.c b/arch/x86/entry/vdso/extable.c
new file mode 100644
index 000000000000..49284d560d36
--- /dev/null
+++ b/arch/x86/entry/vdso/extable.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <asm/current.h>
+#include <asm/vdso.h>
+
+struct vdso_exception_table_entry {
+ int insn, fixup;
+};
+
+bool fixup_vdso_exception(struct pt_regs *regs, int trapnr,
+ unsigned long error_code, unsigned long fault_addr)
+{
+ const struct vdso_image *image = current->mm->context.vdso_image;
+ const struct vdso_exception_table_entry *extable;
+ unsigned int nr_entries, i;
+ unsigned long base;
+
+ if (!current->mm->context.vdso)
+ return false;
+
+ base = (unsigned long)current->mm->context.vdso + image->extable_base;
+ nr_entries = image->extable_len / (sizeof(*extable));
+ extable = image->extable;
+
+ for (i = 0; i < nr_entries; i++) {
+ if (regs->ip == base + extable[i].insn) {
+ regs->ip = base + extable[i].fixup;
+ regs->di = trapnr;
+ regs->si = error_code;
+ regs->dx = fault_addr;
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/arch/x86/entry/vdso/extable.h b/arch/x86/entry/vdso/extable.h
new file mode 100644
index 000000000000..aafdac396948
--- /dev/null
+++ b/arch/x86/entry/vdso/extable.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __VDSO_EXTABLE_H
+#define __VDSO_EXTABLE_H
+
+/*
+ * Inject exception fixup for vDSO code. Unlike normal exception fixup,
+ * vDSO uses a dedicated handler the addresses are relative to the overall
+ * exception table, not each individual entry.
+ */
+#ifdef __ASSEMBLY__
+#define _ASM_VDSO_EXTABLE_HANDLE(from, to) \
+ ASM_VDSO_EXTABLE_HANDLE from to
+
+.macro ASM_VDSO_EXTABLE_HANDLE from:req to:req
+ .pushsection __ex_table, "a"
+ .long (\from) - __ex_table
+ .long (\to) - __ex_table
+ .popsection
+.endm
+#else
+#define _ASM_VDSO_EXTABLE_HANDLE(from, to) \
+ ".pushsection __ex_table, \"a\"\n" \
+ ".long (" #from ") - __ex_table\n" \
+ ".long (" #to ") - __ex_table\n" \
+ ".popsection\n"
+#endif
+
+#endif /* __VDSO_EXTABLE_H */
+
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S
index 93c6dc7812d0..8ef849064501 100644
--- a/arch/x86/entry/vdso/vdso-layout.lds.S
+++ b/arch/x86/entry/vdso/vdso-layout.lds.S
@@ -63,11 +63,18 @@ SECTIONS
* stuff that isn't used at runtime in between.
*/
- .text : { *(.text*) } :text =0x90909090,
+ .text : {
+ *(.text*)
+ *(.fixup)
+ } :text =0x90909090,
+
+
.altinstructions : { *(.altinstructions) } :text
.altinstr_replacement : { *(.altinstr_replacement) } :text
+ __ex_table : { *(__ex_table) } :text
+
/DISCARD/ : {
*(.discard)
*(.discard.*)
diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h
index fa847a620f40..eca2f808bec3 100644
--- a/arch/x86/entry/vdso/vdso2c.h
+++ b/arch/x86/entry/vdso/vdso2c.h
@@ -5,6 +5,41 @@
* are built for 32-bit userspace.
*/
+static void BITSFUNC(copy)(FILE *outfile, const unsigned char *data, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (i % 10 == 0)
+ fprintf(outfile, "\n\t");
+ fprintf(outfile, "0x%02X, ", (int)(data)[i]);
+ }
+}
+
+
+/*
+ * Extract a section from the input data into a standalone blob. Used to
+ * capture kernel-only data that needs to persist indefinitely, e.g. the
+ * exception fixup tables, but only in the kernel, i.e. the section can
+ * be stripped from the final vDSO image.
+ */
+static void BITSFUNC(extract)(const unsigned char *data, size_t data_len,
+ FILE *outfile, ELF(Shdr) *sec, const char *name)
+{
+ unsigned long offset;
+ size_t len;
+
+ offset = (unsigned long)GET_LE(&sec->sh_offset);
+ len = (size_t)GET_LE(&sec->sh_size);
+
+ if (offset + len > data_len)
+ fail("section to extract overruns input data");
+
+ fprintf(outfile, "static const unsigned char %s[%lu] = {", name, len);
+ BITSFUNC(copy)(outfile, data + offset, len);
+ fprintf(outfile, "\n};\n\n");
+}
+
static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
void *stripped_addr, size_t stripped_len,
FILE *outfile, const char *name)
@@ -14,9 +49,8 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
unsigned long mapping_size;
ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
int i;
- unsigned long j;
ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
- *alt_sec = NULL;
+ *alt_sec = NULL, *extable_sec = NULL;
ELF(Dyn) *dyn = 0, *dyn_end = 0;
const char *secstrings;
INT_BITS syms[NSYMS] = {};
@@ -78,6 +112,8 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
if (!strcmp(secstrings + GET_LE(&sh->sh_name),
".altinstructions"))
alt_sec = sh;
+ if (!strcmp(secstrings + GET_LE(&sh->sh_name), "__ex_table"))
+ extable_sec = sh;
}
if (!symtab_hdr)
@@ -149,13 +185,11 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
fprintf(outfile,
"static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {",
mapping_size);
- for (j = 0; j < stripped_len; j++) {
- if (j % 10 == 0)
- fprintf(outfile, "\n\t");
- fprintf(outfile, "0x%02X, ",
- (int)((unsigned char *)stripped_addr)[j]);
- }
+ BITSFUNC(copy)(outfile, stripped_addr, stripped_len);
fprintf(outfile, "\n};\n\n");
+ if (extable_sec)
+ BITSFUNC(extract)(raw_addr, raw_len, outfile,
+ extable_sec, "extable");
fprintf(outfile, "const struct vdso_image %s = {\n", name);
fprintf(outfile, "\t.data = raw_data,\n");
@@ -166,6 +200,14 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
fprintf(outfile, "\t.alt_len = %lu,\n",
(unsigned long)GET_LE(&alt_sec->sh_size));
}
+ if (extable_sec) {
+ fprintf(outfile, "\t.extable_base = %lu,\n",
+ (unsigned long)GET_LE(&extable_sec->sh_offset));
+ fprintf(outfile, "\t.extable_len = %lu,\n",
+ (unsigned long)GET_LE(&extable_sec->sh_size));
+ fprintf(outfile, "\t.extable = extable,\n");
+ }
+
for (i = 0; i < NSYMS; i++) {
if (required_syms[i].export && syms[i])
fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 27566e57e87d..1c8a6a8f7b59 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -15,6 +15,8 @@ struct vdso_image {
unsigned long size; /* Always a multiple of PAGE_SIZE */
unsigned long alt, alt_len;
+ unsigned long extable_base, extable_len;
+ const void *extable;
long sym_vvar_start; /* Negative offset to the vvar area */
@@ -45,6 +47,9 @@ extern void __init init_vdso_image(const struct vdso_image *image);
extern int map_vdso_once(const struct vdso_image *image, unsigned long addr);
+extern bool fixup_vdso_exception(struct pt_regs *regs, int trapnr,
+ unsigned long error_code,
+ unsigned long fault_addr);
#endif /* __ASSEMBLER__ */
#endif /* _ASM_X86_VDSO_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 20/28] x86/fault: Add helper function to sanitize error code
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (18 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 19/28] x86/vdso: Add support for exception fixup in vDSO functions Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 21/28] x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling Jarkko Sakkinen
` (13 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes
From: Sean Christopherson <sean.j.christopherson@intel.com>
...to prepare for vDSO exception fixup, which will expose the error code
to userspace and runs before set_signal_archinfo(), i.e. suppresses the
signal when fixup is successful.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/mm/fault.c | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 78e2807fbede..5babd515f616 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -719,6 +719,18 @@ pgtable_bad(struct pt_regs *regs, unsigned long error_code,
oops_end(flags, regs, sig);
}
+static void sanitize_error_code(unsigned long address,
+ unsigned long *error_code)
+{
+ /*
+ * To avoid leaking information about the kernel page
+ * table layout, pretend that user-mode accesses to
+ * kernel addresses are always protection faults.
+ */
+ if (address >= TASK_SIZE_MAX)
+ *error_code |= X86_PF_PROT;
+}
+
static void set_signal_archinfo(unsigned long address,
unsigned long error_code)
{
@@ -771,6 +783,8 @@ no_context(struct pt_regs *regs, unsigned long error_code,
* faulting through the emulate_vsyscall() logic.
*/
if (current->thread.sig_on_uaccess_err && signal) {
+ sanitize_error_code(address, &error_code);
+
set_signal_archinfo(address, error_code);
/* XXX: hwpoison faults will set the wrong code. */
@@ -920,13 +934,7 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
if (is_errata100(regs, address))
return;
- /*
- * To avoid leaking information about the kernel page table
- * layout, pretend that user-mode accesses to kernel addresses
- * are always protection faults.
- */
- if (address >= TASK_SIZE_MAX)
- error_code |= X86_PF_PROT;
+ sanitize_error_code(address, &error_code);
if (likely(show_unhandled_signals))
show_signal_msg(regs, error_code, address, tsk);
@@ -1045,6 +1053,8 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,
if (is_prefetch(regs, error_code, address))
return;
+ sanitize_error_code(address, &error_code);
+
set_signal_archinfo(address, error_code);
#ifdef CONFIG_MEMORY_FAILURE
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 21/28] x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (19 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 20/28] x86/fault: Add helper function to sanitize error code Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 22/28] x86/traps: Attempt to fixup exceptions " Jarkko Sakkinen
` (12 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Andy Lutomirski, Jarkko Sakkinen, Dave Hansen
From: Sean Christopherson <sean.j.christopherson@intel.com>
vDSO functions can now leverage an exception fixup mechanism similar to
kernel exception fixup. For vDSO exception fixup, the initial user is
Intel's Software Guard Extensions (SGX), which will wrap the low-level
transitions to/from the enclave, i.e. EENTER and ERESUME instructions,
in a vDSO function and leverage fixup to intercept exceptions that would
otherwise generate a signal. This allows the vDSO wrapper to return the
fault information directly to its caller, obviating the need for SGX
applications and libraries to juggle signal handlers.
Attempt to fixup vDSO exceptions immediately prior to populating and
sending signal information. Except for the delivery mechanism, an
exception in a vDSO function should be treated like any other exception
in userspace, e.g. any fault that is successfully handled by the kernel
should not be directly visible to userspace.
Suggested-by: Andy Lutomirski <luto@amacapital.net>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/mm/fault.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 5babd515f616..13be0c67379e 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -28,6 +28,7 @@
#include <asm/mmu_context.h> /* vma_pkey() */
#include <asm/efi.h> /* efi_recover_from_page_fault()*/
#include <asm/desc.h> /* store_idt(), ... */
+#include <asm/vdso.h> /* fixup_vdso_exception() */
#define CREATE_TRACE_POINTS
#include <asm/trace/exceptions.h>
@@ -936,6 +937,9 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
sanitize_error_code(address, &error_code);
+ if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address))
+ return;
+
if (likely(show_unhandled_signals))
show_signal_msg(regs, error_code, address, tsk);
@@ -1055,6 +1059,9 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,
sanitize_error_code(address, &error_code);
+ if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address))
+ return;
+
set_signal_archinfo(address, error_code);
#ifdef CONFIG_MEMORY_FAILURE
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 22/28] x86/traps: Attempt to fixup exceptions in vDSO before signaling
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (20 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 21/28] x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-06-25 15:43 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 23/28] x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave transitions Jarkko Sakkinen
` (11 subsequent siblings)
33 siblings, 1 reply; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Andy Lutomirski, Jarkko Sakkinen, Dave Hansen
From: Sean Christopherson <sean.j.christopherson@intel.com>
vDSO functions can now leverage an exception fixup mechanism similar to
kernel exception fixup. For vDSO exception fixup, the initial user is
Intel's Software Guard Extensions (SGX), which will wrap the low-level
transitions to/from the enclave, i.e. EENTER and ERESUME instructions,
in a vDSO function and leverage fixup to intercept exceptions that would
otherwise generate a signal. This allows the vDSO wrapper to return the
fault information directly to its caller, obviating the need for SGX
applications and libraries to juggle signal handlers.
Attempt to fixup vDSO exceptions immediately prior to populating and
sending signal information. Except for the delivery mechanism, an
exception in a vDSO function should be treated like any other exception
in userspace, e.g. any fault that is successfully handled by the kernel
should not be directly visible to userspace.
Although it's debatable whether or not all exceptions are of interest to
enclaves, defer to the vDSO fixup to decide whether to do fixup or
generate a signal. Future users of vDSO fixup, if there ever are any,
will undoubtedly have different requirements than SGX enclaves, e.g. the
fixup vs. signal logic can be made function specific if/when necessary.
Suggested-by: Andy Lutomirski <luto@amacapital.net>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/kernel/traps.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index d26f9e9c3d83..02eda456c119 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -61,6 +61,7 @@
#include <asm/mpx.h>
#include <asm/vm86.h>
#include <asm/umip.h>
+#include <asm/vdso.h>
#ifdef CONFIG_X86_64
#include <asm/x86_init.h>
@@ -210,6 +211,9 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str,
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = trapnr;
die(str, regs, error_code);
+ } else {
+ if (fixup_vdso_exception(regs, trapnr, error_code, 0))
+ return 0;
}
/*
@@ -561,6 +565,9 @@ do_general_protection(struct pt_regs *regs, long error_code)
return;
}
+ if (fixup_vdso_exception(regs, X86_TRAP_GP, error_code, 0))
+ return;
+
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = X86_TRAP_GP;
@@ -775,6 +782,10 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
SIGTRAP) == NOTIFY_STOP)
goto exit;
+ if (user_mode(regs) &&
+ fixup_vdso_exception(regs, X86_TRAP_DB, error_code, 0))
+ goto exit;
+
/*
* Let others (NMI) know that the debug stack is in use
* as we may switch to the interrupt stack.
@@ -855,6 +866,9 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
if (!si_code)
return;
+ if (fixup_vdso_exception(regs, trapnr, error_code, 0))
+ return;
+
force_sig_fault(SIGFPE, si_code,
(void __user *)uprobe_get_trap_addr(regs), task);
}
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 23/28] x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave transitions
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (21 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 22/28] x86/traps: Attempt to fixup exceptions " Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 24/28] selftests/x86: Add a selftest for SGX Jarkko Sakkinen
` (10 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Andy Lutomirski, Jarkko Sakkinen, Dave Hansen, Haitao Huang,
Jethro Beekman, Dr . Greg Wettstein
From: Sean Christopherson <sean.j.christopherson@intel.com>
Intel Software Guard Extensions (SGX) introduces a new CPL3-only enclave
mode that runs as a sort of black box shared object that is hosted by an
untrusted normal CPL3 process.
Skipping over a great deal of gory architecture details[1], SGX was
designed in such a way that the host process can utilize a library to
build, launch and run an enclave. This is roughly analogous to how
e.g. libc implementations are used by most applications so that the
application can focus on its business logic.
The big gotcha is that because enclaves can generate *and* handle
exceptions, any SGX library must be prepared to handle nearly any
exception at any time (well, any time a thread is executing in an
enclave). In Linux, this means the SGX library must register a
signal handler in order to intercept relevant exceptions and forward
them to the enclave (or in some cases, take action on behalf of the
enclave). Unfortunately, Linux's signal mechanism doesn't mesh well
with libraries, e.g. signal handlers are process wide, are difficult
to chain, etc... This becomes particularly nasty when using multiple
levels of libraries that register signal handlers, e.g. running an
enclave via cgo inside of the Go runtime.
In comes vDSO to save the day. Now that vDSO can fixup exceptions,
add a function, __vdso_sgx_enter_enclave(), to wrap enclave transitions
and intercept any exceptions that occur when running the enclave.
__vdso_sgx_enter_enclave() does NOT adhere to the x86-64 ABI and instead
uses a custom calling convention. The primary motivation is to avoid
issues that arise due to asynchronous enclave exits. The x86-64 ABI
requires that EFLAGS.DF, MXCSR and FCW be preserved by the callee, and
unfortunately for the vDSO, the aformentioned registers/bits are not
restored after an asynchronous exit, e.g. EFLAGS.DF is in an unknown
state while MXCSR and FCW are reset to their init values. So the vDSO
cannot simply pass the buck by requiring enclaves to adhere to the
x86-64 ABI. That leaves three somewhat reasonable options:
1) Save/restore non-volatile GPRs, MXCSR and FCW, and clear EFLAGS.DF
+ 100% compliant with the x86-64 ABI
+ Callable from any code
+ Minimal documentation required
- Restoring MXCSR/FCW is likely unnecessary 99% of the time
- Slow
2) Save/restore non-volatile GPRs and clear EFLAGS.DF
+ Mostly compliant with the x86-64 ABI
+ Callable from any code that doesn't use SIMD registers
- Need to document deviations from x86-64 ABI, i.e. MXCSR and FCW
3) Require the caller to save/restore everything.
+ Fast
+ Userspace can pass all GPRs to the enclave (minus EAX, RBX and RCX)
- Custom ABI
- For all intents and purposes must be called from an assembly wrapper
__vdso_sgx_enter_enclave() implements option (3). The custom ABI is
mostly a documentation issue, and even that is offset by the fact that
being more similar to hardware's ENCLU[EENTER/ERESUME] ABI reduces the
amount of documentation needed for the vDSO, e.g. options (2) and (3)
would need to document which registers are marshalled to/from enclaves.
Requiring an assembly wrapper imparts minimal pain on userspace as SGX
libraries and/or applications need a healthy chunk of assembly, e.g. in
the enclave, regardless of the vDSO's implementation.
Note, the C-like pseudocode describing the assembly routine is wrapped
in a non-existent macro instead of in a comment to trick kernel-doc into
auto-parsing the documentation and function prototype. This is a double
win as the pseudocode is intended to aid kernel developers, not userland
enclave developers.
[1] Documentation/x86/sgx/1.Architecture.rst
Suggested-by: Andy Lutomirski <luto@amacapital.net>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Cc: Haitao Huang <haitao.huang@linux.intel.com>
Cc: Jethro Beekman <jethro@fortanix.com>
Cc: Dr. Greg Wettstein <greg@enjellic.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
arch/x86/entry/vdso/Makefile | 2 +
arch/x86/entry/vdso/vdso.lds.S | 1 +
arch/x86/entry/vdso/vsgx_enter_enclave.S | 101 +++++++++++++++++++++++
arch/x86/include/uapi/asm/sgx.h | 18 ++++
4 files changed, 122 insertions(+)
create mode 100644 arch/x86/entry/vdso/vsgx_enter_enclave.S
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 34bcf87d358c..fb5b9960b192 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -18,6 +18,7 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y
# files to link into the vdso
vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
+vobjs-$(VDSO64-y) += vsgx_enter_enclave.o
# files to link into kernel
obj-y += vma.o extable.o
@@ -85,6 +86,7 @@ CFLAGS_REMOVE_vdso-note.o = -pg
CFLAGS_REMOVE_vclock_gettime.o = -pg
CFLAGS_REMOVE_vgetcpu.o = -pg
CFLAGS_REMOVE_vvar.o = -pg
+CFLAGS_REMOVE_vsgx_enter_enclave.o = -pg
#
# X32 processes use x32 vDSO to access 64bit kernel data.
diff --git a/arch/x86/entry/vdso/vdso.lds.S b/arch/x86/entry/vdso/vdso.lds.S
index d3a2dce4cfa9..50952a995a6c 100644
--- a/arch/x86/entry/vdso/vdso.lds.S
+++ b/arch/x86/entry/vdso/vdso.lds.S
@@ -25,6 +25,7 @@ VERSION {
__vdso_getcpu;
time;
__vdso_time;
+ __vdso_sgx_enter_enclave;
local: *;
};
}
diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S
new file mode 100644
index 000000000000..fe0bf6671d6d
--- /dev/null
+++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/linkage.h>
+#include <asm/export.h>
+#include <asm/errno.h>
+
+#include "extable.h"
+
+#define EX_LEAF 0*8
+#define EX_TRAPNR 0*8+4
+#define EX_ERROR_CODE 0*8+6
+#define EX_ADDRESS 1*8
+
+.code64
+.section .text, "ax"
+
+#ifdef SGX_KERNEL_DOC
+/**
+ * __vdso_sgx_enter_enclave() - Enter an SGX enclave
+ *
+ * @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME
+ * @tcs: **IN \%rbx** - TCS, must be non-NULL
+ * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception' pointer
+ *
+ * Return:
+ * **OUT \%eax** -
+ * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is
+ * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave faults
+ *
+ * **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant with the
+ * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
+ * input parameters must be passed via ``%eax``, ``%rbx`` and ``%rcx``, with
+ * the return value passed via ``%eax``. All registers except ``%rsp`` must
+ * be treated as volatile from the caller's perspective, including but not
+ * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave
+ * being run **must** preserve the untrusted ``%rsp`` and stack.
+ */
+__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
+ struct sgx_enclave_exception *ex_info)
+{
+ if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
+ return -EINVAL;
+
+ if (!tcs)
+ return -EINVAL;
+
+ try {
+ ENCLU[leaf];
+ } catch (exception) {
+ if (e)
+ *e = exception;
+ return -EFAULT;
+ }
+
+ return 0;
+}
+#endif
+ENTRY(__vdso_sgx_enter_enclave)
+ /* EENTER <= leaf <= ERESUME */
+ cmp $0x2, %eax
+ jb bad_input
+
+ cmp $0x3, %eax
+ ja bad_input
+
+ /* TCS must be non-NULL */
+ test %rbx, %rbx
+ je bad_input
+
+ /* Save @exception_info */
+ push %rcx
+
+ /* Load AEP for ENCLU */
+ lea 1f(%rip), %rcx
+1: enclu
+
+ add $0x8, %rsp
+ xor %eax, %eax
+ ret
+
+bad_input:
+ mov $(-EINVAL), %rax
+ ret
+
+.pushsection .fixup, "ax"
+ /* Re-load @exception_info and fill it (if it's non-NULL) */
+2: pop %rcx
+ test %rcx, %rcx
+ je 3f
+
+ mov %eax, EX_LEAF(%rcx)
+ mov %di, EX_TRAPNR(%rcx)
+ mov %si, EX_ERROR_CODE(%rcx)
+ mov %rdx, EX_ADDRESS(%rcx)
+3: mov $(-EFAULT), %rax
+ ret
+.popsection
+
+_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
+
+ENDPROC(__vdso_sgx_enter_enclave)
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index 3b80acde8671..9ed690a38c70 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -65,4 +65,22 @@ struct sgx_enclave_set_attribute {
__u64 attribute_fd;
};
+/**
+ * struct sgx_enclave_exception - structure to report exceptions encountered in
+ * __vdso_sgx_enter_enclave()
+ *
+ * @leaf: ENCLU leaf from \%eax at time of exception
+ * @trapnr: exception trap number, a.k.a. fault vector
+ * @error_code: exception error code
+ * @address: exception address, e.g. CR2 on a #PF
+ * @reserved: reserved for future use
+ */
+struct sgx_enclave_exception {
+ __u32 leaf;
+ __u16 trapnr;
+ __u16 error_code;
+ __u64 address;
+ __u64 reserved[2];
+};
+
#endif /* _UAPI_ASM_X86_SGX_H */
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 24/28] selftests/x86: Add a selftest for SGX
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (22 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 23/28] x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave transitions Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 25/28] x86/sgx: Update MAINTAINERS Jarkko Sakkinen
` (9 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
Add a selftest for SGX. It is a trivial test where a simple enclave
copies one 64-bit word of memory between two memory locations given to
the enclave as arguments.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
tools/testing/selftests/x86/Makefile | 10 +
tools/testing/selftests/x86/sgx/Makefile | 48 ++
tools/testing/selftests/x86/sgx/defines.h | 39 ++
tools/testing/selftests/x86/sgx/encl.c | 20 +
tools/testing/selftests/x86/sgx/encl.lds | 33 ++
.../selftests/x86/sgx/encl_bootstrap.S | 94 ++++
tools/testing/selftests/x86/sgx/encl_piggy.S | 18 +
tools/testing/selftests/x86/sgx/encl_piggy.h | 14 +
tools/testing/selftests/x86/sgx/main.c | 279 ++++++++++
tools/testing/selftests/x86/sgx/sgx_call.S | 15 +
tools/testing/selftests/x86/sgx/sgxsign.c | 508 ++++++++++++++++++
.../testing/selftests/x86/sgx/signing_key.pem | 39 ++
12 files changed, 1117 insertions(+)
create mode 100644 tools/testing/selftests/x86/sgx/Makefile
create mode 100644 tools/testing/selftests/x86/sgx/defines.h
create mode 100644 tools/testing/selftests/x86/sgx/encl.c
create mode 100644 tools/testing/selftests/x86/sgx/encl.lds
create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S
create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.S
create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h
create mode 100644 tools/testing/selftests/x86/sgx/main.c
create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c
create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 186520198de7..4fc9a42f56ea 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -1,4 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
+
+SUBDIRS_64 := sgx
+
all:
include ../lib.mk
@@ -67,6 +70,13 @@ all_32: $(BINARIES_32)
all_64: $(BINARIES_64)
+all_64: $(SUBDIRS_64)
+ @for DIR in $(SUBDIRS_64); do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ mkdir $$BUILD_TARGET -p; \
+ make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+ done
+
EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64)
$(BINARIES_32): $(OUTPUT)/%_32: %.c
diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile
new file mode 100644
index 000000000000..1fd6f2708e81
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/Makefile
@@ -0,0 +1,48 @@
+top_srcdir = ../../../../..
+
+include ../../lib.mk
+
+HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC
+ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \
+ -fno-stack-protector -mrdrnd $(INCLUDES)
+
+TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx
+all_64: $(TEST_CUSTOM_PROGS)
+
+$(TEST_CUSTOM_PROGS): $(OUTPUT)/main.o $(OUTPUT)/sgx_call.o \
+ $(OUTPUT)/encl_piggy.o
+ $(CC) $(HOST_CFLAGS) -o $@ $^
+
+$(OUTPUT)/main.o: main.c
+ $(CC) $(HOST_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/sgx_call.o: sgx_call.S
+ $(CC) $(HOST_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl_piggy.o: $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+ $(CC) $(HOST_CFLAGS) -c encl_piggy.S -o $@
+
+$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf $(OUTPUT)/sgxsign
+ objcopy --remove-section=.got.plt -O binary $< $@
+
+$(OUTPUT)/encl.elf: $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o
+ $(CC) $(ENCL_CFLAGS) -T encl.lds -o $@ $^
+
+$(OUTPUT)/encl.o: encl.c
+ $(CC) $(ENCL_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl_bootstrap.o: encl_bootstrap.S
+ $(CC) $(ENCL_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl.ss: $(OUTPUT)/encl.bin $(OUTPUT)/sgxsign
+ $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+
+$(OUTPUT)/sgxsign: sgxsign.c
+ $(CC) -o $@ $< -lcrypto
+
+EXTRA_CLEAN := $(OUTPUT)/sgx-selftest $(OUTPUT)/sgx-selftest.o \
+ $(OUTPUT)/sgx_call.o $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss \
+ $(OUTPUT)/encl.elf $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o \
+ $(OUTPUT)/sgxsign
+
+.PHONY: clean
diff --git a/tools/testing/selftests/x86/sgx/defines.h b/tools/testing/selftests/x86/sgx/defines.h
new file mode 100644
index 000000000000..3ff73a9d9b93
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/defines.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2016-19 Intel Corporation.
+ */
+
+#ifndef TYPES_H
+#define TYPES_H
+
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+#define __aligned(x) __attribute__((__aligned__(x)))
+#define __packed __attribute__((packed))
+
+/* Derived from asm-generic/bitsperlong.h. */
+#if __x86_64__
+#define BITS_PER_LONG 64
+#else
+#define BITS_PER_LONG 32
+#endif
+#define BITS_PER_LONG_LONG 64
+
+/* Taken from linux/bits.h. */
+#define BIT(nr) (1UL << (nr))
+#define BIT_ULL(nr) (1ULL << (nr))
+#define GENMASK(h, l) \
+ (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+#define GENMASK_ULL(h, l) \
+ (((~0ULL) - (1ULL << (l)) + 1) & \
+ (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
+
+#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h"
+#include "../../../../../arch/x86/include/uapi/asm/sgx.h"
+
+#endif /* TYPES_H */
diff --git a/tools/testing/selftests/x86/sgx/encl.c b/tools/testing/selftests/x86/sgx/encl.c
new file mode 100644
index 000000000000..ede915399742
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <stddef.h>
+#include "defines.h"
+
+static void *memcpy(void *dest, const void *src, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ ((char *)dest)[i] = ((char *)src)[i];
+
+ return dest;
+}
+
+void encl_body(void *rdi, void *rsi)
+{
+ memcpy(rsi, rdi, 8);
+}
diff --git a/tools/testing/selftests/x86/sgx/encl.lds b/tools/testing/selftests/x86/sgx/encl.lds
new file mode 100644
index 000000000000..2ee01ac3ec79
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl.lds
@@ -0,0 +1,33 @@
+OUTPUT_FORMAT(elf64-x86-64)
+
+SECTIONS
+{
+ . = 0;
+ .tcs : {
+ *(.tcs*)
+ }
+
+ . = ALIGN(4096);
+ .text : {
+ *(.text*)
+ *(.rodata*)
+ }
+
+ . = ALIGN(4096);
+ .data : {
+ *(.data*)
+ }
+
+ /DISCARD/ : {
+ *(.data*)
+ *(.comment*)
+ *(.note*)
+ *(.debug*)
+ *(.eh_frame*)
+ }
+}
+
+ASSERT(!DEFINED(.altinstructions), "ALTERNATIVES are not supported in enclaves")
+ASSERT(!DEFINED(.altinstr_replacement), "ALTERNATIVES are not supported in enclaves")
+ASSERT(!DEFINED(.discard.retpoline_safe), "RETPOLINE ALTERNATIVES are not supported in enclaves")
+ASSERT(!DEFINED(.discard.nospec), "RETPOLINE ALTERNATIVES are not supported in enclaves")
diff --git a/tools/testing/selftests/x86/sgx/encl_bootstrap.S b/tools/testing/selftests/x86/sgx/encl_bootstrap.S
new file mode 100644
index 000000000000..d07f970ccdf9
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl_bootstrap.S
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+ .macro ENCLU
+ .byte 0x0f, 0x01, 0xd7
+ .endm
+
+ .section ".tcs", "a"
+ .balign 4096
+
+ .fill 1, 8, 0 # STATE (set by CPU)
+ .fill 1, 8, 0 # FLAGS
+ .quad encl_ssa # OSSA
+ .fill 1, 4, 0 # CSSA (set by CPU)
+ .fill 1, 4, 1 # NSSA
+ .quad encl_entry # OENTRY
+ .fill 1, 8, 0 # AEP (set by EENTER and ERESUME)
+ .fill 1, 8, 0 # OFSBASE
+ .fill 1, 8, 0 # OGSBASE
+ .fill 1, 4, 0xFFFFFFFF # FSLIMIT
+ .fill 1, 4, 0xFFFFFFFF # GSLIMIT
+ .fill 4024, 1, 0 # Reserved
+
+ .text
+
+encl_entry:
+ # RBX contains the base address for TCS, which is also the first address
+ # inside the enclave. By adding the value of le_stack_end to it, we get
+ # the absolute address for the stack.
+ lea (encl_stack)(%rbx), %rax
+ xchg %rsp, %rax
+ push %rax
+
+ push %rcx # push the address after EENTER
+ push %rbx # push the enclave base address
+
+ call encl_body
+
+ pop %rbx # pop the enclave base address
+
+ # Restore XSAVE registers to a synthetic state.
+ mov $0xFFFFFFFF, %rax
+ mov $0xFFFFFFFF, %rdx
+ lea (xsave_area)(%rbx), %rdi
+ fxrstor (%rdi)
+
+ # Clear GPRs.
+ xor %rcx, %rcx
+ xor %rdx, %rdx
+ xor %rdi, %rdi
+ xor %rsi, %rsi
+ xor %r8, %r8
+ xor %r9, %r9
+ xor %r10, %r10
+ xor %r11, %r11
+ xor %r12, %r12
+ xor %r13, %r13
+ xor %r14, %r14
+ xor %r15, %r15
+
+ # Reset status flags.
+ add %rdx, %rdx # OF = SF = AF = CF = 0; ZF = PF = 1
+
+ # Prepare EEXIT target by popping the address of the instruction after
+ # EENTER to RBX.
+ pop %rbx
+
+ # Restore the caller stack.
+ pop %rax
+ mov %rax, %rsp
+
+ # EEXIT
+ mov $4, %rax
+ enclu
+
+ .section ".data", "aw"
+
+encl_ssa:
+ .space 4096
+
+xsave_area:
+ .fill 1, 4, 0x037F # FCW
+ .fill 5, 4, 0
+ .fill 1, 4, 0x1F80 # MXCSR
+ .fill 1, 4, 0xFFFF # MXCSR_MASK
+ .fill 123, 4, 0
+ .fill 1, 4, 0x80000000 # XCOMP_BV[63] = 1, compaction mode
+ .fill 12, 4, 0
+
+ .balign 4096
+ .space 8192
+encl_stack:
diff --git a/tools/testing/selftests/x86/sgx/encl_piggy.S b/tools/testing/selftests/x86/sgx/encl_piggy.S
new file mode 100644
index 000000000000..542001658afb
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl_piggy.S
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+ .section ".rodata", "a"
+
+encl_bin:
+ .globl encl_bin
+ .incbin "encl.bin"
+encl_bin_end:
+ .globl encl_bin_end
+
+encl_ss:
+ .globl encl_ss
+ .incbin "encl.ss"
+encl_ss_end:
+ .globl encl_ss_end
diff --git a/tools/testing/selftests/x86/sgx/encl_piggy.h b/tools/testing/selftests/x86/sgx/encl_piggy.h
new file mode 100644
index 000000000000..ee8224f8cc8d
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl_piggy.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+#ifndef ENCL_PIGGY_H
+#define ENCL_PIGGY_H
+
+extern unsigned char encl_bin[];
+extern unsigned char encl_bin_end[];
+extern unsigned char encl_ss[];
+extern unsigned char encl_ss_end[];
+
+#endif /* ENCL_PIGGY_H */
diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c
new file mode 100644
index 000000000000..e2265f841fb0
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/main.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <elf.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "encl_piggy.h"
+#include "defines.h"
+#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h"
+#include "../../../../../arch/x86/include/uapi/asm/sgx.h"
+
+static const uint64_t MAGIC = 0x1122334455667788ULL;
+
+struct vdso_symtab {
+ Elf64_Sym *elf_symtab;
+ const char *elf_symstrtab;
+ Elf64_Word *elf_hashtab;
+};
+
+static void *vdso_get_base_addr(char *envp[])
+{
+ Elf64_auxv_t *auxv;
+ int i;
+
+ for (i = 0; envp[i]; i++);
+ auxv = (Elf64_auxv_t *)&envp[i + 1];
+
+ for (i = 0; auxv[i].a_type != AT_NULL; i++) {
+ if (auxv[i].a_type == AT_SYSINFO_EHDR)
+ return (void *)auxv[i].a_un.a_val;
+ }
+
+ return NULL;
+}
+
+static Elf64_Dyn *vdso_get_dyntab(void *addr)
+{
+ Elf64_Ehdr *ehdr = addr;
+ Elf64_Phdr *phdrtab = addr + ehdr->e_phoff;
+ int i;
+
+ for (i = 0; i < ehdr->e_phnum; i++)
+ if (phdrtab[i].p_type == PT_DYNAMIC)
+ return addr + phdrtab[i].p_offset;
+
+ return NULL;
+}
+
+static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag)
+{
+ int i;
+
+ for (i = 0; dyntab[i].d_tag != DT_NULL; i++)
+ if (dyntab[i].d_tag == tag)
+ return addr + dyntab[i].d_un.d_ptr;
+
+ return NULL;
+}
+
+static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab)
+{
+ Elf64_Dyn *dyntab = vdso_get_dyntab(addr);
+
+ symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB);
+ if (!symtab->elf_symtab)
+ return false;
+
+ symtab->elf_symstrtab = vdso_get_dyn(addr, dyntab, DT_STRTAB);
+ if (!symtab->elf_symstrtab)
+ return false;
+
+ symtab->elf_hashtab = vdso_get_dyn(addr, dyntab, DT_HASH);
+ if (!symtab->elf_hashtab)
+ return false;
+
+ return true;
+}
+
+static unsigned long elf_sym_hash(const char *name)
+{
+ unsigned long h = 0, high;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ high = h & 0xf0000000;
+
+ if (high)
+ h ^= high >> 24;
+
+ h &= ~high;
+ }
+
+ return h;
+}
+
+static Elf64_Sym *vdso_symtab_get(struct vdso_symtab *symtab, const char *name)
+{
+ Elf64_Word bucketnum = symtab->elf_hashtab[0];
+ Elf64_Word *buckettab = &symtab->elf_hashtab[2];
+ Elf64_Word *chaintab = &symtab->elf_hashtab[2 + bucketnum];
+ Elf64_Sym *sym;
+ Elf64_Word i;
+
+ for (i = buckettab[elf_sym_hash(name) % bucketnum]; i != STN_UNDEF;
+ i = chaintab[i]) {
+ sym = &symtab->elf_symtab[i];
+ if (!strcmp(name, &symtab->elf_symstrtab[sym->st_name]))
+ return sym;
+ }
+
+ return NULL;
+}
+
+static bool encl_create(int dev_fd, unsigned long bin_size,
+ struct sgx_secs *secs)
+{
+ struct sgx_enclave_create ioc;
+ void *base;
+ int rc;
+
+ memset(secs, 0, sizeof(*secs));
+ secs->ssa_frame_size = 1;
+ secs->attributes = SGX_ATTR_MODE64BIT;
+ secs->xfrm = 3;
+
+ for (secs->size = 4096; secs->size < bin_size; )
+ secs->size <<= 1;
+
+ base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_SHARED, dev_fd, 0);
+ if (base == MAP_FAILED) {
+ perror("mmap");
+ return false;
+ }
+
+ secs->base = (uint64_t)base;
+
+ ioc.src = (unsigned long)secs;
+ rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_CREATE, &ioc);
+ if (rc) {
+ fprintf(stderr, "ECREATE failed rc=%d.\n", rc);
+ munmap(base, secs->size);
+ return false;
+ }
+
+ return true;
+}
+
+static bool encl_add_page(int dev_fd, unsigned long addr, void *data,
+ uint64_t flags)
+{
+ struct sgx_enclave_add_page ioc;
+ struct sgx_secinfo secinfo;
+ int rc;
+
+ memset(&secinfo, 0, sizeof(secinfo));
+ secinfo.flags = flags;
+
+ ioc.secinfo = (unsigned long)&secinfo;
+ ioc.mrmask = 0xFFFF;
+ ioc.addr = addr;
+ ioc.src = (uint64_t)data;
+
+ rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_ADD_PAGE, &ioc);
+ if (rc) {
+ fprintf(stderr, "EADD failed rc=%d.\n", rc);
+ return false;
+ }
+
+ return true;
+}
+
+static bool encl_load(struct sgx_secs *secs, unsigned long bin_size)
+{
+ struct sgx_enclave_init ioc;
+ uint64_t offset;
+ uint64_t flags;
+ int dev_fd;
+ int rc;
+
+ dev_fd = open("/dev/sgx/enclave", O_RDWR);
+ if (dev_fd < 0) {
+ fprintf(stderr, "Unable to open /dev/sgx\n");
+ return false;
+ }
+
+ if (!encl_create(dev_fd, bin_size, secs))
+ goto out_dev_fd;
+
+ for (offset = 0; offset < bin_size; offset += 0x1000) {
+ if (!offset)
+ flags = SGX_SECINFO_TCS;
+ else
+ flags = SGX_SECINFO_REG | SGX_SECINFO_R |
+ SGX_SECINFO_W | SGX_SECINFO_X;
+
+ if (!encl_add_page(dev_fd, secs->base + offset,
+ encl_bin + offset, flags))
+ goto out_map;
+ }
+
+ ioc.sigstruct = (uint64_t)&encl_ss;
+ rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_INIT, &ioc);
+ if (rc) {
+ printf("EINIT failed rc=%d\n", rc);
+ goto out_map;
+ }
+
+ close(dev_fd);
+ return true;
+out_map:
+ munmap((void *)secs->base, secs->size);
+out_dev_fd:
+ close(dev_fd);
+ return false;
+}
+
+void sgx_call(void *rdi, void *rsi, void *tcs,
+ struct sgx_enclave_exception *exception,
+ void *eenter);
+
+int main(int argc, char *argv[], char *envp[])
+{
+ unsigned long bin_size = encl_bin_end - encl_bin;
+ unsigned long ss_size = encl_ss_end - encl_ss;
+ struct sgx_enclave_exception exception;
+ Elf64_Sym *eenter_sym;
+ struct vdso_symtab symtab;
+ struct sgx_secs secs;
+ uint64_t result = 0;
+ void *eenter;
+ void *addr;
+
+ memset(&exception, 0, sizeof(exception));
+
+ addr = vdso_get_base_addr(envp);
+ if (!addr)
+ exit(1);
+
+ if (!vdso_get_symtab(addr, &symtab))
+ exit(1);
+
+ eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave");
+ if (!eenter_sym)
+ exit(1);
+ eenter = addr + eenter_sym->st_value;
+
+ printf("Binary size %lu (0x%lx), SIGSTRUCT size %lu\n", bin_size,
+ bin_size, ss_size);
+ if (ss_size != sizeof(struct sgx_sigstruct)) {
+ fprintf(stderr, "The size of SIGSTRUCT should be %lu\n",
+ sizeof(struct sgx_sigstruct));
+ exit(1);
+ }
+
+ printf("Loading the enclave.\n");
+ if (!encl_load(&secs, bin_size))
+ exit(1);
+
+ printf("Input: 0x%lx\n", MAGIC);
+ sgx_call((void *)&MAGIC, &result, (void *)secs.base, &exception,
+ eenter);
+ if (result != MAGIC) {
+ fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
+ exit(1);
+ }
+
+ printf("Output: 0x%lx\n", result);
+ exit(0);
+}
diff --git a/tools/testing/selftests/x86/sgx/sgx_call.S b/tools/testing/selftests/x86/sgx/sgx_call.S
new file mode 100644
index 000000000000..14bd0a044199
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgx_call.S
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+* Copyright(c) 2016-18 Intel Corporation.
+*/
+
+ .text
+
+ .global sgx_call
+sgx_call:
+ push %rbx
+ mov $0x02, %rax
+ mov %rdx, %rbx
+ call *%r8
+ pop %rbx
+ ret
diff --git a/tools/testing/selftests/x86/sgx/sgxsign.c b/tools/testing/selftests/x86/sgx/sgxsign.c
new file mode 100644
index 000000000000..0b89823fc703
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgxsign.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include "defines.h"
+
+struct sgx_sigstruct_payload {
+ struct sgx_sigstruct_header header;
+ struct sgx_sigstruct_body body;
+};
+
+static const char *sign_key_pass;
+
+static bool check_crypto_errors(void)
+{
+ int err;
+ bool had_errors = false;
+ const char *filename;
+ int line;
+ char str[256];
+
+ for ( ; ; ) {
+ if (ERR_peek_error() == 0)
+ break;
+
+ had_errors = true;
+ err = ERR_get_error_line(&filename, &line);
+ ERR_error_string_n(err, str, sizeof(str));
+ fprintf(stderr, "crypto: %s: %s:%d\n", str, filename, line);
+ }
+
+ return had_errors;
+}
+
+static void exit_usage(const char *program)
+{
+ fprintf(stderr,
+ "Usage: %s/sign-le <key> <enclave> <sigstruct>\n", program);
+ exit(1);
+}
+
+static int pem_passwd_cb(char *buf, int size, int rwflag, void *u)
+{
+ if (!sign_key_pass)
+ return -1;
+
+ strncpy(buf, sign_key_pass, size);
+ /* no retry */
+ sign_key_pass = NULL;
+
+ return strlen(buf) >= size ? size - 1 : strlen(buf);
+}
+
+static inline const BIGNUM *get_modulus(RSA *key)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ return key->n;
+#else
+ const BIGNUM *n;
+
+ RSA_get0_key(key, &n, NULL, NULL);
+ return n;
+#endif
+}
+
+static RSA *load_sign_key(const char *path)
+{
+ FILE *f;
+ RSA *key;
+
+ f = fopen(path, "rb");
+ if (!f) {
+ fprintf(stderr, "Unable to open %s\n", path);
+ return NULL;
+ }
+ key = RSA_new();
+ if (!PEM_read_RSAPrivateKey(f, &key, pem_passwd_cb, NULL))
+ return NULL;
+ fclose(f);
+
+ if (BN_num_bytes(get_modulus(key)) != SGX_MODULUS_SIZE) {
+ fprintf(stderr, "Invalid key size %d\n",
+ BN_num_bytes(get_modulus(key)));
+ RSA_free(key);
+ return NULL;
+ }
+
+ return key;
+}
+
+static void reverse_bytes(void *data, int length)
+{
+ int i = 0;
+ int j = length - 1;
+ uint8_t temp;
+ uint8_t *ptr = data;
+
+ while (i < j) {
+ temp = ptr[i];
+ ptr[i] = ptr[j];
+ ptr[j] = temp;
+ i++;
+ j--;
+ }
+}
+
+enum mrtags {
+ MRECREATE = 0x0045544145524345,
+ MREADD = 0x0000000044444145,
+ MREEXTEND = 0x00444E4554584545,
+};
+
+static bool mrenclave_update(EVP_MD_CTX *ctx, const void *data)
+{
+ if (!EVP_DigestUpdate(ctx, data, 64)) {
+ fprintf(stderr, "digest update failed\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool mrenclave_commit(EVP_MD_CTX *ctx, uint8_t *mrenclave)
+{
+ unsigned int size;
+
+ if (!EVP_DigestFinal_ex(ctx, (unsigned char *)mrenclave, &size)) {
+ fprintf(stderr, "digest commit failed\n");
+ return false;
+ }
+
+ if (size != 32) {
+ fprintf(stderr, "invalid digest size = %u\n", size);
+ return false;
+ }
+
+ return true;
+}
+
+struct mrecreate {
+ uint64_t tag;
+ uint32_t ssaframesize;
+ uint64_t size;
+ uint8_t reserved[44];
+} __attribute__((__packed__));
+
+
+static bool mrenclave_ecreate(EVP_MD_CTX *ctx, uint64_t blob_size)
+{
+ struct mrecreate mrecreate;
+ uint64_t encl_size;
+
+ for (encl_size = 0x1000; encl_size < blob_size; )
+ encl_size <<= 1;
+
+ memset(&mrecreate, 0, sizeof(mrecreate));
+ mrecreate.tag = MRECREATE;
+ mrecreate.ssaframesize = 1;
+ mrecreate.size = encl_size;
+
+ if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL))
+ return false;
+
+ return mrenclave_update(ctx, &mrecreate);
+}
+
+struct mreadd {
+ uint64_t tag;
+ uint64_t offset;
+ uint64_t flags; /* SECINFO flags */
+ uint8_t reserved[40];
+} __attribute__((__packed__));
+
+static bool mrenclave_eadd(EVP_MD_CTX *ctx, uint64_t offset, uint64_t flags)
+{
+ struct mreadd mreadd;
+
+ memset(&mreadd, 0, sizeof(mreadd));
+ mreadd.tag = MREADD;
+ mreadd.offset = offset;
+ mreadd.flags = flags;
+
+ return mrenclave_update(ctx, &mreadd);
+}
+
+struct mreextend {
+ uint64_t tag;
+ uint64_t offset;
+ uint8_t reserved[48];
+} __attribute__((__packed__));
+
+static bool mrenclave_eextend(EVP_MD_CTX *ctx, uint64_t offset, uint8_t *data)
+{
+ struct mreextend mreextend;
+ int i;
+
+ for (i = 0; i < 0x1000; i += 0x100) {
+ memset(&mreextend, 0, sizeof(mreextend));
+ mreextend.tag = MREEXTEND;
+ mreextend.offset = offset + i;
+
+ if (!mrenclave_update(ctx, &mreextend))
+ return false;
+
+ if (!mrenclave_update(ctx, &data[i + 0x00]))
+ return false;
+
+ if (!mrenclave_update(ctx, &data[i + 0x40]))
+ return false;
+
+ if (!mrenclave_update(ctx, &data[i + 0x80]))
+ return false;
+
+ if (!mrenclave_update(ctx, &data[i + 0xC0]))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * measure_encl - measure enclave
+ * @path: path to the enclave
+ * @mrenclave: measurement
+ *
+ * Calculates MRENCLAVE. Assumes that the very first page is a TCS page and
+ * following pages are regular pages. Does not measure the contents of the
+ * enclave as the signing tool is used at the moment only for the launch
+ * enclave, which is pass-through (everything gets a token).
+ */
+static bool measure_encl(const char *path, uint8_t *mrenclave)
+{
+ FILE *file;
+ struct stat sb;
+ EVP_MD_CTX *ctx;
+ uint64_t flags;
+ uint64_t offset;
+ uint8_t data[0x1000];
+ int rc;
+
+ ctx = EVP_MD_CTX_create();
+ if (!ctx)
+ return false;
+
+ file = fopen(path, "rb");
+ if (!file) {
+ perror("fopen");
+ EVP_MD_CTX_destroy(ctx);
+ return false;
+ }
+
+ rc = stat(path, &sb);
+ if (rc) {
+ perror("stat");
+ goto out;
+ }
+
+ if (!sb.st_size || sb.st_size & 0xfff) {
+ fprintf(stderr, "Invalid blob size %lu\n", sb.st_size);
+ goto out;
+ }
+
+ if (!mrenclave_ecreate(ctx, sb.st_size))
+ goto out;
+
+ for (offset = 0; offset < sb.st_size; offset += 0x1000) {
+ if (!offset)
+ flags = SGX_SECINFO_TCS;
+ else
+ flags = SGX_SECINFO_REG | SGX_SECINFO_R |
+ SGX_SECINFO_W | SGX_SECINFO_X;
+
+ if (!mrenclave_eadd(ctx, offset, flags))
+ goto out;
+
+ rc = fread(data, 1, 0x1000, file);
+ if (!rc)
+ break;
+ if (rc < 0x1000)
+ goto out;
+
+ if (!mrenclave_eextend(ctx, offset, data))
+ goto out;
+ }
+
+ if (!mrenclave_commit(ctx, mrenclave))
+ goto out;
+
+ fclose(file);
+ EVP_MD_CTX_destroy(ctx);
+ return true;
+out:
+ fclose(file);
+ EVP_MD_CTX_destroy(ctx);
+ return false;
+}
+
+/**
+ * sign_encl - sign enclave
+ * @sigstruct: pointer to SIGSTRUCT
+ * @key: 3072-bit RSA key
+ * @signature: byte array for the signature
+ *
+ * Calculates EMSA-PKCSv1.5 signature for the given SIGSTRUCT. The result is
+ * stored in big-endian format so that it can be further passed to OpenSSL
+ * libcrypto functions.
+ */
+static bool sign_encl(const struct sgx_sigstruct *sigstruct, RSA *key,
+ uint8_t *signature)
+{
+ struct sgx_sigstruct_payload payload;
+ unsigned int siglen;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ bool ret;
+
+ memcpy(&payload.header, &sigstruct->header, sizeof(sigstruct->header));
+ memcpy(&payload.body, &sigstruct->body, sizeof(sigstruct->body));
+
+ SHA256((unsigned char *)&payload, sizeof(payload), digest);
+
+ ret = RSA_sign(NID_sha256, digest, SHA256_DIGEST_LENGTH, signature,
+ &siglen, key);
+
+ return ret;
+}
+
+struct q1q2_ctx {
+ BN_CTX *bn_ctx;
+ BIGNUM *m;
+ BIGNUM *s;
+ BIGNUM *q1;
+ BIGNUM *qr;
+ BIGNUM *q2;
+};
+
+static void free_q1q2_ctx(struct q1q2_ctx *ctx)
+{
+ BN_CTX_free(ctx->bn_ctx);
+ BN_free(ctx->m);
+ BN_free(ctx->s);
+ BN_free(ctx->q1);
+ BN_free(ctx->qr);
+ BN_free(ctx->q2);
+}
+
+static bool alloc_q1q2_ctx(const uint8_t *s, const uint8_t *m,
+ struct q1q2_ctx *ctx)
+{
+ ctx->bn_ctx = BN_CTX_new();
+ ctx->s = BN_bin2bn(s, SGX_MODULUS_SIZE, NULL);
+ ctx->m = BN_bin2bn(m, SGX_MODULUS_SIZE, NULL);
+ ctx->q1 = BN_new();
+ ctx->qr = BN_new();
+ ctx->q2 = BN_new();
+
+ if (!ctx->bn_ctx || !ctx->s || !ctx->m || !ctx->q1 || !ctx->qr ||
+ !ctx->q2) {
+ free_q1q2_ctx(ctx);
+ return false;
+ }
+
+ return true;
+}
+
+static bool calc_q1q2(const uint8_t *s, const uint8_t *m, uint8_t *q1,
+ uint8_t *q2)
+{
+ struct q1q2_ctx ctx;
+
+ if (!alloc_q1q2_ctx(s, m, &ctx)) {
+ fprintf(stderr, "Not enough memory for Q1Q2 calculation\n");
+ return false;
+ }
+
+ if (!BN_mul(ctx.q1, ctx.s, ctx.s, ctx.bn_ctx))
+ goto out;
+
+ if (!BN_div(ctx.q1, ctx.qr, ctx.q1, ctx.m, ctx.bn_ctx))
+ goto out;
+
+ if (BN_num_bytes(ctx.q1) > SGX_MODULUS_SIZE) {
+ fprintf(stderr, "Too large Q1 %d bytes\n",
+ BN_num_bytes(ctx.q1));
+ goto out;
+ }
+
+ if (!BN_mul(ctx.q2, ctx.s, ctx.qr, ctx.bn_ctx))
+ goto out;
+
+ if (!BN_div(ctx.q2, NULL, ctx.q2, ctx.m, ctx.bn_ctx))
+ goto out;
+
+ if (BN_num_bytes(ctx.q2) > SGX_MODULUS_SIZE) {
+ fprintf(stderr, "Too large Q2 %d bytes\n",
+ BN_num_bytes(ctx.q2));
+ goto out;
+ }
+
+ BN_bn2bin(ctx.q1, q1);
+ BN_bn2bin(ctx.q2, q2);
+
+ free_q1q2_ctx(&ctx);
+ return true;
+out:
+ free_q1q2_ctx(&ctx);
+ return false;
+}
+
+static bool save_sigstruct(const struct sgx_sigstruct *sigstruct,
+ const char *path)
+{
+ FILE *f = fopen(path, "wb");
+
+ if (!f) {
+ fprintf(stderr, "Unable to open %s\n", path);
+ return false;
+ }
+
+ fwrite(sigstruct, sizeof(*sigstruct), 1, f);
+ fclose(f);
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ uint64_t header1[2] = {0x000000E100000006, 0x0000000000010000};
+ uint64_t header2[2] = {0x0000006000000101, 0x0000000100000060};
+ struct sgx_sigstruct ss;
+ const char *program;
+ int opt;
+ RSA *sign_key;
+
+ memset(&ss, 0, sizeof(ss));
+ ss.header.header1[0] = header1[0];
+ ss.header.header1[1] = header1[1];
+ ss.header.header2[0] = header2[0];
+ ss.header.header2[1] = header2[1];
+ ss.exponent = 3;
+
+#ifndef CONFIG_EINITTOKENKEY
+ ss.body.attributes = SGX_ATTR_MODE64BIT;
+#else
+ ss.body.attributes = SGX_ATTR_MODE64BIT | SGX_ATTR_EINITTOKENKEY;
+#endif
+ ss.body.xfrm = 3,
+
+ sign_key_pass = getenv("KBUILD_SGX_SIGN_PIN");
+ program = argv[0];
+
+ do {
+ opt = getopt(argc, argv, "");
+ switch (opt) {
+ case -1:
+ break;
+ default:
+ exit_usage(program);
+ }
+ } while (opt != -1);
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3)
+ exit_usage(program);
+
+ /* sanity check only */
+ if (check_crypto_errors())
+ exit(1);
+
+ sign_key = load_sign_key(argv[0]);
+ if (!sign_key)
+ goto out;
+
+ BN_bn2bin(get_modulus(sign_key), ss.modulus);
+
+ if (!measure_encl(argv[1], ss.body.mrenclave))
+ goto out;
+
+ if (!sign_encl(&ss, sign_key, ss.signature))
+ goto out;
+
+ if (!calc_q1q2(ss.signature, ss.modulus, ss.q1, ss.q2))
+ goto out;
+
+ /* convert to little endian */
+ reverse_bytes(ss.signature, SGX_MODULUS_SIZE);
+ reverse_bytes(ss.modulus, SGX_MODULUS_SIZE);
+ reverse_bytes(ss.q1, SGX_MODULUS_SIZE);
+ reverse_bytes(ss.q2, SGX_MODULUS_SIZE);
+
+ if (!save_sigstruct(&ss, argv[2]))
+ goto out;
+ exit(0);
+out:
+ check_crypto_errors();
+ exit(1);
+}
diff --git a/tools/testing/selftests/x86/sgx/signing_key.pem b/tools/testing/selftests/x86/sgx/signing_key.pem
new file mode 100644
index 000000000000..d76f21f19187
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/signing_key.pem
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIG4wIBAAKCAYEApalGbq7Q+usM91CPtksu3D+b0Prc8gAFL6grM3mg85A5Bx8V
+cfMXPgtrw8EYFwQxDAvzZWwl+9VfOX0ECrFRBkOHcOiG0SnADN8+FLj1UiNUQwbp
+S6OzhNWuRcSbGraSOyUlVlV0yMQSvewyzGklOaXBe30AJqzIBc8QfdSxKuP8rs0Z
+ga6k/Bl73osrYKByILJTUUeZqjLERsE6GebsdzbWgKn8qVqng4ZS4yMNg6LeRlH3
++9CIPgg4jwpSLHcp7dq2qTIB9a0tGe9ayp+5FbucpB6U7ePold0EeRN6RlJGDF9k
+L93v8P5ykz5G5gYZ2g0K1X2sHIWV4huxPgv5PXgdyQYbK+6olqj0d5rjYuwX57Ul
+k6SroPS1U6UbdCjG5txM+BNGU0VpD0ZhrIRw0leQdnNcCO9sTJuInZrgYacSVJ7u
+mtB+uCt+uzUesc+l+xPRYA+9e14lLkZp7AAmo9FvL816XDI09deehJ3i/LmHKCRN
+tuqC5TprRjFwUr6dAgEDAoIBgG5w2Z8fNfycs0+LCnmHdJLVEotR6KFVWMpwHMz7
+wKJgJgS/Y6FMuilc8oKAuroCy11dTO5IGVKOP3uorVx2NgQtBPXwWeDGgAiU1A3Q
+o4wXjYIEm4fCd63jyYPYZ2ckYXzDbjmOTdstYdPyzIhGGNEZK6eoqsRzMAPfYFPj
+IMdCqHSIu6vJw1K7p+myHOsVoWshjODaZnF3LYSA0WaZ8vokjwBxUxuRxQJZjJds
+s60XPtmL+qfgWtQFewoG4XL6GuD8FcXccynRRtzrLtFNPIl9BQfWfjBBhTC1/Te1
+0Z6XbZvpdUTD9OfLB7SbR2OUFNpKQgriO0iYVdbW3cr7uu38Zwp4W1TX73DPjoi6
+KNooP6SGWd4mRJW2+dUmSYS4QNG8eVVZswKcploEIXlAKRsOe4kzJJ1iETugIe85
+uX8nd1WYEp65xwoRUg8hqng0MeyveVbXqNKuJG6tzNDt9kgFYo+hmC/oouAW2Dtc
+T9jdRAwKJXqA2Eg6OkgXCEv+kwKBwQDYaQiFMlFhsmLlqI+EzCUh7c941/cL7m6U
+7j98+8ngl0HgCEcrc10iJVCKakQW3YbPzAx3XkKTaGjWazvvrFarXIGlOud64B8a
+iWyQ7VdlnmZnNEdk+C83tI91OQeaTKqRLDGzKh29Ry/jL8Pcbazt+kDgxa0H7qJp
+roADUanLQuNkYubpbhFBh3xpa2EExaVq6rF7nIVsD8W9TrbmPKA4LgH7z0iy544D
+kVCNYsTjYDdUWP+WiSor8kCnnpjnN9sCgcEAw/eNezUD1UDf6OYFC9+5JZJFn4Tg
+mZMyN93JKIb199ffwnjtHUSjcyiWeesXucpzwtGbTcwQnDisSW4oneYKLSEBlBaq
+scqiUugyGZZOthFSCbdXYXMViK2vHrKlkse7GxVlROKcEhM/pRBrmjaGO8eWR+D4
+FO2wCXzVs3KgV6j779frw0vC54oHOxc9+Lu1rSHp4i+600koyvL/zF6U/5tZXIvN
+YW2yoiQJnjCmVA1pwbwV6KAUTPDTMnBK+YjnAoHBAJBGBa4hi5Z27JkbCliIGMFJ
+NPs6pLKe9GNJf6in2+sPgUAFhMeiPhbDiwbxgrnpBIqICE+ULGJFmzmc0p/IOceT
+ARjR76dAFLxbnbXzj5kURETNhO36yiUjCk4mBRGIcbYddndxaSjaH+zKgpLzyJ6m
+1esuc1qfFvEfAAI2cTIsl5hB70ZJYNZaUvDyQK3ZGPHxy6e9rkgKg9OJz0QoatAe
+q/002yHvtAJg4F5B2JeVejg7VQ8GHB1MKxppu0TP5wKBwQCCpQj8zgKOKz/wmViy
+lSYZDC5qWJW7t3bP6TDFr06lOpUsUJ4TgxeiGw778g/RMaKB4RIz3WBoJcgw9BsT
+7rFza1ZiucchMcGMmswRDt8kC4wGejpA92Owc8oUdxkMhSdnY5jYlxK2t3/DYEe8
+JFl9L7mFQKVjSSAGUzkiTGrlG1Kf5UfXh9dFBq98uilQfSPIwUaWynyM23CHTKqI
+Pw3/vOY9sojrnncWwrEUIG7is5vWfWPwargzSzd29YdRBe8CgcEAuRVewK/YeNOX
+B7ZG6gKKsfsvrGtY7FPETzLZAHjoVXYNea4LVZ2kn4hBXXlvw/4HD+YqcTt4wmif
+5JQlDvjNobUiKJZpzy7hklVhF7wZFl4pCF7Yh43q9iQ7gKTaeUG7MiaK+G8Zz8aY
+HW9rsiihbdZkccMvnPfO9334XMxl3HtBRzLstjUlbLB7Sdh+7tZ3JQidCOFNs5pE
+XyWwnASPu4tKfDahH1UUTp1uJcq/6716CSWg080avYxFcn75qqsb
+-----END RSA PRIVATE KEY-----
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 25/28] x86/sgx: Update MAINTAINERS
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (23 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 24/28] selftests/x86: Add a selftest for SGX Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 26/28] docs: x86/sgx: Add Architecture documentation Jarkko Sakkinen
` (8 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
Add the maintainer information for the SGX subsystem.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
MAINTAINERS | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3671fdea5010..eb3b80811653 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8044,6 +8044,18 @@ L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-intel-mid.c
+INTEL SGX
+M: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
+M: Sean Christopherson <sean.j.christopherson@intel.com>
+L: linux-sgx@vger.kernel.org
+S: Maintained
+Q: https://patchwork.kernel.org/project/intel-sgx/list/
+T: git https://github.com/jsakkine-intel/linux-sgx.git
+F: arch/x86/include/asm/sgx.h
+F: arch/x86/include/uapi/asm/sgx.h
+F: arch/x86/kernel/cpu/sgx/*
+K: \bSGX_
+
INTERCONNECT API
M: Georgi Djakov <georgi.djakov@linaro.org>
S: Maintained
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 26/28] docs: x86/sgx: Add Architecture documentation
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (24 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 25/28] x86/sgx: Update MAINTAINERS Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 27/28] docs: x86/sgx: Document kernel internals Jarkko Sakkinen
` (7 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 24263 bytes --]
From: Sean Christopherson <sean.j.christopherson@intel.com>
Document microarchitectural features of SGX relevant to the kernel.
They are documented in detail enough to understand the implementation.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
Documentation/index.rst | 1 +
Documentation/x86/index.rst | 10 +
Documentation/x86/sgx/1.Architecture.rst | 431 +++++++++++++++++++++++
Documentation/x86/sgx/index.rst | 16 +
4 files changed, 458 insertions(+)
create mode 100644 Documentation/x86/index.rst
create mode 100644 Documentation/x86/sgx/1.Architecture.rst
create mode 100644 Documentation/x86/sgx/index.rst
diff --git a/Documentation/index.rst b/Documentation/index.rst
index 80a421cb935e..3511400dc092 100644
--- a/Documentation/index.rst
+++ b/Documentation/index.rst
@@ -102,6 +102,7 @@ implementation.
:maxdepth: 2
sh/index
+ x86/index
Filesystem Documentation
------------------------
diff --git a/Documentation/x86/index.rst b/Documentation/x86/index.rst
new file mode 100644
index 000000000000..6f3251c4b7b9
--- /dev/null
+++ b/Documentation/x86/index.rst
@@ -0,0 +1,10 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+x86 Documentation
+=================
+
+.. toctree::
+ :maxdepth: 1
+
+ sgx/index
diff --git a/Documentation/x86/sgx/1.Architecture.rst b/Documentation/x86/sgx/1.Architecture.rst
new file mode 100644
index 000000000000..a4de6c610231
--- /dev/null
+++ b/Documentation/x86/sgx/1.Architecture.rst
@@ -0,0 +1,431 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============
+Architecture
+============
+
+Introduction
+============
+
+SGX is a set of instructions and mechanisms that enable ring 3 applications to
+set aside private regions of code and data for the purpose of establishing and
+running enclaves. An enclave is a secure entity whose private memory can only
+be accessed by code running within the enclave. Accesses from outside the
+enclave, including software running at a higher privilege level and other
+enclaves, are disallowed by hardware.
+
+SGX also provides for local and remote attestation. `Attestation`_ allows an
+enclave to attest its identity, that it has not been tampered with, that it is
+running on a genuine platform with Intel SGX enabled, and the security
+properties of the platform on which it is running.
+
+You can determine if your CPU supports SGX by querying ``/proc/cpuinfo``:
+
+ ``cat /proc/cpuinfo | grep sgx``
+
+
+Enclave Page Cache
+==================
+
+SGX utilizes an Enclave Page Cache (EPC) to store pages that are associated
+with an enclave. The EPC is secure storage whose exact physical implementation
+is micro-architecture specific (see `EPC Implemenations`_). Similar to normal
+system memory, the EPC is managed by privileged software using conventional
+paging mechanisms, e.g. the kernel can grant/deny access to EPC memory by
+manipulating a process' page tables, and can swap pages in/out of the EPC in
+order to oversubscribe the EPC.
+
+Unlikely regular memory, hardware prevents arbitrary insertion, eviction,
+deletion, access, etc... to/from the EPC. Software must instead use dedicated
+`SGX instructions`_ to operate on the EPC, which enables the processor to
+provide SGX's security guarantees by enforcing various restrictions and
+behaviors, e.g. limits concurrent accesses to EPC pages and ensures proper TLB
+flushing when moving pages in/out of the EPC.
+
+Accesses to EPC pages are allowed if and only if the access is classified as an
+"enclave access". There are two categories of allowed enclave accesses: direct
+and indirect. Direct enclave accesses are generated if and only the processor
+is executing in Enclave Mode (see `Enclave execution`_). Indirect enclave
+accesses are generated by various ENCL{S,U,V} functions, many of which can be
+executed outside of Enclave Mode.
+
+Non-enclave accesses to the EPC result in undefined behavior. Conversely,
+enclave accesses to non-EPC memory result in a page fault (#PF)[1]_. Page
+faults due to invalid enclave accesses set the PF_SGX flag (bit 15) in the page
+fault error code[2]_.
+
+Although all EPC implementations will undoubtedly encrypt the EPC itself, all
+all EPC code/data is stored unencrypted in the processor's caches. I.e. SGX
+relies on the aforementioned mechanisms to protect an enclave's secrets while
+they are resident in the cache.
+
+Note, EPC pages are always 4KB sized and aligned. Software can map EPC using
+using large pages, but the processor always operates on a 4KB granularity when
+working with EPC pages.
+
+
+SGX instructions
+================
+
+SGX introduces three new instructions, ENCLS, ENCLU and ENCLV, for Supervisor,
+User and Virtualization respectively. ENCL{S,U,V} are umbrella instructions,
+using a single opcode as the front end to a variety of SGX functions. The leaf
+function to execute is specified via %eax, with %rbx, %rcx and %rdx optionally
+used for leaf-specific purposes.
+
+Note that supervisor software, i.e. the kernel, creates and manages enclaves,
+but only user-level software can execute/enter an enclave.
+
+ENCLS Leafs
+-----------
+
+ - ECREATE: create an enclave
+ - EADD: add page to an uninitialized enclave
+ - EAUG: add page to an initialized enclave
+ - EEXTEND: extended the measurement of an (uninitialized) enclave
+ - EINIT: verify and initialize enclave
+ - EDBG{RD,WR}: read/write from/to a debug enclave’s memory
+ - EMODPR: restrict an EPC page’s permissions
+ - EMODT: modify an EPC page’s type
+ - EBLOCK: mark a page as blocked in EPCM
+ - ETRACK{C}: activate blocking tracing
+ - EWB: write back page from EPC to regular memory
+ - ELD{B,U}{C}: load page in {un}blocked state from system memory to EPC
+ - EPA: add version array (use to track evicted EPC pages)
+ - EREMOVE: remove a page from EPC
+ - ERDINFO: retrieve info about an EPC page from EPCM
+
+ENCLU Leafs
+-----------
+ - EENTER: enter an enclave
+ - ERESUME: resume execution of an interrupted enclave
+ - EEXIT: exit an enclave
+ - EGETKEY: retrieve a cryptographic key from the processor
+ - EREPORT: generate a cryptographic report describing an enclave
+ - EMODPE: extend an EPC page's permissions
+ - EACCEPT: accept changes to an EPC page
+ - EACCEPTCOPY: copy an existing EPC page to an uninitialized EPC page
+
+ENCLV Leafs
+-----------
+ - E{DEC,INC}VIRTCHILD: {dec,inc}rement SECS virtual refcount
+ - ESETCONTEXT: set SECS’ context pointer
+
+
+EPC page types
+==============
+
+All pages in the EPC have an explicit page type identifying the type of page.
+The type of page affects the page's accessibility, concurrency requirements,
+lifecycle, etc...
+
+SGX Enclave Control Structure (SECS)
+ An enclave is defined and referenced by an SGX Enclave Control Structure.
+ When creating an enclave (via ECREATE), software provides a source SECS for
+ the enclave, which is copied into a target EPC page. The source SECS
+ contains security and measurement information, as well as attributes and
+ properties of the enclave. Once the SECS is copied into the EPC, it's used
+ by the processor to store enclave metadata, e.g. the number of EPC pages
+ associated with the enclave, and is no longer directly accessible by
+ software.
+
+Regular (REG)
+ Regular EPC pages contain the code and data of an enclave. Code and data
+ pages can be added to an uninitialized enclave (prior to EINIT) via EADD.
+ Post EINIT, pages can be added to an enclave via EAUG. Pages added via
+ EAUG must be explicitly accepted by the enclave via EACCEPT or EACCEPTCOPY.
+
+Thread Control Structure (TCS)
+ Thread Control Structure pages define the entry points to an enclave and
+ track the execution state of an enclave thread. A TCS can only be used by
+ a single logical CPU at any given time, but otherwise has no attachment to
+ any particular logical CPU. Like regular pages, TCS pages are added to
+ enclaves via EADD and EINIT.
+
+Version Array (VA)
+ Version Array pages contain 512 slots, each of which can contain a version
+ number for a page evicted from the EPC. A version number is a unique 8-byte
+ value that is fed into the MAC computation used to verify the contents of an
+ evicted page when reloading said page into the EPC. VA pages are the only
+ page type not directly associated with an enclave, and are allocated in the
+ EPC via EPA. Note that VA pages can also be evicted from the EPC, but
+ doing so requires another VA page/slot to hold the version number of the VA
+ page being evicted.
+
+Trim (TRIM)
+ The Trim page type indicates that a page has been trimmed from the enclave’s
+ address space and is no longer accessible to enclave software, i.e. is about
+ to be removed from the enclave (via EREMOVE). Removing pages from a running
+ enclaves requires the enclave to explicit accept the removal (via EACCEPT).
+ The intermediate Trim type allows software to batch deallocation operations
+ to improve efficiency, e.g. minimize transitions between userspace, enclave
+ and kernel.
+
+
+Enclave Page Cache Map
+======================
+
+The processor tracks EPC pages via the Enclave Page Cache Map (EPCM). The EPCM
+is a processor-managed structure that enforces access restrictions to EPC pages
+in addition to the software-managed page tables. The EPCM contains one entry
+per EPC page, and although the details are implementation specific, all
+implementations contain the following architectural information:
+
+ - The status of EPC page with respect to validity and accessibility.
+ - An SECS identifier of the enclave to which the page belongs.
+ - The type of page: regular, SECS, TCS, VA or TRIM
+ - The linear address through which the enclave is allowed to access the page.
+ - The specified read/write/execute permissions on that page.
+
+Access violations, e.g. insufficient permissions or incorrect linear address,
+detected via the EPCM result in a page fault (#PF)[1]_ exception being signaled
+by the processor. Page faults due to EPCM violations set the PF_SGX flag
+(bit 15) in the page fault error code[2]_.
+
+The EPCM is consulted if and only if walking the software-managed page tables,
+i.e. the kernel's page tables, succeeds. I.e. the effective permissions for an
+EPC page are a logical AND of the kernel's page tables and the corresponding
+EPCM entry. This allows the kernel to make its page tables more restrictive
+without triggering an EPCM violation, e.g. it may mark an entry as not-present
+prior to evicting a page from the EPC.
+
+**IMPORTANT** For all intents and purposes the SGX architecture allows the
+processor to invalidate all EPCM entries at will, i.e. requires that software
+be prepared to handle an EPCM fault at any time. Most processors are expected
+to implement the EPC{M} as a subset of system DRAM that is encrypted with an
+ephemeral key, i.e. a key that is randomly generated at processor reset. As a
+result of using an ephemeral key, the contents of the EPC{M} are lost when the
+processor is powered down as part of an S3 transition or when a virtual machine
+is live migrated to a new physical system.
+
+
+Enclave initialization
+======================
+
+Because software cannot directly access the EPC except when executing in an
+enclave, an enclave must be built using ENCLS functions (ECREATE and EADD) as
+opposed to simply copying the enclave from the filesystem to memory. Once an
+enclave is built, it must be initialized (via EINIT) before userspace can enter
+the enclave and begin `Enclave execution`_.
+
+During the enclave build process, two "measurements", i.e. SHA-256 hashes, are
+taken of the enclave: MRENCLAVE and MRSIGNER. MRENCLAVE measures the enclave's
+contents, e.g. code/data explicitly added to the measurement (via EEXTEND), as
+well as metadata from the enclave's build process, e.g. pages offsets (relative
+to the enclave's base) and page permissions of all pages added to the enclave
+(via EADD). MRENCLAVE is initialized by ECREATE and finalized by EINIT.
+MRSIGNER is simply the SHA-256 hash of the public key used to sign the enclave.
+
+EINIT accepts two parameters in addition to the SECS of the target enclave: an
+Enclave Signature Struct (SIGSTRUCT) and an EINIT token (EINITTOKEN).
+SIGSTRUCT is a structure created and signed by the enclave's developer. Among
+other fields, SIGSTRUCT contains the expected MRENCLAVE of the enclave and the
+MRSIGNER of the enclave. SIGSTRUCT's MRENCLAVE is used by the processor to
+verify that the enclave was properly built (at runtime), and its SIGSTRUCT is
+copied to the SECS upon successful EINIT. EINITTOKEN is an optional parameter
+that is consumed as part of `Launch Control`_.
+
+
+Enclave execution
+=================
+
+Enclaves execute in a bespoke sub-mode of ring 3, appropriately named Enclave
+Mode. Enclave Mode changes behavior in key ways to support SGX's security
+guarantees and to reduce the probability of unintentional disclosure of
+sensitive data.
+
+A notable cornerstone of Enclave Mode is the Enclave Linear Range (ELRANGE).
+An enclave is associated with one, and only one, contiguous linear address
+range, its ELRANGE. The ELRANGE is specified via the SIZE and BASEADDR fields
+in the SECS (provided to ECREATE). The processor queries the active enclave's
+ELRANGE to differentiate enclave and non-enclave accesses, i.e. accesses that
+originate in Enclave Mode *and* whose linear address falls within ELRANGE are
+considered (direct) enclave accesses. Note, the processor also generates
+(indirect) enclave accesses when executing ENCL* instructions, which may occur
+outside of Enclave Mode, e.g. when copying the SECS to its target EPC page
+during ECREATE.
+
+Enclave Mode changes include, but are not limited to:
+
+ - Permits direct software access to EPC pages owned by the enclave
+ - Ensures enclave accesses map to the EPC (EPCM violation, i.e. #PF w/ PF_SGX)
+ - Prevents executing code outside the enclave's ELRANGE (#GP fault)
+ - Changes the behavior of exceptions/events
+ - Causes many instructions to become illegal, i.e. generate an exception
+ - Supresses all instruction breakpoints*
+ - Suppresses data breakpoints within enclave's ELRANGE*
+
+ * For non-debug enclaves.
+
+Transitions to/from Enclave Mode have semantics that are a lovely blend of
+SYSCALL, SYSRET and VM-Exit. In normal execution, entering and exiting Enclave
+Mode can only be done through EENTER and EEXIT respectively. EENTER+EEXIT is
+analogous to SYSCALL+SYSRET, e.g. EENTER/SYSCALL load RCX with the next RIP and
+EEXIT/SYSRET load RIP from R{B,C}X, and EENTER can only jump to a predefined
+location controlled by the enclave/kernel.
+
+But when an exception, interrupt, VM-Exit, etc... occurs, enclave transitions
+behave more like VM-Exit and VMRESUME. To maintain the black box nature of the
+enclave, the processor automatically switches register context when any of the
+aforementioned events occur (the SDM refers to such events as Enclave Exiting
+Events (EEE)).
+
+To handle an EEE, the processor performs an Asynchronous Enclave Exits (AEX).
+Note, although exceptions and traps are synchronous from a processor execution
+perspective, the are asynchronous from the enclave's perspective as the enclave
+is not provided an opportunity to save/fuzz state prior to exiting the enclave.
+On an AEX, the processor exits the enclave to a predefined %rip called the
+Asynchronous Exiting Pointer (AEP). The AEP is specified at enclave entry (via
+EENTER/ERESUME) and saved into the associated TCS, similar to how a hypervisor
+specifies the VM-Exit target (via VMCS.HOST_RIP at VMLAUNCH/VMRESUME), i.e. the
+the AEP is an exit location controlled by the enclave's untrusted runtime.
+
+On an AEX, the processor fully exits the enclave prior to vectoring the event,
+i.e. from the event handler's perspective the event occurred at the AEP. Thus,
+IRET/RSM/VMRESUME (from the event handler) returns control to the enclave's
+untrusted runtime, which can take appropriate action, e.g. immediately ERESUME
+the enclave on interrupts, forward expected exceptions to the enclave, restart
+the enclave on fatal exceptions, and so on and so forth.
+
+To preserve the enclave's state across AEX events, the processor automatically
+saves architectural into a State Save Area (SSA). Because SGX supports nested
+AEX events, e.g. the untrusted runtime can re-EENTER the enclave after an AEX,
+which can in turn trigger an AEX, the TCS holds a pointer to a stack of SSA
+frames (as opposed to a single SSA), an index to the current SSA frame and the
+total number of available frames. When an AEX occurs, the processor saves the
+architectural state into the TCS's current SSA frame. The untrusted runtime
+can then pop the last SSA frame (off the TCS's stack) via ERESUME, i.e. restart
+the enclave after the AEX is handled.
+
+
+Launch Control
+==============
+
+SGX provides a set of controls, referred to as Launch Control, that governs the
+initialization of enclaves. The processor internally stores a SHA-256 hash of
+a 3072-bit RSA public key, i.e. a MRSIGNER, often referred to as the "LE pubkey
+hash". The LE pubkey hash is used during EINIT to prevent launching an enclave
+without proper authorization. In order for EINIT to succeed, the enclave's
+MRSIGNER (from SIGSTRUCT) *or* the MRSIGNER of the enclave's EINITTOKEN must
+match the LE pubkey hash.
+
+An EINITTOKEN can only be created by a so called Launch Enclave (LE). A LE is
+an enclave with SECS.ATTRIBUTES.EINITTOKEN_KEY=1, which grants it access to the
+EINITTOKEN_KEY (retrieved via EGETKEY). EINITTOKENs provide a ready-built
+mechanism for userspace to bless enclaves without requiring additional kernel
+infrastructure.
+
+Processors that support SGX Launch Control Configuration, enumerated by the
+SGX_LC flag (bit 30 in CPUID 0x7.0x0.ECX), expose the LE pubkey hash as a set
+of four MSRs, aptly named IA32_SGXLEPUBKEYHASH[0-3]. The reset value of the
+MSRs is an internally defined (Intel) key (processors that don't support
+SGX_LC also use an internally defined key, it's just not exposed to software).
+
+While the IA32_SGXLEPUBKEYHASH MSRs are readable on any platform that supports
+SGX_LC, the MSRs are only writable if the IA32_FEATURE_CONTROL is locked with
+bit 17 ("SGX Launch Control Enable" per the SDM, or more accurately "SGX LE
+pubkey hash writable") set to '1'. Note, the MSRs are also writable prior to
+`SGX activation`_.
+
+Note, while "Launch Control Configuration" is the official feature name used by
+the Intel SDM, other documentation may use the term "Flexible Launch Control",
+or even simply "Launch Control". Colloquially, the vast majority of usage of
+the term "Launch Control" is synonymous with "Launch Control Configuration".
+
+
+EPC oversubscription
+====================
+
+SGX supports the concept of EPC oversubscription. Analogous to swapping system
+DRAM to disk, enclave pages can be swapped from the EPC to memory, and later
+reloaded from memory to the EPC. But because the kernel is untrusted, swapping
+pages in/out of the EPC has specialized requirements:
+
+ - The kernel cannot directly access EPC memory, i.e. cannot copy data to/from
+ the EPC.
+ - The kernel must "prove" to hardware that there are no valid TLB entries for
+ said page prior to eviction (a stale TLB entry would allow an attacker to
+ bypass SGX access controls).
+ - When loading a page back into the EPC, hardware must be able to verify
+ the integrity and freshness of the data.
+ - When loading an enclave page, e.g. regular and TCS pages, hardware must be
+ able to associate the page with an SECS, i.e. refcount an enclaves pages.
+
+To satisfy the above requirements, the CPU provides dedicated ENCLS functions
+to support paging data in/out of the EPC:
+
+ - EBLOCK: Mark a page as blocked in the EPC Map (EPCM). Attempting to access
+ a blocked page that misses the TLB will fault.
+ - ETRACK: Activate TLB tracking. Hardware verifies that all translations for
+ pages marked as "blocked" have been flushed from the TLB.
+ - EPA: Add Version Array page to the EPC (see `EPC page types`_)
+ - EWB: Write back a page from EPC to memory, e.g. RAM. Software must
+ supply a VA slot, memory to hold the Paging Crypto Metadata (PCMD) of the
+ page and obviously backing for the evicted page.
+ - ELD*: Load a page in {un}blocked state from memory to EPC.
+
+Swapped EPC pages are {de,en}crypted on their way in/out of the EPC, e.g. EWB
+encrypts and ELDU decrypts. The version number (stored in a VA page) and PCMD
+structure associated with an evicted EPC page seal a page (prevent undetected
+modification) and ensure its freshness (prevent rollback to a stale version of
+the page) while the page resides in unprotected storage, e.g. memory or disk.
+
+
+Attestation
+===========
+
+SGX provides mechanisms that allow software to implement what Intel refers to
+as Local Attestation (used by enclaves running on a the same physical platform
+to securely identify one another) and Remote Attestation (a process by which an
+enclave attests itself to a remote entity in order to gain the trust of said
+entity).
+
+The details of Local Attestation and Remote Attestation are far beyond the
+scope of this document. Please see Intel's Software Developer's Manual and/or
+use your search engine of choice to learn more about SGX's attestation
+capabilities.
+
+
+EPC Implemenations
+==================
+
+PRM with MEE
+--------------
+
+Initial hardware support for SGX implements the EPC by reserving a chunk of
+system DRAM, referred to as Processor Reserved Memory (PRM). A percentage of
+PRM is consumed by the processor to implement the EPCM, with the remainder of
+PRM being exposed to software as the EPC. PRM is configured by firmware via
+dedicated PRM Range Registers (PRMRRs). The PRMRRs are locked which are locked as part of SGX activation, i.e.
+resizing the PRM, and thus EPC, requires rebooting the system.
+
+An autonomous hardware unit called the Memory Encryption Engine (MEE) protects
+the confidentiality, integrity, and freshness of the PRM, e.g. {de,en}crypts
+data as it is read/written from/to DRAM to provide confidentiality.
+
+
+SGX activation
+==============
+
+Before SGX can be fully enabled, e.g. via FEATURE_CONTROL, the platform must
+undergo explicit SGX activation. SGX activation is a mechanism by which the
+processor verifies and locks the platform configuration set by pre-boot
+firmware, e.g. to ensure it satisfies SGX's security requirements. Before
+SGX is activated (and its configuration locked), firmware can modify the
+PRMRRs, e.g. to set the base/size of the PRM and thus EPC, and can also write
+the SGX_LEPUBKEYHASH MSRs. Notably, the latter allows pre-boot firmware to
+lock the SGX_LEPUBKEYHASH MSRs to a non-Intel value by writing the MSRs and
+locking MSR_IA32_FEATURE_CONTROL without setting the "SGX LE pubkey hash
+writable" flag, i.e. making the SGX_LEPUBKEYHASH MSRs readonly.
+
+
+Footnotes
+=========
+
+.. [1] All processors that do not support the SGX2 ISA take an errata and
+ signal #GP(0) instead of #PF(PF_SGX) when vectoring EPCM violations and
+ faults due to enclave-accesses to non-EPC memory.
+
+.. [2] Note that despite being vectored as a #PF, a #PF with PF_SGX has nothing
+ to do with conventional paging.
+
diff --git a/Documentation/x86/sgx/index.rst b/Documentation/x86/sgx/index.rst
new file mode 100644
index 000000000000..c5dfef62e612
--- /dev/null
+++ b/Documentation/x86/sgx/index.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+Software Guard Extensions
+=========================
+
+Intel(R) SGX is a set of architectural extensions that enables applications to
+establish secure containers, a.k.a. enclaves. SGX enclaves provide security
+guarantees such as integrity and confidentiality, even when running on a system
+where privileged software, e.g. kernel, hypervisor, etc... is untrusted and
+potentially malicious.
+
+.. toctree::
+ :maxdepth: 1
+
+ 1.Architecture
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 27/28] docs: x86/sgx: Document kernel internals
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (25 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 26/28] docs: x86/sgx: Add Architecture documentation Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-17 10:39 ` [PATCH v20 28/28] docs: x86/sgx: Document the enclave API Jarkko Sakkinen
` (6 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
From: Sean Christopherson <sean.j.christopherson@intel.com>
Document some of the more tricky parts of the kernel implementation
internals.
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
Documentation/x86/sgx/2.Kernel-internals.rst | 56 ++++++++++++++++++++
Documentation/x86/sgx/index.rst | 1 +
2 files changed, 57 insertions(+)
create mode 100644 Documentation/x86/sgx/2.Kernel-internals.rst
diff --git a/Documentation/x86/sgx/2.Kernel-internals.rst b/Documentation/x86/sgx/2.Kernel-internals.rst
new file mode 100644
index 000000000000..de359bf605ca
--- /dev/null
+++ b/Documentation/x86/sgx/2.Kernel-internals.rst
@@ -0,0 +1,56 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================
+Kernel Internals
+================
+
+CPU configuration
+=================
+
+Because SGX has an ever evolving and expanding feature set, it's possible for
+a BIOS or VMM to configure a system in such a way that not all CPUs are equal,
+e.g. where Launch Control is only enabled on a subset of CPUs. Linux does
+*not* support such a heterogeneous system configuration, nor does it even
+attempt to play nice in the face of a misconfigured system. With the exception
+of Launch Control's hash MSRs, which can vary per CPU, Linux assumes that all
+CPUs have a configuration that is identical to the boot CPU.
+
+EPC oversubscription
+====================
+
+SGX allows to have larger enclaves than amount of available EPC by providing a
+subset of leaf instruction for swapping EPC pages to the system memory. The
+details of these instructions are discussed in the architecture document. Due
+to the unique requirements for swapping EPC pages, and because EPC pages do not
+have associated page structures, management of the EPC is not handled by the
+standard memory subsystem.
+
+SGX directly handles swapping of EPC pages, including a thread to initiate the
+reclaiming process and a rudimentary LRU mechanism. When the amount of free EPC
+pages goes below a low watermark the swapping thread starts reclaiming pages.
+The pages that have not been recently accessed (i.e. do not have the A bit set)
+are selected as victim pages. Each enclave holds an shmem file as a backing
+storage for reclaimed pages.
+
+Launch Control
+==============
+
+The current kernel implementation supports only writable MSRs. The launch is
+performed by setting the MSRs to the hash of the public key modulus of the
+enclave signer and a token with the valid bit set to zero. Because kernel makes
+ultimately all the launch decisions token are not needed for anything. We
+don't need or have a launch enclave for generating them as the MSRs must always
+be writable.
+
+Provisioning
+============
+
+The use of provisioning must be controlled because it allows to get access to
+the provisioning keys to attest to a remote party that the software is running
+inside a legit enclave. This could be used by a malware network to ensure that
+its nodes are running inside legit enclaves.
+
+The driver introduces a special device file /dev/sgx/provision and a special
+ioctl SGX_IOC_ENCLAVE_SET_ATTRIBUTE to accomplish this. A file descriptor
+pointing to /dev/sgx/provision is passed to ioctl from which kernel authorizes
+the PROVISION_KEY attribute to the enclave.
diff --git a/Documentation/x86/sgx/index.rst b/Documentation/x86/sgx/index.rst
index c5dfef62e612..5d660e83d984 100644
--- a/Documentation/x86/sgx/index.rst
+++ b/Documentation/x86/sgx/index.rst
@@ -14,3 +14,4 @@ potentially malicious.
:maxdepth: 1
1.Architecture
+ 2.Kernel-internals
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [PATCH v20 28/28] docs: x86/sgx: Document the enclave API
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (26 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 27/28] docs: x86/sgx: Document kernel internals Jarkko Sakkinen
@ 2019-04-17 10:39 ` Jarkko Sakkinen
2019-04-18 17:10 ` [PATCH v20 00/28] Intel SGX1 support Dr. Greg
` (5 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-17 10:39 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
Jarkko Sakkinen
Document the enclave driver API i.e. the set of ioctl's used to create
and manage enclaves and set their privileges
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
Documentation/x86/sgx/3.API.rst | 27 +++++++++++++++++++++++++++
Documentation/x86/sgx/index.rst | 1 +
2 files changed, 28 insertions(+)
create mode 100644 Documentation/x86/sgx/3.API.rst
diff --git a/Documentation/x86/sgx/3.API.rst b/Documentation/x86/sgx/3.API.rst
new file mode 100644
index 000000000000..b113aeb05f54
--- /dev/null
+++ b/Documentation/x86/sgx/3.API.rst
@@ -0,0 +1,27 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===
+API
+===
+
+The enclave life-cycle starts by opening `/dev/sgx/enclave`. After this there is
+already a data structure inside kernel tracking the enclave that is initially
+uncreated. After this a set of ioctl's can be used to create, populate and
+initialize the enclave.
+
+You can close (if you want) the fd after you've mmap()'d. As long as the file is
+open the enclave stays alive so you might want to do that after you don't need
+it anymore. Even munmap() won't destruct the enclave if the file is open.
+Neither will closing the fd as long as you have mmap() done over the fd (even
+if it does not across the range defined in SECS).
+
+Finally, there is ioctl to authorize priviliged attributes:
+`SGX_IOC_ENCLAVE_SET_ATTRIBUTE`. Each of them is presented by a file inside
+`/dev/sgx/`. Right now there is only one such file `/dev/sgx/provision`, which
+controls the `PROVISON_KEY` attribute.
+
+.. kernel-doc:: arch/x86/kernel/cpu/sgx/driver/ioctl.c
+ :functions: sgx_ioc_enclave_create
+ sgx_ioc_enclave_add_page
+ sgx_ioc_enclave_init
+ sgx_ioc_enclave_set_attribute
diff --git a/Documentation/x86/sgx/index.rst b/Documentation/x86/sgx/index.rst
index 5d660e83d984..de0b78328611 100644
--- a/Documentation/x86/sgx/index.rst
+++ b/Documentation/x86/sgx/index.rst
@@ -15,3 +15,4 @@ potentially malicious.
1.Architecture
2.Kernel-internals
+ 3.API
--
2.19.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (27 preceding siblings ...)
2019-04-17 10:39 ` [PATCH v20 28/28] docs: x86/sgx: Document the enclave API Jarkko Sakkinen
@ 2019-04-18 17:10 ` Dr. Greg
2019-04-18 17:24 ` Dave Hansen
` (2 more replies)
2019-04-23 0:37 ` [RFC PATCH v1 0/3] An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack Cedric Xing
` (4 subsequent siblings)
33 siblings, 3 replies; 285+ messages in thread
From: Dr. Greg @ 2019-04-18 17:10 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: torvalds, linux-kernel, x86, linux-sgx, akpm, dave.hansen,
sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes
[-- Attachment #1: Type: text/plain, Size: 9413 bytes --]
On Wed, Apr 17, 2019 at 01:39:10PM +0300, Jarkko Sakkinen wrote:
Good morning, I hope this note finds the impending end of the work
week going well for everyone.
First of all, a thank you to Jarkko for his efforts in advancing this
driver, a process that can be decidedly thankless. Our apologies in
advance for contributing to this phenomenon.
> Intel(R) SGX is a set of CPU instructions that can be used by
> applications to set aside private regions of code and data. The code
> outside the enclave is disallowed to access the memory inside the
> enclave by the CPU access control. In a way you can think that SGX
> provides inverted sandbox. It protects the application from a
> malicious host.
Unfortunately, in its current implementation, the driver will not
protect a host from malicious enclaves. If it advances in its current
form, the mainline Linux kernel will be implementing a driver with
known and documented security issues.
In addition, the driver breaks all existing SGX software by breaking
compatibility with what is a 3+ year ABI provided by the existing
driver. This seems to contravene the well understood philosophy that
Linux doesn't, if at all possible, break existing applications,
something that we believe can be easily prevented if those interested
in these issues choose to read on.
The following reflections and code come from a team that has been
active in SGX development, since before hardware was even publically
available. A team that has authored a complete re-implementation of
all of the SGX runtime infrastructure, which is the only consumer of
the services provided by an SGX driver. A team that has also authored
and maintains a significant cohort of SGX based applications. So I
believe we speak from a vantage point of experience with respect to
these issues and not from a 'bike-shedding' or political perspective.
The fundamental problem is that the proposed driver lacks an enclave
initialization policy mechanism consistent with the security
guarantees that SGX hardware is designed to deliver. SGX is designed
to provide system architects a framework for building IAGO resistent
security architectures through the use of cryptographically defined
identities.
Other then an attempt to limit acccess to the PROVISION attribute, the
driver makes no attempt to regulate, in a cryptographically secure
fashion, what enclaves can be initialized, who can initialize them or
what characteristics they can be initialized with. The security
implications of this were recently documented in a paper by
researchers at Graz, here is the link for those interested:
https://arxiv.org/pdf/1702.08719.pdf
Both the current controls for enclave access to the PROVISION
attribute and the security controls that are being proposed to emerge
for the driver, sometime in the future, suffer from being dependent on
discretionary access controls, ie. file privileges, that can be
defeated by a privilege escalation attack. Those of us building
architectures on top of this technology have a need to certify that an
application will provide security contracts robust in the face of a
privilege escalation event or platform compromise.
To be very blunt, and my apologies for doing so, the design for this
driver has been driven by idealogy rather then by technology. Our
focus with this e-mail and proposed driver modification is an attempt
to satisfy both technical requirements and political idealogy, if that
is even possible.
When we raised these issues six months ago, we were told that we were
handwaving bloat into the kernel. We take a criticism like that very
seriously so we are returning with code, in what we believe is
'bloat-free' form.
The attached patch, against the jarkko-sgx/master branch of Jarkko's
driver repository, provides a framework that implements
cryptographically secure enclave initialization policies. The patch
and it's signature are also available from the following URL's:
ftp://ftp.idfusion.net/pub/idfusion/jarkko-master-SFLC.patch
ftp://ftp.idfusion.net/pub/idfusion/jarkko-master-SFLC.patch.asc
The modification, in series form, is also available in the following
GIT repository:
git://git.idfusion.net/src/linux-sgx/in-tree jarkko-master-SFLC
The driver modification implements cryptographically secure enclave
initialization only if the platform owner chooses to do so and does
not limit the development of alternative strategies in the future. It
also allows the platform owner to choose the ability to remain
compatible with existing runtimes and applications.
The policy architecture allows just about any conceivable
initialization policy to be implemented and in any combination. It
supports both a plurality of launch enclaves as well as support for
multiple signing keys if a token-less approach is desired. It also
provides cryptographically secure control of access to the PROVISION
attribute and the ability to completely block access to the attribute
if that is desired.
The driver modification has been extensively tested in all of these
scenarios, including combinations thereof. We just completed a major
round of development that was based on Flexible Launch Control
platforms and deployed onto fixed launch control platforms using the
out-of-tree driver, without a need to maintain disparate runtimes.
On that note, given that the new driver API is completely incompatible
with all of the existing runtime software in the field, we believe the
issue of proper testing of this driver is an open problem. I'm assuming
that Intel has a yet to be released PSW/SDK that supports the driver,
otherwise our enhancements to the driver is the only way that it can
experience legitimate field testing.
The interface for using the policy management framework is straight
forward. The following three pseudo-files are available:
/sys/kernel/security/sgx/launch_keys
/sys/kernel/security/sgx/provisioning_keys
/sys/kernel/security/sgx/signing_keys
The SHA256 hashes of the cryptographic keys used to sign enclaves
(MRSIGNER values) are written into these files, ie, the following
command:
echo "SHA256_HASH" >| /sys/kernel/security/sgx/signing_keys
Would only allow enclaves to be initialized whose MRSIGNER value
matches the SHA256_HASH value.
The 'launch_keys' file maintains a list of signer signatures that are
allowed to initialize enclaves with the EINITTOKEN attribute set.
The 'provisioning_keys' file maintains a list of signer signatures
that are allowed to initialize enclaves with the PROVISION attribute
set.
Writing the 'lock' keyword to a file permanently blocks any further
directives from being recognized. At that point, a very targeted
ring-0 attack would need to be conducted to circumvent the enclave
initialization policies. This provides a framework that allows a
flexible launch control platform to attain the approximate security
guarantees offered by a fixed launch control platform.
If the platform owner chooses to do nothing, the driver will
initialize any enclave that is presented to it. FWIW, we have close
to a century of enterprise system's administration experience on our
team and we could not envision any competent system's administrator,
concerned about security, who would allow such a configuration.
So that is how it all works, the changelogs in the GIT repository have
much more extensive documentation. We fit the entire architecture
into approximately one page of memory, which seems to be a minor
amount of bloat for everyone getting to do whatever they want to do,
including leaving Linux mainline with a secure driver implementation.
Here is the diffstat:
---------------------------------------------------------------------------
arch/x86/include/uapi/asm/sgx.h | 2 +
arch/x86/kernel/cpu/sgx/driver/Makefile | 1 +
arch/x86/kernel/cpu/sgx/driver/driver.h | 7 +
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 88 ++++-
arch/x86/kernel/cpu/sgx/driver/main.c | 5 +
arch/x86/kernel/cpu/sgx/driver/policy.c | 584 ++++++++++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/main.c | 27 +-
arch/x86/kernel/cpu/sgx/sgx.h | 3 +-
8 files changed, 704 insertions(+), 13 deletions(-)
---------------------------------------------------------------------------
With respect to a mainline Linux driver at large, an issue that we
believe needs clarification is whether or not a mandate will be issued
by Intel to OEM's that Flexible Launch Control is mandatory on all
platforms that are henceforth shipped. Otherwise the mainline Linux
driver will be in the unenviable position of only working sporadically
and will not be a universal solution for all hardware.
We have a solution for that as well, but the above is probably enough
cannon fodder for debate so we will leave that sleeping dog lie for
the time being.
Hopefully all of the above is helpful for enhancing the quality of the
Linux SGX eco-system.
Best wishes for a productive weekend.
Dr. Greg
As always,
Dr. G.W. Wettstein, Ph.D. Enjellic Systems Development, LLC.
4206 N. 19th Ave. Specializing in information infra-structure
Fargo, ND 58102 development.
PH: 701-281-1686
FAX: 701-281-3949 EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"I can only provide the information, I can't make you hear it."
-- Shelley Bainter
[-- Attachment #2: jarkko-master-SFLC.patch --]
[-- Type: text/plain, Size: 23667 bytes --]
arch/x86/include/uapi/asm/sgx.h | 2 +
arch/x86/kernel/cpu/sgx/driver/Makefile | 1 +
arch/x86/kernel/cpu/sgx/driver/driver.h | 7 +
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 88 ++++-
arch/x86/kernel/cpu/sgx/driver/main.c | 5 +
arch/x86/kernel/cpu/sgx/driver/policy.c | 584 ++++++++++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/main.c | 27 +-
arch/x86/kernel/cpu/sgx/sgx.h | 3 +-
8 files changed, 704 insertions(+), 13 deletions(-)
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index 9ed690a38c70..694981e03c65 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -53,7 +53,9 @@ struct sgx_enclave_add_page {
* @sigstruct: address for the SIGSTRUCT data
*/
struct sgx_enclave_init {
+ __u64 addr;
__u64 sigstruct;
+ __u64 einittoken;
};
/**
diff --git a/arch/x86/kernel/cpu/sgx/driver/Makefile b/arch/x86/kernel/cpu/sgx/driver/Makefile
index 01ebbbb06a47..ac7702201229 100644
--- a/arch/x86/kernel/cpu/sgx/driver/Makefile
+++ b/arch/x86/kernel/cpu/sgx/driver/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_INTEL_SGX_DRIVER) += sgx.o
sgx-$(CONFIG_INTEL_SGX_DRIVER) += ioctl.o
sgx-$(CONFIG_INTEL_SGX_DRIVER) += main.o
+sgx-$(CONFIG_INTEL_SGX_DRIVER) += policy.o
diff --git a/arch/x86/kernel/cpu/sgx/driver/driver.h b/arch/x86/kernel/cpu/sgx/driver/driver.h
index 153b4a48aa6f..d5994c4aa65d 100644
--- a/arch/x86/kernel/cpu/sgx/driver/driver.h
+++ b/arch/x86/kernel/cpu/sgx/driver/driver.h
@@ -33,6 +33,13 @@ extern u32 sgx_xsave_size_tbl[64];
extern const struct file_operations sgx_provision_fops;
+uint8_t * sgx_get_launch_signer(uint8_t *signature);
+int sgx_get_key_hash(const void *modulus, void *hash);
+bool sgx_launch_control(uint8_t *modulus);
+bool sgx_provisioning_control(uint8_t *modulus);
+bool sgx_signing_control(uint8_t *modulus);
+int sgx_policy_fs(void);
+void sgx_policy_fs_remove(void);
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
#endif /* __ARCH_X86_INTEL_SGX_H__ */
diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
index 3a01c3dd579d..0d758f1498f7 100644
--- a/arch/x86/kernel/cpu/sgx/driver/ioctl.c
+++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
@@ -639,7 +639,17 @@ static int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus,
return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash);
}
-static int sgx_get_key_hash(const void *modulus, void *hash)
+/**
+* sgx_get_key_hash - Compute the hash of the enclave signer.
+*
+* @modulus: A pointer to the signature modulus.
+* @hash: A pointer to the signature buffer.
+*
+* Return:
+* 0 on success,
+* Cryptographic subsystem error.
+*/
+int sgx_get_key_hash(const void *modulus, void *hash)
{
struct crypto_shash *tfm;
int ret;
@@ -655,20 +665,26 @@ static int sgx_get_key_hash(const void *modulus, void *hash)
}
static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
- struct sgx_einittoken *token)
+ struct sgx_einittoken *token, bool use_lc)
{
u64 mrsigner[4];
int ret;
int i;
int j;
+ uint8_t *(*get_signer)(uint8_t *) = NULL;
/* Check that the required attributes have been authorized. */
if (encl->secs_attributes & ~encl->allowed_attributes)
return -EINVAL;
- ret = sgx_get_key_hash(sigstruct->modulus, mrsigner);
- if (ret)
- return ret;
+ if (use_lc)
+ get_signer = sgx_get_launch_signer;
+ else {
+ pr_debug("%s: Using modulus signature.\n", __FILE__);
+ ret = sgx_get_key_hash(sigstruct->modulus, mrsigner);
+ if (ret)
+ return ret;
+ }
flush_work(&encl->work);
@@ -683,7 +699,7 @@ static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) {
for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) {
ret = sgx_einit(sigstruct, token, encl->secs.epc_page,
- mrsigner);
+ mrsigner, get_signer);
if (ret == SGX_UNMASKED_EVENT)
continue;
else
@@ -737,6 +753,7 @@ static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd,
unsigned long arg)
{
+ bool use_sc, use_lc, use_pc;
struct sgx_enclave_init *initp = (struct sgx_enclave_init *)arg;
struct sgx_encl *encl = filep->private_data;
struct sgx_einittoken *einittoken;
@@ -744,6 +761,25 @@ static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd,
struct page *initp_page;
int ret;
+ use_sc = sgx_signing_control(NULL);
+ pr_debug("%s: Signing control status: %s\n", __FILE__,
+ use_sc ? "active" : "disabled");
+
+ use_lc = sgx_launch_control(NULL);
+ pr_debug("%s: Launch control status: %s\n", __FILE__,
+ use_lc ? "active" : "disabled");
+
+ use_pc = sgx_provisioning_control(NULL);
+ pr_debug("%s: Provisioning control status: %s\n", __FILE__,
+ use_pc ? "active" : "disabled");
+
+ if (!use_sc) {
+ if (initp->einittoken != 0 && !use_lc)
+ return -EINVAL;
+ if (initp->einittoken == 0 && use_lc)
+ return -EINVAL;
+ }
+
initp_page = alloc_page(GFP_HIGHUSER);
if (!initp_page)
return -ENOMEM;
@@ -751,7 +787,16 @@ static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd,
sigstruct = kmap(initp_page);
einittoken = (struct sgx_einittoken *)
((unsigned long)sigstruct + PAGE_SIZE / 2);
- memset(einittoken, 0, sizeof(*einittoken));
+ if ( !initp->einittoken )
+ memset(einittoken, 0, sizeof(*einittoken));
+ else {
+ if (copy_from_user(einittoken,
+ (void __user *)initp->einittoken,
+ sizeof(*einittoken))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ }
if (copy_from_user(sigstruct, (void __user *)initp->sigstruct,
sizeof(*sigstruct))) {
@@ -759,8 +804,35 @@ static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd,
goto out;
}
+ if (!use_lc && (sigstruct->body.attributes & SGX_ATTR_EINITTOKENKEY)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if ((sigstruct->body.attributes & SGX_ATTR_EINITTOKENKEY) &&
+ sgx_launch_control(sigstruct->modulus))
+ encl->allowed_attributes |= SGX_ATTR_EINITTOKENKEY;
+
+ if (!use_pc)
+ encl->allowed_attributes |= SGX_ATTR_PROVISIONKEY;
+ else {
+ if ((sigstruct->body.attributes & SGX_ATTR_PROVISIONKEY) &&
+ sgx_provisioning_control(sigstruct->modulus))
+ encl->allowed_attributes |= SGX_ATTR_PROVISIONKEY;
+ }
+
+ if (use_sc) {
+ use_sc = sgx_signing_control(sigstruct->modulus);
+ if (!use_sc && !use_lc) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (use_sc)
+ use_lc = false;
+ }
+
+ ret = sgx_encl_init(encl, sigstruct, einittoken, use_lc);
- ret = sgx_encl_init(encl, sigstruct, einittoken);
out:
kunmap(initp_page);
diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c
index afe844aa81d6..503c2a9728ec 100644
--- a/arch/x86/kernel/cpu/sgx/driver/main.c
+++ b/arch/x86/kernel/cpu/sgx/driver/main.c
@@ -348,6 +348,10 @@ static int __init sgx_drv_init(void)
{
int ret;
+ ret = sgx_policy_fs();
+ if (ret)
+ return ret;
+
ret = sgx_drv_subsys_init();
if (ret)
return ret;
@@ -362,6 +366,7 @@ module_init(sgx_drv_init);
static void __exit sgx_drv_exit(void)
{
+ sgx_policy_fs_remove();
platform_driver_unregister(&sgx_drv);
sgx_drv_subsys_exit();
}
diff --git a/arch/x86/kernel/cpu/sgx/driver/policy.c b/arch/x86/kernel/cpu/sgx/driver/policy.c
new file mode 100644
index 000000000000..c72b1604c1fa
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/policy.c
@@ -0,0 +1,584 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) IDfusion, LLC
+
+#define KEY_SIZE 32
+
+#include <linux/types.h>
+#include <linux/seq_file.h>
+#include <linux/atomic.h>
+#include <linux/security.h>
+#include "driver.h"
+
+/* Variables for launch key management. */
+struct list_key {
+ struct list_head list;
+ uint8_t key[KEY_SIZE];
+};
+
+static struct dentry *sgx_fs;
+static struct dentry *launch_keys;
+static atomic_t launch_keys_opencount = ATOMIC_INIT(1);
+static unsigned int launch_keys_count;
+static bool launch_keys_locked;
+static DEFINE_MUTEX(launch_key_list_mutex);
+static LIST_HEAD(launch_key_list);
+
+static struct dentry *provision_keys;
+static atomic_t provision_keys_opencount = ATOMIC_INIT(1);
+static unsigned int provision_keys_count;
+static bool provision_keys_locked;
+static DEFINE_MUTEX(provision_key_list_mutex);
+static LIST_HEAD(provision_key_list);
+
+static struct dentry *signing_keys;
+static atomic_t signing_keys_opencount = ATOMIC_INIT(1);
+static unsigned int signing_keys_count;
+static bool signing_keys_locked;
+static DEFINE_MUTEX(signing_key_list_mutex);
+static LIST_HEAD(signing_key_list);
+
+/**
+ * have_signer - Verify the presence presence of a key signer.
+ *
+ * @signature: Pointer to signature of signer.
+ *
+ * Return:
+ * 0 Signer signature was not found.
+ * 1 Signer signature was found.
+ */
+static bool have_signer(struct list_head *keylist, struct mutex *lock,
+ uint8_t *signature)
+{
+ bool retn = false;
+ struct list_key *kp;
+
+ mutex_lock(lock);
+ list_for_each_entry(kp, keylist, list) {
+ pr_debug("%s: Checking signer=%*phN, ks=%*phN\n", __func__,
+ KEY_SIZE, signature, KEY_SIZE, kp->key);
+ if (memcmp(kp->key, signature, KEY_SIZE) == 0) {
+ retn = true;
+ goto done;
+ }
+ }
+
+ done:
+ mutex_unlock(lock);
+ return retn;
+}
+
+/**
+ * sgx_launch_control - Launch Control policy status
+ *
+ * Verifies whether or not launch control is active.
+ *
+ * Return:
+ * 0 No launch control is authorized.
+ * 1 Launch control is permitted.
+ */
+bool sgx_launch_control(uint8_t *modulus)
+{
+ bool retn = false;
+ uint8_t mrsigner[KEY_SIZE];
+
+ if (modulus == NULL) {
+ retn = launch_keys_count > 0;
+ goto done;
+ }
+
+ pr_debug("%s: Verifying launch control modulus.\n", __func__);
+ if (sgx_get_key_hash(modulus, mrsigner))
+ goto done;
+ retn = have_signer(&launch_key_list, &launch_key_list_mutex, mrsigner);
+
+ done:
+ return retn;
+}
+
+/**
+ * sgx_get_launch_signer - Iterate through list of enclave authorizers.
+ *
+ * @signer: The last returned enclave signer.
+ *
+ * This function iterates through the list of enclave signers from the
+ * last signature. Calling the function with a NULL signer value
+ * resets the iteration to the beginning of the list.
+ *
+ * Return:
+ * NULL indicates end of list
+ * non-NULL the next enclave signature on the list.
+ */
+
+uint8_t * sgx_get_launch_signer(uint8_t *signature)
+{
+ bool seeking = false;
+ uint8_t *retn = NULL;
+ struct list_key *kp;
+
+ if (!signature) {
+ kp = list_first_entry(&launch_key_list, struct list_key, list);
+ return kp->key;
+ }
+
+ kp = list_last_entry(&launch_key_list, struct list_key, list);
+ if ( memcmp(kp->key, signature, sizeof(kp->key)) == 0 )
+ return NULL;
+
+ mutex_lock(&launch_key_list_mutex);
+ list_for_each_entry(kp, &launch_key_list, list) {
+ if (seeking) {
+ retn = kp->key;
+ goto done;
+ }
+ pr_debug("%s: Skipping: %*phN\n", __func__, KEY_SIZE, kp->key);
+ if (memcmp(kp->key, signature, KEY_SIZE) == 0)
+ seeking = true;
+ }
+
+ done:
+ mutex_unlock(&launch_key_list_mutex);
+ return retn;
+}
+
+/**
+ * sgx_provisioning_control - Provisioning control verification.
+ *
+ * This function returns a true value if provision control is active
+ * and the signature of the modulus of the signing key for an enclave
+ * with the PROVISION bit set is on the list of approved keys.
+ *
+ * Return:
+ * 0 No provisioning control is authorized.
+ * 1 Provisioning is authorized for the enclave.
+ */
+bool sgx_provisioning_control(uint8_t *modulus)
+{
+ bool retn = false;
+ uint8_t mrsigner[KEY_SIZE];
+
+ if (modulus == NULL) {
+ retn = provision_keys_count > 0;
+ goto done;
+ }
+
+ pr_debug("Verifying provisioning control signature.\n");
+ if (sgx_get_key_hash(modulus, mrsigner))
+ goto done;
+ retn = have_signer(&provision_key_list, &provision_key_list_mutex,
+ mrsigner);
+
+ done:
+ return retn;
+}
+
+/**
+ * sgx_signing_control - Signing control verification.
+ *
+ * This function returns a true value if signing control is active
+ * and the signature of the modulus of the signing key for an enclave
+ * is on the list of approved keys.
+ *
+ * Return:
+ * 0 No signing control is authorized.
+ * 1 Signing is authorized for the enclave.
+ */
+bool sgx_signing_control(uint8_t *modulus)
+{
+ bool retn = false;
+ uint8_t mrsigner[KEY_SIZE];
+
+ if (modulus == NULL) {
+ retn = signing_keys_count > 0;
+ goto done;
+ }
+
+ pr_debug("Verifying signing control signature.\n");
+ if (sgx_get_key_hash(modulus, mrsigner))
+ goto done;
+ retn = have_signer(&signing_key_list, &signing_key_list_mutex,
+ mrsigner);
+
+ done:
+ return retn;
+}
+
+static int process_write_key(const char __user *buf, size_t datalen,
+ unsigned int *keycnt, struct mutex *lock,
+ struct list_head *keylist)
+{
+ ssize_t retn;
+
+ char *p, keybufr[KEY_SIZE*2 + 1], key[KEY_SIZE];
+
+ struct list_key *kp;
+
+ if (datalen != sizeof(keybufr)) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ memset(keybufr, '\0', sizeof(keybufr));
+ if (copy_from_user(keybufr, buf, datalen)) {
+ retn = -EFAULT;
+ goto done;
+ }
+
+ p = strchr(keybufr, '\n');
+ if (!p) {
+ retn = -EINVAL;
+ goto done;
+ }
+ *p = '\0';
+ if (hex2bin(key, keybufr, sizeof(key))) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+ if (!kp) {
+ retn = -ENOMEM;
+ goto done;
+ }
+ memcpy(kp->key, key, sizeof(kp->key));
+
+ mutex_lock(lock);
+ list_add_tail(&kp->list, keylist);
+ ++*keycnt;
+ mutex_unlock(lock);
+
+ retn = datalen;
+ pr_debug("%s: Added key: %*phN\n", __func__, KEY_SIZE, key);
+
+ done:
+ return retn;
+}
+
+static int process_lock(const char __user *buf, size_t datalen, bool *lockfile)
+{
+ char bufr[5];
+
+ if (datalen != strlen("lock") + 1)
+ return 0;
+
+ memset(bufr, '\0', sizeof(bufr));
+ if (copy_from_user(bufr, buf, datalen-1))
+ return -EFAULT;
+ if ( strcmp(bufr, "lock") != 0 )
+ return 0;
+
+ *lockfile = true;
+ return datalen;
+}
+
+static int process_clear(const char __user *buf, size_t datalen, char *type,
+ unsigned int *keycnt, struct mutex *lock,
+ struct list_head *keylist)
+{
+ char bufr[6];
+ struct list_key *kp, *kp_tmp;
+
+ if (datalen != strlen("clear") + 1)
+ return 0;
+
+ memset(bufr, '\0', sizeof(bufr));
+ if (copy_from_user(bufr, buf, datalen-1))
+ return -EFAULT;
+ if ( strcmp(bufr, "clear") != 0 )
+ return 0;
+
+ mutex_lock(lock);
+ list_for_each_entry_safe(kp, kp_tmp, keylist, list) {
+ pr_debug("[%s]: Freeing signature: %*phN\n", __FILE__,
+ KEY_SIZE, kp->key);
+ list_del(&kp->list);
+ kfree(kp);
+ }
+ *keycnt = 0;
+ mutex_unlock(lock);
+
+ pr_info("Cleared %s signatures.\n", type);
+ return datalen;
+}
+
+static int process_dump(const char __user *buf, size_t datalen, char *type,
+ struct mutex *lock, struct list_head *keylist)
+{
+ char bufr[5];
+ struct list_key *kp;
+
+ if (datalen != strlen("dump") + 1)
+ return 0;
+
+ memset(bufr, '\0', sizeof(bufr));
+ if (copy_from_user(bufr, buf, datalen-1))
+ return -EFAULT;
+ if ( strcmp(bufr, "dump") != 0 )
+ return 0;
+
+ pr_info("SGX %s keys:\n", type);
+ mutex_lock(lock);
+ list_for_each_entry(kp, keylist, list) {
+ pr_info("SGX %s: %*phN\n", type, KEY_SIZE, kp->key);
+ }
+ mutex_unlock(lock);
+
+ return datalen;
+}
+
+static int open_launch_keys(struct inode * inode, struct file * filp)
+{
+ if (!(filp->f_flags & O_WRONLY))
+ return -EACCES;
+ if (atomic_dec_and_test(&launch_keys_opencount))
+ return 0;
+ return -EBUSY;
+}
+
+static ssize_t write_launch_keys(struct file *file, const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ ssize_t retn;
+
+ if (launch_keys_locked) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ if (*ppos != 0) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ retn = process_lock(buf, datalen, &launch_keys_locked);
+ if (retn != 0)
+ return retn;
+
+ retn = process_clear(buf, datalen, "launch control",
+ &launch_keys_count, &launch_key_list_mutex,
+ &launch_key_list);
+ if (retn != 0)
+ return retn;
+
+ retn = process_dump(buf, datalen, "lc", &launch_key_list_mutex,
+ &launch_key_list);
+ if (retn != 0)
+ return retn;
+
+ retn = process_write_key(buf, datalen, &launch_keys_count,
+ &launch_key_list_mutex, &launch_key_list);
+
+done:
+ return retn;
+}
+
+static int release_launch_keys(struct inode *inode, struct file *file)
+{
+ atomic_set(&launch_keys_opencount, 1);
+ return 0;
+}
+
+static const struct file_operations launch_keys_ops = {
+ .open = open_launch_keys,
+ .write = write_launch_keys,
+ .release = release_launch_keys,
+ .llseek = generic_file_llseek,
+};
+
+/* Provisioning control. */
+
+static int open_provision_keys(struct inode * inode, struct file * filp)
+{
+ if (!(filp->f_flags & O_WRONLY))
+ return -EACCES;
+ if (atomic_dec_and_test(&provision_keys_opencount))
+ return 0;
+ return -EBUSY;
+}
+
+static ssize_t write_provision_keys(struct file *file, const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ ssize_t retn;
+
+ if (provision_keys_locked) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ if (*ppos != 0) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ retn = process_lock(buf, datalen, &provision_keys_locked);
+ if (retn != 0)
+ return retn;
+
+ retn = process_clear(buf, datalen, "provisioning control",
+ &provision_keys_count, &provision_key_list_mutex,
+ &provision_key_list);
+ if (retn != 0)
+ return retn;
+
+ retn = process_dump(buf, datalen, "pc", &provision_key_list_mutex,
+ &provision_key_list);
+ if (retn != 0)
+ return retn;
+
+ retn = process_write_key(buf, datalen, &provision_keys_count,
+ &provision_key_list_mutex,
+ &provision_key_list);
+done:
+ return retn;
+}
+
+static int release_provision_keys(struct inode *inode, struct file *file)
+{
+ atomic_set(&provision_keys_opencount, 1);
+ return 0;
+}
+
+static const struct file_operations provision_keys_ops = {
+ .open = open_provision_keys,
+ .write = write_provision_keys,
+ .release = release_provision_keys,
+ .llseek = generic_file_llseek,
+};
+
+/*
+ * Signing control.
+ */
+
+static int open_signing_keys(struct inode * inode, struct file * filp)
+{
+ if (!(filp->f_flags & O_WRONLY))
+ return -EACCES;
+ if (atomic_dec_and_test(&signing_keys_opencount))
+ return 0;
+ return -EBUSY;
+}
+
+static ssize_t write_signing_keys(struct file *file, const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ ssize_t retn;
+
+ if (signing_keys_locked) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ if (*ppos != 0) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ retn = process_lock(buf, datalen, &signing_keys_locked);
+ if (retn != 0)
+ return retn;
+
+ retn = process_clear(buf, datalen, "signing control",
+ &signing_keys_count, &signing_key_list_mutex,
+ &signing_key_list);
+ if (retn != 0)
+ return retn;
+
+ retn = process_dump(buf, datalen, "sc", &signing_key_list_mutex,
+ &signing_key_list);
+ if (retn != 0)
+ return retn;
+
+ retn = process_write_key(buf, datalen, &signing_keys_count,
+ &signing_key_list_mutex, &signing_key_list);
+done:
+ return retn;
+}
+
+static int release_signing_keys(struct inode *inode, struct file *file)
+{
+ atomic_set(&signing_keys_opencount, 1);
+ return 0;
+}
+
+static const struct file_operations signing_keys_ops = {
+ .open = open_signing_keys,
+ .write = write_signing_keys,
+ .release = release_signing_keys,
+ .llseek = generic_file_llseek,
+};
+
+int sgx_policy_fs(void)
+{
+ int retn = -1;
+
+ sgx_fs = securityfs_create_dir("sgx", NULL);
+ if(IS_ERR(sgx_fs)) {
+ retn = PTR_ERR(sgx_fs);
+ goto err;
+ }
+
+ launch_keys = securityfs_create_file("launch_keys", S_IWUSR | S_IRUSR,
+ sgx_fs, NULL, &launch_keys_ops);
+ if (IS_ERR(launch_keys)) {
+ retn = PTR_ERR(launch_keys);
+ goto err;
+ }
+
+ provision_keys = securityfs_create_file("provisioning_keys",
+ S_IWUSR | S_IRUSR,
+ sgx_fs, NULL,
+ &provision_keys_ops);
+ if (IS_ERR(provision_keys)) {
+ retn = PTR_ERR(provision_keys);
+ goto err;
+ }
+
+ signing_keys = securityfs_create_file("signing_keys",
+ S_IWUSR | S_IRUSR,
+ sgx_fs, NULL,
+ &signing_keys_ops);
+ if (IS_ERR(signing_keys)) {
+ retn = PTR_ERR(signing_keys);
+ goto err;
+ }
+
+ return 0;
+
+ err:
+ securityfs_remove(launch_keys);
+ securityfs_remove(provision_keys);
+ securityfs_remove(signing_keys);
+ securityfs_remove(sgx_fs);
+ return retn;
+}
+
+void sgx_policy_fs_remove(void)
+{
+ struct list_key *kp, *kp_tmp;
+
+ mutex_lock(&launch_key_list_mutex);
+ list_for_each_entry_safe(kp, kp_tmp, &launch_key_list, list) {
+ list_del(&kp->list);
+ kfree(kp);
+ }
+ mutex_unlock(&launch_key_list_mutex);
+
+ mutex_lock(&provision_key_list_mutex);
+ list_for_each_entry_safe(kp, kp_tmp, &provision_key_list, list) {
+ list_del(&kp->list);
+ kfree(kp);
+ }
+ mutex_unlock(&provision_key_list_mutex);
+
+ mutex_lock(&signing_key_list_mutex);
+ list_for_each_entry_safe(kp, kp_tmp, &signing_key_list, list) {
+ list_del(&kp->list);
+ kfree(kp);
+ }
+ mutex_unlock(&signing_key_list_mutex);
+
+ securityfs_remove(launch_keys);
+ securityfs_remove(provision_keys);
+ securityfs_remove(signing_keys);
+ securityfs_remove(sgx_fs);
+}
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index 07adb35c260b..c5a9df27702c 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -189,6 +189,8 @@ static void sgx_update_lepubkeyhash_msrs(u64 *lepubkeyhash, bool enforce)
* @token: a pointer an EINITTOKEN (optional)
* @secs: a pointer a SECS
* @lepubkeyhash: the desired value for IA32_SGXLEPUBKEYHASHx MSRs
+ * @launch_signer: a pointer to a function returning possible
+ * launch signers
*
* Execute ENCLS[EINIT], writing the IA32_SGXLEPUBKEYHASHx MSRs according
* to @lepubkeyhash (if possible and necessary).
@@ -198,13 +200,30 @@ static void sgx_update_lepubkeyhash_msrs(u64 *lepubkeyhash, bool enforce)
* -errno or SGX error on failure
*/
int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
- struct sgx_epc_page *secs, u64 *lepubkeyhash)
+ struct sgx_epc_page *secs, u64 *lepubkeyhash,
+ uint8_t * (*launch_signer)(uint8_t *))
{
int ret;
+ uint8_t *signer;
+
+ if (launch_signer) {
+ signer = (*launch_signer)(NULL);
+ while (signer) {
+ pr_debug("%s: Trying signer: %*phN\n", __FILE__, 32,
+ signer);
+ preempt_disable();
+ sgx_update_lepubkeyhash_msrs((u64 *) signer, true);
+ ret = __einit(sigstruct, token, sgx_epc_addr(secs));
+ preempt_enable();
+ if (!ret)
+ return ret;
+ signer = (*launch_signer)(signer);
+ }
+ return ret;
+ }
- if (!boot_cpu_has(X86_FEATURE_SGX_LC))
- return __einit(sigstruct, token, sgx_epc_addr(secs));
-
+ pr_debug("%s: Setting LC register for EINIT to: %*phN.\n", __FILE__,
+ 32, lepubkeyhash);
preempt_disable();
sgx_update_lepubkeyhash_msrs(lepubkeyhash, false);
ret = __einit(sigstruct, token, sgx_epc_addr(secs));
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index 8a1dff1e5e8a..50fc9ee9d556 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -85,6 +85,7 @@ struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim);
int __sgx_free_page(struct sgx_epc_page *page);
void sgx_free_page(struct sgx_epc_page *page);
int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
- struct sgx_epc_page *secs, u64 *lepubkeyhash);
+ struct sgx_epc_page *secs, u64 *lepubkeyhash,
+ uint8_t *(*get_signer)(uint8_t *));
#endif /* _X86_SGX_H */
[-- Attachment #3: jarkko-master-SFLC.patch.asc --]
[-- Type: text/plain, Size: 455 bytes --]
-----BEGIN PGP SIGNATURE-----
iQEcBAABAgAGBQJct1mVAAoJEP1SNsFLeja67mYIAKWtG2bydNO9J9aMYBHegc/b
0uMms4rP6o5YRDoOVKMMyP5etoTQ2jRv1RUEukghnvHyMdEaad2JXcASiWmzrXTy
lQXQlb8ejIc6C2PpCPmxB9pCV8ZtSTkoCsWgc4KvgjVtCJVbamx40CqvBoibcf+9
/nFKuqhXho163cT9PdKTWuxB5vgpaMUhtediEa2NhiTp6vfsCv9VZmWTf5OpTrIc
h4ENGcHR6kJGnmbCTJlwzPgmZA2yK929MkFlOObhkexTG5xxTZlRxaveQX0QToz8
uzZ44c6a5IckNeD9yykyuC1vXdgV1tYlVJYGWhBZYMFwH2YkGsws7hlKfu51Tqw=
=8RH8
-----END PGP SIGNATURE-----
^ permalink raw reply related [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-18 17:10 ` [PATCH v20 00/28] Intel SGX1 support Dr. Greg
@ 2019-04-18 17:24 ` Dave Hansen
2019-04-19 16:24 ` Dr. Greg
2019-04-18 18:01 ` Dave Hansen
2019-04-18 18:07 ` Andy Lutomirski
2 siblings, 1 reply; 285+ messages in thread
From: Dave Hansen @ 2019-04-18 17:24 UTC (permalink / raw)
To: Dr. Greg, Jarkko Sakkinen
Cc: torvalds, linux-kernel, x86, linux-sgx, akpm,
sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes
On 4/18/19 10:10 AM, Dr. Greg wrote:
> In addition, the driver breaks all existing SGX software by breaking
> compatibility with what is a 3+ year ABI provided by the existing
> driver. This seems to contravene the well understood philosophy that
> Linux doesn't, if at all possible, break existing applications,
Sorry, that doesn't apply to out-of-tree modules. While we don't go out
of our way to intentionally break apps who are relying on out-of-tree
modules, we also don't go our of or way to keep them working.
Please stop asking about this. I don't see any route where it's going
to change.
Companies ideally shouldn't be getting their customers hooked on
out-of-tree ABIs and customers should consume out-of-tree ABIs
*expecting* them to break in the future.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-18 17:10 ` [PATCH v20 00/28] Intel SGX1 support Dr. Greg
2019-04-18 17:24 ` Dave Hansen
@ 2019-04-18 18:01 ` Dave Hansen
2019-04-19 14:17 ` Dr. Greg
2019-04-18 18:07 ` Andy Lutomirski
2 siblings, 1 reply; 285+ messages in thread
From: Dave Hansen @ 2019-04-18 18:01 UTC (permalink / raw)
To: Dr. Greg, Jarkko Sakkinen
Cc: torvalds, linux-kernel, x86, linux-sgx, akpm,
sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes
On 4/18/19 10:10 AM, Dr. Greg wrote:
> Both the current controls for enclave access to the PROVISION
> attribute and the security controls that are being proposed to emerge
> for the driver, sometime in the future, suffer from being dependent on
> discretionary access controls, ie. file privileges, that can be
> defeated by a privilege escalation attack. Those of us building
> architectures on top of this technology have a need to certify that an
> application will provide security contracts robust in the face of a
> privilege escalation event or platform compromise.
I'm not following.
Are you saying that the implementation here is too permissive with the
enclaves that are allowed to run? Because it's too permissive, this
leaves us vulnerable to SGX being used to conceal a cache attack?
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-18 17:10 ` [PATCH v20 00/28] Intel SGX1 support Dr. Greg
2019-04-18 17:24 ` Dave Hansen
2019-04-18 18:01 ` Dave Hansen
@ 2019-04-18 18:07 ` Andy Lutomirski
2 siblings, 0 replies; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-18 18:07 UTC (permalink / raw)
To: Dr. Greg
Cc: Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Dave Hansen, Christopherson, Sean J, nhorman,
npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao,
Andy Shevchenko, Thomas Gleixner, Svahn, Kai, Borislav Petkov,
Josh Triplett, Andrew Lutomirski, Huang, Kai, David Rientjes
On Thu, Apr 18, 2019 at 10:11 AM Dr. Greg <greg@enjellic.com> wrote:
>
> On Wed, Apr 17, 2019 at 01:39:10PM +0300, Jarkko Sakkinen wrote:
>
> Good morning, I hope this note finds the impending end of the work
> week going well for everyone.
>
> First of all, a thank you to Jarkko for his efforts in advancing this
> driver, a process that can be decidedly thankless. Our apologies in
> advance for contributing to this phenomenon.
>
> > Intel(R) SGX is a set of CPU instructions that can be used by
> > applications to set aside private regions of code and data. The code
> > outside the enclave is disallowed to access the memory inside the
> > enclave by the CPU access control. In a way you can think that SGX
> > provides inverted sandbox. It protects the application from a
> > malicious host.
>
> Unfortunately, in its current implementation, the driver will not
> protect a host from malicious enclaves. If it advances in its current
> form, the mainline Linux kernel will be implementing a driver with
> known and documented security issues.
"I can use this kernel or CPU feature to do something unpleasant that
I could do anyway, if less nicely" is a far cry from "known and
documented security issues".
We've hashed this out to death. Once SGX is upstream, feel free to
submit patches.
>
> In addition, the driver breaks all existing SGX software by breaking
> compatibility with what is a 3+ year ABI provided by the existing
> driver. This seems to contravene the well understood philosophy that
> Linux doesn't, if at all possible, break existing applications,
> something that we believe can be easily prevented if those interested
> in these issues choose to read on.
As I understand it, Cedric is going to submit a patch for this
shortly, assuming I have correctly guessed what supposed ABI break
you're talking about.
In the mean time, I need to correct you on a critical point. Linux
has *never* had a policy that it will retain compatibility with an API
that wasn't upstream in the first place.
[removed extensive verbiage. Dr. Greg, if you want your emails to be
read, please try to be more concise, and please try to repeat yourself
less.]
> The attached patch, against the jarkko-sgx/master branch of Jarkko's
> driver repository, provides a framework that implements
> cryptographically secure enclave initialization policies. The patch
> and it's signature are also available from the following URL's:
>
> ftp://ftp.idfusion.net/pub/idfusion/jarkko-master-SFLC.patch
I just spend several minutes trying to read your code. It would
benefit dramatically from some attempt at documentation, and, at the
very least, you can't have a function foo(type *ptr) that does
something almost completely different when ptr == NULL and when ptr !=
NULL. For a concrete example, consider sgx_launch_control(). If you
pass NULL, then there's a vaguely credible argument that the function
does what the name suggests. If you pass non-NULL, it doesn't. The
result is bug-prone and unreadable.
If I'm understanding it right, it causes the SGX ABI to be almost
completely incompatible between the case where the list of launch
signers is empty and the case where the list is non-empty. This is a
non-starter. We're also extensively discussed how launch enclaves
would be supported if we were to support them, and the generally
agreed-upon solution was that the *kernel* would handle running a
launch enclave. Jarkko even has code for this, but it's tabled until
someone comes up with a credible argument that we *want* to support
launch enclaves.
> The policy architecture allows just about any conceivable
> initialization policy to be implemented and in any combination.
No it doesn't, because it requires the application (or its runtime or
SDK or whatever) to bundle the launch enclave that implements the
fancy policy, which means that the platform owner actually can't swap
out the policy without breaking all the applications. This is the
primary reason that, if Linux were to support LE-based launch control,
it would do so in the kernel.
> If the platform owner chooses to do nothing, the driver will
> initialize any enclave that is presented to it. FWIW, we have close
> to a century of enterprise system's administration experience on our
> team and we could not envision any competent system's administrator,
> concerned about security, who would allow such a configuration.
A sufficiently careful administrator who wants to protect against
enclave malware might want the ability to revoke permission to run a
specific enclave, which your approach can't do. Again, this has all
been covered extensively.
To be crystal clear, i fully agree that we are likely to eventually
want some kind of in-kernel launch policy. But I don't think that
defining such a policy should block the upstreaming of the driver, and
I don't think that your proposal for how the policy should work is an
acceptable proposal.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 16/28] x86/sgx: Add provisioning
2019-04-17 10:39 ` [PATCH v20 16/28] x86/sgx: Add provisioning Jarkko Sakkinen
@ 2019-04-19 3:06 ` Huang, Kai
2019-04-23 14:33 ` Jarkko Sakkinen
2019-04-24 1:34 ` Jethro Beekman
1 sibling, 1 reply; 285+ messages in thread
From: Huang, Kai @ 2019-04-19 3:06 UTC (permalink / raw)
To: jarkko.sakkinen, linux-kernel, linux-sgx, x86
Cc: Svahn, Kai, nhorman, jmorris, Christopherson, Sean J, josh, tglx,
Ayoun, Serge, Huang, Haitao, linux-security-module, akpm,
npmccallum, rientjes, luto, Katz-zamir, Shay, Hansen, Dave, bp,
serge, andriy.shevchenko
On Wed, 2019-04-17 at 13:39 +0300, Jarkko Sakkinen wrote:
> In order to provide a mechanism for devilering provisoning rights:
>
> 1. Add a new device file /dev/sgx/provision that works as a token for
> allowing an enclave to have the provisioning privileges.
> 2. Add a new ioctl called SGX_IOC_ENCLAVE_SET_ATTRIBUTE that accepts the
> following data structure:
>
> struct sgx_enclave_set_attribute {
> __u64 addr;
> __u64 attribute_fd;
> };
>
> A daemon could sit on top of /dev/sgx/provision and send a file
> descriptor of this file to a process that needs to be able to provision
> enclaves.
>
> The way this API is used is straight-forward. Lets assume that dev_fd is
> a handle to /dev/sgx/enclave and prov_fd is a handle to
> /dev/sgx/provision. You would allow SGX_IOC_ENCLAVE_CREATE to
> initialize an enclave with the PROVISIONKEY attribute by
>
> params.addr = <enclave address>;
> params.token_fd = prov_fd;
>
Should be params.attribute_fd = prov_fd;
Thanks,
-Kai
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-18 18:01 ` Dave Hansen
@ 2019-04-19 14:17 ` Dr. Greg
2019-04-19 14:25 ` Dave Hansen
2019-04-19 15:27 ` Andy Lutomirski
0 siblings, 2 replies; 285+ messages in thread
From: Dr. Greg @ 2019-04-19 14:17 UTC (permalink / raw)
To: Dave Hansen
Cc: Jarkko Sakkinen, torvalds, linux-kernel, x86, linux-sgx, akpm,
sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes
On Thu, Apr 18, 2019 at 11:01:00AM -0700, Dave Hansen wrote:
Good morning to everyone.
> On 4/18/19 10:10 AM, Dr. Greg wrote:
> > Both the current controls for enclave access to the PROVISION
> > attribute and the security controls that are being proposed to emerge
> > for the driver, sometime in the future, suffer from being dependent on
> > discretionary access controls, ie. file privileges, that can be
> > defeated by a privilege escalation attack. Those of us building
> > architectures on top of this technology have a need to certify that an
> > application will provide security contracts robust in the face of a
> > privilege escalation event or platform compromise.
> I'm not following.
>
> Are you saying that the implementation here is too permissive with
> the enclaves that are allowed to run? Because it's too permissive,
> this leaves us vulnerable to SGX being used to conceal a cache
> attack?
I believe that would be the conclusion of a dispassionate observer who
has followed this conversation and read the paper that I provided a
link to.
For the benefit of those with a disinclination to read, particularly
16 page research papers, the following link provides a summary of the
issues at hand.
https://www.securityweek.com/intel-sgx-can-be-abused-hide-advanced-malware-researchers
Of relevance to this conversation is Intel Security's official
response to the paper, which is as follows:
"The value of Intel SGX is to execute code in a protected enclave;
however, Intel SGX does not guarantee that the code executed in the
enclave is from a trusted source. In all cases, we recommend
utilizing programs, files, apps and plugins from trusted sources,"
Intel said.
The issue is not as much the ABI break but the following facts that
are at hand:
1.) The proposed mainline driver offers no cryptographic or
architecturally relevant security controls for ensuring that enclaves
are from a trusted source.
2.) Based on Andy's comments there may be a disinclination to ever
provide those controls.
3.) The approach we propose addresses these issues while imposing no
functional limitations on how Linux platform owners can use enclave
technology.
Seems like a win.
There you go, one sentence replies.
Dr. Greg
As always,
Dr. G.W. Wettstein, Ph.D. Enjellic Systems Development, LLC.
4206 N. 19th Ave. Specializing in information infra-structure
Fargo, ND 58102 development.
PH: 701-281-1686
FAX: 701-281-3949 EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"Laugh now but you won't be laughing when we find you laying on the
side of the road dead."
-- Betty Wettstein
At the Lake
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 14:17 ` Dr. Greg
@ 2019-04-19 14:25 ` Dave Hansen
2019-04-19 15:27 ` Andy Lutomirski
1 sibling, 0 replies; 285+ messages in thread
From: Dave Hansen @ 2019-04-19 14:25 UTC (permalink / raw)
To: Dr. Greg
Cc: Jarkko Sakkinen, torvalds, linux-kernel, x86, linux-sgx, akpm,
sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes
On 4/19/19 7:17 AM, Dr. Greg wrote:
> 3.) The approach we propose addresses these issues while imposing no
> functional limitations on how Linux platform owners can use enclave
> technology.
Let's say v20 gets merged. Is there any reason your approach can not be
rebased on top of that code, reviewed and merged later?
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 14:17 ` Dr. Greg
2019-04-19 14:25 ` Dave Hansen
@ 2019-04-19 15:27 ` Andy Lutomirski
2019-04-19 19:38 ` Jethro Beekman
1 sibling, 1 reply; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-19 15:27 UTC (permalink / raw)
To: Dr. Greg
Cc: Dave Hansen, Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, Christopherson, Sean J, nhorman,
npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao,
Andy Shevchenko, Thomas Gleixner, Svahn, Kai, Borislav Petkov,
Josh Triplett, Andrew Lutomirski, Huang, Kai, David Rientjes
> On Apr 19, 2019, at 7:17 AM, Dr. Greg <greg@enjellic.com> wrote:
>
> On Thu, Apr 18, 2019 at 11:01:00AM -0700, Dave Hansen wrote:
> "The value of Intel SGX is to execute code in a protected enclave;
> however, Intel SGX does not guarantee that the code executed in the
> enclave is from a trusted source. In all cases, we recommend
> utilizing programs, files, apps and plugins from trusted sources,"
> Intel said.
Linux *has* mechanisms to enforce the provenance of code, and they
have nothing to do with SGX. Off the top of my head, there’s IMA,
SELinux (depending on policy), and dm-verity.
So it seems to me that our bases are already pretty well covered. I
see two cases where some additional protection for SGX might make
sense:
1. You care more about the provenance of enclaves than the provenance
of normal code. (“You” is the platform owner, not a remote party
verifying SGX quotes.”) There are any number of solutions that could
work here, and not all of them involve crypto.
2. You care about the case where the kernel is compromised. In this
case, nothing that's been discussed helps much on an FLC system, and
even the pre-LC systems aren't a whole lot better given the lack of
init token revocation.
But I think we may be missing a much bigger issue that does need
consideration before the driver gets merged. We're all focusing on
*additional* SGX protections, but I'm not even sure we have the SGX
protections up to snuff with the rest of the system. There are many,
many Linux systems that enforce a policy that *all* executable text
needs to come from a verified source. On these systems, you can't
mmap some writable memory, write to it, and then change it to
executable. (Obviously, JITs either don't work or need special
permissions on these systems.)
Unless I'm missing it, the current SGX API is entirely incompatible
with this model -- the host process supplies text *bytes* to the
kernel, and the kernel merrily loads those bytes into executable
enclave memory. Whoops!
I think we may need to change the API so that enclaves are loaded from
a file where the contents of the file are in some appropriate format.
(The file should, at least, contain MRENCLAVE, but various antivirus
tools would much prefer if the actual enclave contents were in the
file.) It's not entirely clear that the enclave text and data need to
be in the file, since they're covered by the hash.) Then, to start an
enclave, you pass an fd to the file to the SGX driver, and the SGX
driver parses out the relevant data initializes the enclave. Before
this happens, the driver could call into IMA and LSM hooks, and the
driver would also verify that the file didn't come from a noexec
filesystem.
I suppose another approach would be to treat SGX the same way that
ld.so is treated, mostly by requiring that the buffers passed to the
driver that contain text be marked executable. This seems quite a bit
weakter to me.
What do you all think?
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-18 17:24 ` Dave Hansen
@ 2019-04-19 16:24 ` Dr. Greg
2019-04-19 16:39 ` Dave Hansen
0 siblings, 1 reply; 285+ messages in thread
From: Dr. Greg @ 2019-04-19 16:24 UTC (permalink / raw)
To: Dave Hansen
Cc: Jarkko Sakkinen, torvalds, linux-kernel, x86, linux-sgx, akpm,
sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes
On Thu, Apr 18, 2019 at 10:24:42AM -0700, Dave Hansen wrote:
Good morning again.
> On 4/18/19 10:10 AM, Dr. Greg wrote:
> > In addition, the driver breaks all existing SGX software by breaking
> > compatibility with what is a 3+ year ABI provided by the existing
> > driver. This seems to contravene the well understood philosophy that
> > Linux doesn't, if at all possible, break existing applications,
> Sorry, that doesn't apply to out-of-tree modules. While we don't go
> out of our way to intentionally break apps who are relying on
> out-of-tree modules, we also don't go our of or way to keep them
> working.
Yes, there is no question that we understand this concept.
The salient point is that when given an opportunity to preserve and
transition an existing development community, provide an
architecturally relevant driver and to impose no restrictions on how a
new, as yet untested and undesigned security architecture can emerge,
the decision is made to break all compatibility.
> Please stop asking about this. I don't see any route where it's going
> to change.
Which goes to my first e-mail where I noted this was about idealogy
rather then technology.
Nothing wrong with that as long as we are intellectually honest.
> Companies ideally shouldn't be getting their customers hooked on
> out-of-tree ABIs and customers should consume out-of-tree ABIs
> *expecting* them to break in the future.
At the risk of being indelicate, it was your company that hooked the
SGX development community on out-of-tree driver ABI's and software.
We are just trying to find a mutually beneficial and productive path
forward.
On that note.
One of the issues we have raised in multiple missives, that remains
unaddressed, was the notion that the proposed driver may not work on
all SGX hardware moving forward. Is there going to be an OEM mandated
requirement, enforced by Intel licensing, that all SGX capable
platforms will implement Flexible Launch Control?
For those following along at home, here is a link to the Intel
Security announcements made at RSA-2019 in February:
https://newsroom.intel.com/news/rsa-2019-intel-partner-ecosystem-offer-new-silicon-enabled-security-solutions/
Of relevant note is the section 'Operational Control':
"Intel is delivering a new capability called flexible launch control
that enables a company's data center operations to set and manage
their own unique security policies for launching enclaves as well as
providing controlled access to sensitive platform identification
information. This capability is currently available on Intel
SGX-enabled Intel Xeon E Processors and some Intel NUC's".
FLC is primarily about supporting Data-Center Attestation Services
(DCAS) on XEON class servers. New technologies are released on NUC's
since those are the platforms that Intel seems to target for developer
experimentation.
We have had some experience with legal and liability sensitivities
surrounding security in general and SGX in particular. Absent an
official policy statement, it is a really open question whether this
driver will be universally useful, with the end result being a fair
amount of chaos for the Linux SGX community.
As opposed to Windows, which will have a known and stable ABI that
works on any SGX capable hardware.
As I noted in my first e-mail yesterday, we anticipated this and our
architecture provides a path forward for resolving this issue as well.
Have a good weekend.
Dr. Greg
As always,
Dr. G.W. Wettstein, Ph.D. Enjellic Systems Development, LLC.
4206 N. 19th Ave. Specializing in information infra-structure
Fargo, ND 58102 development.
PH: 701-281-1686
FAX: 701-281-3949 EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"If you get to thinkin' you're a person of some influence, try
orderin' somebody else's dog around."
-- Cowboy Wisdom
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 16:24 ` Dr. Greg
@ 2019-04-19 16:39 ` Dave Hansen
0 siblings, 0 replies; 285+ messages in thread
From: Dave Hansen @ 2019-04-19 16:39 UTC (permalink / raw)
To: Dr. Greg
Cc: Jarkko Sakkinen, torvalds, linux-kernel, x86, linux-sgx, akpm,
sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes
On 4/19/19 9:24 AM, Dr. Greg wrote:
>> Companies ideally shouldn't be getting their customers hooked on
>> out-of-tree ABIs and customers should consume out-of-tree ABIs
>> *expecting* them to break in the future.
> At the risk of being indelicate, it was your company that hooked the
> SGX development community on out-of-tree driver ABI's and software.
I would encourage anyone who has been impacted by this to communicate
that back to the folks at Intel from whom they received the out-of-tree
code about the impact.
> Is there going to be an OEM mandated requirement, enforced by Intel
> licensing, that all SGX capable platforms will implement Flexible
> Launch Control?
Heck if I know. I don't think LKML is a great place to discuss Intel
licensing requirements.
What I *do* know is that when builds a platform without Flexible Launch
Control, Linux does not support SGX on that platform. I guess that
could be spelled out in some documentation explicitly, if it isn't already.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 15:27 ` Andy Lutomirski
@ 2019-04-19 19:38 ` Jethro Beekman
2019-04-19 20:39 ` Thomas Gleixner
0 siblings, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-04-19 19:38 UTC (permalink / raw)
To: Andy Lutomirski, Dr. Greg
Cc: Dave Hansen, Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, Christopherson, Sean J, nhorman,
npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao,
Andy Shevchenko, Thomas Gleixner, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
On 2019-04-19 08:27, Andy Lutomirski wrote:
> There are many,
> many Linux systems that enforce a policy that *all* executable text
> needs to come from a verified source. On these systems, you can't
> mmap some writable memory, write to it, and then change it to
> executable.
How is this implemented on those systems? AFAIK there's no kernel config
option that changes the semantics of mmap as you describe.
--
Jethro Beekman | Fortanix
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 19:38 ` Jethro Beekman
@ 2019-04-19 20:39 ` Thomas Gleixner
2019-04-19 20:46 ` Jethro Beekman
0 siblings, 1 reply; 285+ messages in thread
From: Thomas Gleixner @ 2019-04-19 20:39 UTC (permalink / raw)
To: Jethro Beekman
Cc: Andy Lutomirski, Dr. Greg, Dave Hansen, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Fri, 19 Apr 2019, Jethro Beekman wrote:
> On 2019-04-19 08:27, Andy Lutomirski wrote:
> > There are many,
> > many Linux systems that enforce a policy that *all* executable text
> > needs to come from a verified source. On these systems, you can't
> > mmap some writable memory, write to it, and then change it to
> > executable.
>
> How is this implemented on those systems? AFAIK there's no kernel config
> option that changes the semantics of mmap as you describe.
That has nothing to do with mmap() semantics. You mmap() writeable memory
and then you change the permissions via mprotect(). mprotect() calls into
LSM and depending on policy and security model this will reject the
request.
Andy was pointing out that the SGX ioctl bypasses the LSM mechanics which
is obviously a bad thing.
Thanks,
tglx
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 20:39 ` Thomas Gleixner
@ 2019-04-19 20:46 ` Jethro Beekman
2019-04-19 20:50 ` Thomas Gleixner
2019-04-19 21:05 ` Jethro Beekman
0 siblings, 2 replies; 285+ messages in thread
From: Jethro Beekman @ 2019-04-19 20:46 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Andy Lutomirski, Dr. Greg, Dave Hansen, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On 2019-04-19 13:39, Thomas Gleixner wrote:
> On Fri, 19 Apr 2019, Jethro Beekman wrote:
>
>> On 2019-04-19 08:27, Andy Lutomirski wrote:
>>> There are many,
>>> many Linux systems that enforce a policy that *all* executable text
>>> needs to come from a verified source. On these systems, you can't
>>> mmap some writable memory, write to it, and then change it to
>>> executable.
>>
>> How is this implemented on those systems? AFAIK there's no kernel config
>> option that changes the semantics of mmap as you describe.
>
> That has nothing to do with mmap() semantics. You mmap() writeable memory
> and then you change the permissions via mprotect(). mprotect() calls into
> LSM and depending on policy and security model this will reject the
> request.
>
> Andy was pointing out that the SGX ioctl bypasses the LSM mechanics which
> is obviously a bad thing.
We could modify the driver such that when you call ioctl EADD, the page
table permissions need to be the PAGEINFO.SECINFO.FLAGS | PROT_WRITE,
otherwise you get EPERM or so. After EADD, if you want, you can restrict
the page table permissions again using mprotect but the page table
permissions don't really matter for SGX.
--
Jethro Beekman | Fortanix
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 20:46 ` Jethro Beekman
@ 2019-04-19 20:50 ` Thomas Gleixner
2019-04-19 20:54 ` Jethro Beekman
2019-04-19 21:05 ` Jethro Beekman
1 sibling, 1 reply; 285+ messages in thread
From: Thomas Gleixner @ 2019-04-19 20:50 UTC (permalink / raw)
To: Jethro Beekman
Cc: Andy Lutomirski, Dr. Greg, Dave Hansen, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Fri, 19 Apr 2019, Jethro Beekman wrote:
> On 2019-04-19 13:39, Thomas Gleixner wrote:
> > On Fri, 19 Apr 2019, Jethro Beekman wrote:
> >
> >> On 2019-04-19 08:27, Andy Lutomirski wrote:
> >>> There are many,
> >>> many Linux systems that enforce a policy that *all* executable text
> >>> needs to come from a verified source. On these systems, you can't
> >>> mmap some writable memory, write to it, and then change it to
> >>> executable.
> >>
> >> How is this implemented on those systems? AFAIK there's no kernel config
> >> option that changes the semantics of mmap as you describe.
> >
> > That has nothing to do with mmap() semantics. You mmap() writeable memory
> > and then you change the permissions via mprotect(). mprotect() calls into
> > LSM and depending on policy and security model this will reject the
> > request.
> >
> > Andy was pointing out that the SGX ioctl bypasses the LSM mechanics which
> > is obviously a bad thing.
>
> We could modify the driver such that when you call ioctl EADD, the page
> table permissions need to be the PAGEINFO.SECINFO.FLAGS | PROT_WRITE,
> otherwise you get EPERM or so. After EADD, if you want, you can restrict
> the page table permissions again using mprotect but the page table
> permissions don't really matter for SGX.
And the point of that is? That you still can cirumvent LSM for feeding
executable code into SGX.
No, we are not making special cases and exceptions for SGX.
Thanks,
tglx
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 20:50 ` Thomas Gleixner
@ 2019-04-19 20:54 ` Jethro Beekman
2019-04-19 21:15 ` Andy Lutomirski
0 siblings, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-04-19 20:54 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Andy Lutomirski, Dr. Greg, Dave Hansen, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On 2019-04-19 13:50, Thomas Gleixner wrote:
> On Fri, 19 Apr 2019, Jethro Beekman wrote:
>> On 2019-04-19 13:39, Thomas Gleixner wrote:
>>> On Fri, 19 Apr 2019, Jethro Beekman wrote:
>>>
>>>> On 2019-04-19 08:27, Andy Lutomirski wrote:
>>>>> There are many,
>>>>> many Linux systems that enforce a policy that *all* executable text
>>>>> needs to come from a verified source. On these systems, you can't
>>>>> mmap some writable memory, write to it, and then change it to
>>>>> executable.
>>>>
>>>> How is this implemented on those systems? AFAIK there's no kernel config
>>>> option that changes the semantics of mmap as you describe.
>>>
>>> That has nothing to do with mmap() semantics. You mmap() writeable memory
>>> and then you change the permissions via mprotect(). mprotect() calls into
>>> LSM and depending on policy and security model this will reject the
>>> request.
>>>
>>> Andy was pointing out that the SGX ioctl bypasses the LSM mechanics which
>>> is obviously a bad thing.
>>
>> We could modify the driver such that when you call ioctl EADD, the page
>> table permissions need to be the PAGEINFO.SECINFO.FLAGS | PROT_WRITE,
>> otherwise you get EPERM or so. After EADD, if you want, you can restrict
>> the page table permissions again using mprotect but the page table
>> permissions don't really matter for SGX.
>
> And the point of that is? That you still can cirumvent LSM for feeding
> executable code into SGX.
How? LSM would see that you're trying to map a page RWX so you can do
your ioctl?
> No, we are not making special cases and exceptions for SGX.
Maybe I didn't express myself clearly? I don't think I was suggesting
anything like that.
--
Jethro Beekman | Fortanix
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 20:46 ` Jethro Beekman
2019-04-19 20:50 ` Thomas Gleixner
@ 2019-04-19 21:05 ` Jethro Beekman
1 sibling, 0 replies; 285+ messages in thread
From: Jethro Beekman @ 2019-04-19 21:05 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Andy Lutomirski, Dr. Greg, Dave Hansen, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On 2019-04-19 13:46, Jethro Beekman wrote:
> On 2019-04-19 13:39, Thomas Gleixner wrote:
>> On Fri, 19 Apr 2019, Jethro Beekman wrote:
>>
>>> On 2019-04-19 08:27, Andy Lutomirski wrote:
>>>> There are many,
>>>> many Linux systems that enforce a policy that *all* executable text
>>>> needs to come from a verified source. On these systems, you can't
>>>> mmap some writable memory, write to it, and then change it to
>>>> executable.
>>>
>>> How is this implemented on those systems? AFAIK there's no kernel config
>>> option that changes the semantics of mmap as you describe.
>>
>> That has nothing to do with mmap() semantics. You mmap() writeable memory
>> and then you change the permissions via mprotect(). mprotect() calls into
>> LSM and depending on policy and security model this will reject the
>> request.
>>
>> Andy was pointing out that the SGX ioctl bypasses the LSM mechanics which
>> is obviously a bad thing.
>
> We could modify the driver such that when you call ioctl EADD, the page
> table permissions need to be the PAGEINFO.SECINFO.FLAGS | PROT_WRITE,
> otherwise you get EPERM or so. After EADD, if you want, you can restrict
Actually, I don't think you even need to include PAGEINFO.SECINFO.FLAGS,
you just need to ensure PROT_WRITE. Regular page table checks take care
of PAGEINFO.SECINFO.FLAGS.
--
Jethro Beekman | Fortanix
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 20:54 ` Jethro Beekman
@ 2019-04-19 21:15 ` Andy Lutomirski
2019-04-19 21:19 ` Jethro Beekman
0 siblings, 1 reply; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-19 21:15 UTC (permalink / raw)
To: Jethro Beekman
Cc: Thomas Gleixner, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
> On Apr 19, 2019, at 1:54 PM, Jethro Beekman <jethro@fortanix.com> wrote:
>
>> On 2019-04-19 13:50, Thomas Gleixner wrote:
>>> On Fri, 19 Apr 2019, Jethro Beekman wrote:
>>>> On 2019-04-19 13:39, Thomas Gleixner wrote:
>>>>> On Fri, 19 Apr 2019, Jethro Beekman wrote:
>>>>>
>>>>>> On 2019-04-19 08:27, Andy Lutomirski wrote:
>>>>>> There are many,
>>>>>> many Linux systems that enforce a policy that *all* executable text
>>>>>> needs to come from a verified source. On these systems, you can't
>>>>>> mmap some writable memory, write to it, and then change it to
>>>>>> executable.
>>>>>
>>>>> How is this implemented on those systems? AFAIK there's no kernel config
>>>>> option that changes the semantics of mmap as you describe.
>>>>
>>>> That has nothing to do with mmap() semantics. You mmap() writeable memory
>>>> and then you change the permissions via mprotect(). mprotect() calls into
>>>> LSM and depending on policy and security model this will reject the
>>>> request.
>>>>
>>>> Andy was pointing out that the SGX ioctl bypasses the LSM mechanics which
>>>> is obviously a bad thing.
>>>
>>> We could modify the driver such that when you call ioctl EADD, the page
>>> table permissions need to be the PAGEINFO.SECINFO.FLAGS | PROT_WRITE,
>>> otherwise you get EPERM or so. After EADD, if you want, you can restrict
>>> the page table permissions again using mprotect but the page table
>>> permissions don't really matter for SGX.
>>
>> And the point of that is? That you still can cirumvent LSM for feeding
>> executable code into SGX.
>
> How? LSM would see that you're trying to map a page RWX so you can do
> your ioctl?
With plain mmap() + mprotect(), the LSM will prevent you from making memory that *was* writable executable. This is by design and SELinux supports it. I don’t remember the name of the associated SELinux permission off the top of my head.
If we start enforcing equivalent rules on SGX, then the current API will simply not allow enclaves to be loaded — no matter how you slice it, loading an enclave with the current API is indistinguishable from making arbitrary data executable.
Put another way, you can compile your enclave, ship it as a file, and get it appropriately verified (by LSM attribute, IMA, dm-verity, whatever) and run it, but, with the current API, the kernel has no way of knowing that the userspace enclave loader is actually reading it from the file in question.
So I think we need to work on the API.
>
>> No, we are not making special cases and exceptions for SGX.
>
> Maybe I didn't express myself clearly? I don't think I was suggesting
> anything like that.
>
> --
> Jethro Beekman | Fortanix
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 21:15 ` Andy Lutomirski
@ 2019-04-19 21:19 ` Jethro Beekman
2019-04-19 21:31 ` Andy Lutomirski
2019-04-19 21:34 ` Thomas Gleixner
0 siblings, 2 replies; 285+ messages in thread
From: Jethro Beekman @ 2019-04-19 21:19 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Thomas Gleixner, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
Jethro Beekman | Fortanix
On 2019-04-19 14:15, Andy Lutomirski wrote:
>
>
>
>
>> On Apr 19, 2019, at 1:54 PM, Jethro Beekman <jethro@fortanix.com> wrote:
>>
>>> On 2019-04-19 13:50, Thomas Gleixner wrote:
>>>> On Fri, 19 Apr 2019, Jethro Beekman wrote:
>>>>> On 2019-04-19 13:39, Thomas Gleixner wrote:
>>>>>> On Fri, 19 Apr 2019, Jethro Beekman wrote:
>>>>>>
>>>>>>> On 2019-04-19 08:27, Andy Lutomirski wrote:
>>>>>>> There are many,
>>>>>>> many Linux systems that enforce a policy that *all* executable text
>>>>>>> needs to come from a verified source. On these systems, you can't
>>>>>>> mmap some writable memory, write to it, and then change it to
>>>>>>> executable.
>>>>>>
>>>>>> How is this implemented on those systems? AFAIK there's no kernel config
>>>>>> option that changes the semantics of mmap as you describe.
>>>>>
>>>>> That has nothing to do with mmap() semantics. You mmap() writeable memory
>>>>> and then you change the permissions via mprotect(). mprotect() calls into
>>>>> LSM and depending on policy and security model this will reject the
>>>>> request.
>>>>>
>>>>> Andy was pointing out that the SGX ioctl bypasses the LSM mechanics which
>>>>> is obviously a bad thing.
>>>>
>>>> We could modify the driver such that when you call ioctl EADD, the page
>>>> table permissions need to be the PAGEINFO.SECINFO.FLAGS | PROT_WRITE,
>>>> otherwise you get EPERM or so. After EADD, if you want, you can restrict
>>>> the page table permissions again using mprotect but the page table
>>>> permissions don't really matter for SGX.
>>>
>>> And the point of that is? That you still can cirumvent LSM for feeding
>>> executable code into SGX.
>>
>> How? LSM would see that you're trying to map a page RWX so you can do
>> your ioctl?
>
> With plain mmap() + mprotect(), the LSM will prevent you from making memory that *was* writable executable. This is by design and SELinux supports it. I don’t remember the name of the associated SELinux permission off the top of my head.
>
> If we start enforcing equivalent rules on SGX, then the current API will simply not allow enclaves to be loaded — no matter how you slice it, loading an enclave with the current API is indistinguishable from making arbitrary data executable.
Yes this is exactly what I intended here: a very simple change that
stops SGX from confusing LSM. Just by enforcing that everything that
looks like a memory write (EADD, EAUG, EDBGWR, etc.) actually requires
write permissions, reality and LSM should be on the same page.
If you want to go further and actually allow this behavior when your LSM
would otherwise prohibit it, presumably the same workarounds that exist
for JITs can be used for SGX.
--
Jethro Beekman | Fortanix
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 21:19 ` Jethro Beekman
@ 2019-04-19 21:31 ` Andy Lutomirski
2019-04-19 21:35 ` Jethro Beekman
2019-04-19 21:34 ` Thomas Gleixner
1 sibling, 1 reply; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-19 21:31 UTC (permalink / raw)
To: Jethro Beekman
Cc: Thomas Gleixner, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
> On Apr 19, 2019, at 2:19 PM, Jethro Beekman <jethro@fortanix.com> wrote:
>
>> .
>>
>> If we start enforcing equivalent rules on SGX, then the current API will simply not allow enclaves to be loaded — no matter how you slice it, loading an enclave with the current API is indistinguishable from making arbitrary data executable.
>
> Yes this is exactly what I intended here: a very simple change that
> stops SGX from confusing LSM. Just by enforcing that everything that
> looks like a memory write (EADD, EAUG, EDBGWR, etc.) actually requires
> write permissions, reality and LSM should be on the same page.
>
> If you want to go further and actually allow this behavior when your LSM
> would otherwise prohibit it, presumably the same workarounds that exist
> for JITs can be used for SGX.
>
>
I do think we need to follow LSM rules. But my bigger point is that there are policies that don’t allow JIT at all. I think we should arrange the SGX API so it’s still usable when such a policy is in effect.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 21:19 ` Jethro Beekman
2019-04-19 21:31 ` Andy Lutomirski
@ 2019-04-19 21:34 ` Thomas Gleixner
1 sibling, 0 replies; 285+ messages in thread
From: Thomas Gleixner @ 2019-04-19 21:34 UTC (permalink / raw)
To: Jethro Beekman
Cc: Andy Lutomirski, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
[-- Attachment #1: Type: text/plain, Size: 1134 bytes --]
On Fri, 19 Apr 2019, Jethro Beekman wrote:
> On 2019-04-19 14:15, Andy Lutomirski wrote:
> > With plain mmap() + mprotect(), the LSM will prevent you from making
> > memory that *was* writable executable. This is by design and SELinux
> > supports it. I don’t remember the name of the associated SELinux
> > permission off the top of my head.
> >
> > If we start enforcing equivalent rules on SGX, then the current API
> > will simply not allow enclaves to be loaded — no matter how you slice
> > it, loading an enclave with the current API is indistinguishable from
> > making arbitrary data executable.
> >
> Yes this is exactly what I intended here: a very simple change that
> stops SGX from confusing LSM. Just by enforcing that everything that
> looks like a memory write (EADD, EAUG, EDBGWR, etc.) actually requires
> write permissions, reality and LSM should be on the same page.
And how so? You create writeable AND executable memory. That's a nono and
you can argue in circles, that's not going to change with any of your
proposed changes. Andy clearly made a proposal which solves it in a proper
way.
Thanks,
tglx
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 21:31 ` Andy Lutomirski
@ 2019-04-19 21:35 ` Jethro Beekman
2019-04-19 21:38 ` Thomas Gleixner
0 siblings, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-04-19 21:35 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Thomas Gleixner, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On 2019-04-19 14:31, Andy Lutomirski wrote:
> I do think we need to follow LSM rules. But my bigger point is that there are policies that don’t allow JIT at all. I think we should arrange the SGX API so it’s still usable when such a policy is in effect.
I don't think we need to arrange that right now. This patch set needs to
be merged after more than 2 years of development. I'd like to avoid
introducing any more big changes. Let's just do what I described to make
LSM not broken, which is a minimal change to the current approach. We
can adjust the API later to support the use case you describe.
--
Jethro Beekman | Fortanix
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 21:35 ` Jethro Beekman
@ 2019-04-19 21:38 ` Thomas Gleixner
2019-04-19 21:56 ` Jethro Beekman
0 siblings, 1 reply; 285+ messages in thread
From: Thomas Gleixner @ 2019-04-19 21:38 UTC (permalink / raw)
To: Jethro Beekman
Cc: Andy Lutomirski, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
[-- Attachment #1: Type: text/plain, Size: 859 bytes --]
On Fri, 19 Apr 2019, Jethro Beekman wrote:
> On 2019-04-19 14:31, Andy Lutomirski wrote:
> > I do think we need to follow LSM rules. But my bigger point is that
> > there are policies that don’t allow JIT at all. I think we should
> > arrange the SGX API so it’s still usable when such a policy is in
> > effect.
> I don't think we need to arrange that right now. This patch set needs to
> be merged after more than 2 years of development. I'd like to avoid
We merge stuff when it is ready and not when someone declares that it needs
to be merged.
> introducing any more big changes. Let's just do what I described to make
> LSM not broken, which is a minimal change to the current approach. We
> can adjust the API later to support the use case you describe.
You are working around LSM nothing else and that's just not going to fly.
Thanks,
tglx
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 21:38 ` Thomas Gleixner
@ 2019-04-19 21:56 ` Jethro Beekman
2019-04-20 5:42 ` Thomas Gleixner
2019-04-22 16:26 ` Andy Lutomirski
0 siblings, 2 replies; 285+ messages in thread
From: Jethro Beekman @ 2019-04-19 21:56 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Andy Lutomirski, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On 2019-04-19 14:34, Thomas Gleixner wrote:
> And how so? You create writeable AND executable memory. That's a nono and
> you can argue in circles, that's not going to change with any of your
> proposed changes.
On 2019-04-19 14:38, Thomas Gleixner wrote:
> You are working around LSM nothing else and that's just not going to fly.
Based on your comments, I'm still unsure if we're on the same page with
regards to what I'm proposing.
Here's a regular non-SGX flow that LSM would likely prevent:
mmap(PROT_READ|PROT_WRITE)
memcpy()
mmap(PROT_READ|PROT_EXEC) <-- denied by LSM
Or just something based on regular PT permissions:
mmap(PROT_READ|PROT_EXEC)
memcpy() <-- SIGSEGV
Now, the equivalent for SGX:
mmap(PROT_READ|PROT_WRITE)
ioctl(EADD)
mmap(PROT_READ|PROT_EXEC) <-- denied by LSM
This works fine with v20 as-is. However, consider the equivalent of the
PT-based flow:
mmap(PROT_READ|PROT_EXEC)
ioctl(EADD) <-- no error!
It's not me that's working around the LSM, it's the SGX driver! It's
writing to memory that's not marked writable! The fundamental issue here
is that the SGX instruction set has several instructions that bypass the
page table permission bits, and this is (naturally) confusing to any
kind of reference monitor like the LSM framework. You can come up with
similar scenarios that involve PROT_READ|PROT_WRITE|PROT_EXEC or
ptrace(PTRACE_POKETEXT). So, clearly, the proper way to fix this failure
of complete mediation is by enforcing appropriate page-table permissions
even on the SGX instructions that don't do it themselves. Just make any
implicit memory access look like a regular memory access and now
everyone is on the same page (pun intended).
--
Jethro Beekman | Fortanix
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 21:56 ` Jethro Beekman
@ 2019-04-20 5:42 ` Thomas Gleixner
2019-04-20 16:02 ` Dr. Greg
2019-04-22 16:26 ` Andy Lutomirski
1 sibling, 1 reply; 285+ messages in thread
From: Thomas Gleixner @ 2019-04-20 5:42 UTC (permalink / raw)
To: Jethro Beekman
Cc: Andy Lutomirski, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On Fri, 19 Apr 2019, Jethro Beekman wrote:
> On 2019-04-19 14:34, Thomas Gleixner wrote:
> > And how so? You create writeable AND executable memory. That's a nono and
> > you can argue in circles, that's not going to change with any of your
> > proposed changes.
>
> On 2019-04-19 14:38, Thomas Gleixner wrote:
> > You are working around LSM nothing else and that's just not going to fly.
>
> Based on your comments, I'm still unsure if we're on the same page with
> regards to what I'm proposing.
>
> Here's a regular non-SGX flow that LSM would likely prevent:
>
> mmap(PROT_READ|PROT_WRITE)
> memcpy()
> mmap(PROT_READ|PROT_EXEC) <-- denied by LSM
>
> Or just something based on regular PT permissions:
>
> mmap(PROT_READ|PROT_EXEC)
> memcpy() <-- SIGSEGV
>
> Now, the equivalent for SGX:
>
> mmap(PROT_READ|PROT_WRITE)
> ioctl(EADD)
> mmap(PROT_READ|PROT_EXEC) <-- denied by LSM
This is completely irrelevant, really.
The point is that the SGX driver loads and executes arbitrary data which is
handed in from user space via an ioctl w/o any chance of verifying where
that comes from.
What Andy proposed is to open a file with the SGX payload and hand in the
file descriptor. That way LSM can decide whether this is allowed or denied
based on the file descriptor and whatever the security model/policy is in a
particular setup.
Right know the SGX driver and its proposed API prevent any form of LSM
auditing and whatever permission checks you had in mind won't change that
at all.
Thanks,
tglx
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-20 5:42 ` Thomas Gleixner
@ 2019-04-20 16:02 ` Dr. Greg
2019-04-22 15:01 ` Sean Christopherson
0 siblings, 1 reply; 285+ messages in thread
From: Dr. Greg @ 2019-04-20 16:02 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Jethro Beekman, Andy Lutomirski, Andy Lutomirski, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On Sat, Apr 20, 2019 at 07:42:13AM +0200, Thomas Gleixner wrote:
Good morning/weekend to everyone.
> On Fri, 19 Apr 2019, Jethro Beekman wrote:
> > On 2019-04-19 14:34, Thomas Gleixner wrote:
> > > And how so? You create writeable AND executable memory. That's a nono and
> > > you can argue in circles, that's not going to change with any of your
> > > proposed changes.
> > On 2019-04-19 14:38, Thomas Gleixner wrote:
> > > You are working around LSM nothing else and that's just not going to fly.
> >
> > Based on your comments, I'm still unsure if we're on the same page with
> > regards to what I'm proposing.
> >
> > Here's a regular non-SGX flow that LSM would likely prevent:
> >
> > mmap(PROT_READ|PROT_WRITE)
> > memcpy()
> > mmap(PROT_READ|PROT_EXEC) <-- denied by LSM
> >
> > Or just something based on regular PT permissions:
> >
> > mmap(PROT_READ|PROT_EXEC)
> > memcpy() <-- SIGSEGV
> >
> > Now, the equivalent for SGX:
> >
> > mmap(PROT_READ|PROT_WRITE)
> > ioctl(EADD)
> > mmap(PROT_READ|PROT_EXEC) <-- denied by LSM
> This is completely irrelevant, really.
>
> The point is that the SGX driver loads and executes arbitrary data
> which is handed in from user space via an ioctl w/o any chance of
> verifying where that comes from.
>
> What Andy proposed is to open a file with the SGX payload and hand
> in the file descriptor. That way LSM can decide whether this is
> allowed or denied based on the file descriptor and whatever the
> security model/policy is in a particular setup.
>
> Right know the SGX driver and its proposed API prevent any form of
> LSM auditing and whatever permission checks you had in mind won't
> change that at all.
I don't even want to remotely get in the middle of this arguement,
since we are probably already persona-non-grata for having stimulated
a discussion that has slowed upstreaming, but some comments to assist
and clarify further progress on the driver.
We understand and support the need for the LSM to trap these events,
but what does LSM provenance mean if the platform is compromised?
That is, technically, the target application for SGX technology.
This issue is what drove our concern for having ring-0 based
enforcement of who can initialize an enclave. As Andy has pointed
out, the mmap semantics of the SGX driver allow the introduction of
executable code that bypasses LSM enforcement, but the platform owner,
with appropriate enforcements on an FLC platform, has cryptographic
verification and trust for the origin and provenance of that executable
code.
It may be possible to load the executable code into enclave memory but
the processor will refuse to access the memory until the EINIT
instruction validates that the loaded code is compliant with the
enclave metadata and measurement. That is why we were argueing for,
simple, ring-0 based verification of the key signature that authorizes
the EINIT instruction to complete.
From a more practical perspective, an enclave shared image is a rather
complex beast, that contains a lot of metadata. An application has to
open the shared image file and parse the metadata in order to properly
construct the enclave image from the text in the image file. I
believe in practice that will effectively activate LSM recognition of
the executable code.
That obviously leaves a cunning adversary an opportunity to teleport a
simple binary file or network stream onto a platform and load and
execute the contents. But that takes us back to the need for ring-0
enforcement of enclave initialization... :-)
We wrote a major enhancement to the Linux IMA architecture to
implement introspection of behavioral namespaces. The actor/subject
events get forwarded up into a modeling engine running inside of a
namespace specific SGX enclave instance that issues a system call to
instruct an LSM that we wrote, and that pairs with the IMA extension,
to 'discipline' the process if it is acting outside its behavioral
specification.
We will verify with Jarkko's current HEAD, but I believe it is
consistently trapping access to the shared image file and hence the
text of the enclave.
> Thanks,
>
> tglx
Have a good weekend.
Dr. Greg
As always,
Dr. G.W. Wettstein, Ph.D. Enjellic Systems Development, LLC.
4206 N. 19th Ave. Specializing in information infra-structure
Fargo, ND 58102 development.
PH: 701-281-1686
FAX: 701-281-3949 EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"Simplicity is prerequisite for reliability."
-- Edsger W. Dijkstra
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-20 16:02 ` Dr. Greg
@ 2019-04-22 15:01 ` Sean Christopherson
2019-04-22 16:24 ` Dr. Greg
0 siblings, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-04-22 15:01 UTC (permalink / raw)
To: Dr. Greg
Cc: Thomas Gleixner, Jethro Beekman, Andy Lutomirski,
Andy Lutomirski, Dave Hansen, Jarkko Sakkinen, Linus Torvalds,
LKML, X86 ML, linux-sgx, Andrew Morton, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On Sat, Apr 20, 2019 at 11:02:47AM -0500, Dr. Greg wrote:
> We understand and support the need for the LSM to trap these events,
> but what does LSM provenance mean if the platform is compromised?
> That is, technically, the target application for SGX technology.
No, it's not. Protecting the kernel/platform from a malicious entity is
outside the scope of SGX.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-22 15:01 ` Sean Christopherson
@ 2019-04-22 16:24 ` Dr. Greg
2019-04-22 16:48 ` Sean Christopherson
0 siblings, 1 reply; 285+ messages in thread
From: Dr. Greg @ 2019-04-22 16:24 UTC (permalink / raw)
To: Sean Christopherson
Cc: Dr. Greg, Thomas Gleixner, Jethro Beekman, Andy Lutomirski,
Andy Lutomirski, Dave Hansen, Jarkko Sakkinen, Linus Torvalds,
LKML, X86 ML, linux-sgx, Andrew Morton, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On Mon, Apr 22, 2019 at 08:01:19AM -0700, Sean Christopherson wrote:
Good morning to everyone, I hope the week is starting well.
> On Sat, Apr 20, 2019 at 11:02:47AM -0500, Dr. Greg wrote:
> > We understand and support the need for the LSM to trap these
> > events, but what does LSM provenance mean if the platform is
> > compromised? That is, technically, the target application for SGX
> > technology.
> No, it's not. Protecting the kernel/platform from a malicious
> entity is outside the scope of SGX.
You must have misinterpreted my statement, providing security
guarantees in the face of a compromised platform is exactly what SGX
was designed to do and is how Intel is marketing the technology.
From the first paragraph (Introduction) in the following document:
https://software.intel.com/sites/default/files/managed/50/8c/Intel-SGX-Product-Brief.pdf
"Intel Software Guard Extensions (Intel SGX) protects selected code
and data from disclosure or modification. Developers can partition
their application into CPU hardened 'enclaves' or protected areas of
execution that increase security even on compromised platforms".
In addition, one of the major use cases for this technology is the
ability to push data and application code up onto cloud platforms with
a guarantee that not even the platform owner or administrators can
compromise the integrity or confidentiality of the code and data.
As I've noted before, from an OS driver perspective, security and
privacy models which are dependent on an uncompromised platform and
user privileges are inconsistent with the SGX security architecture.
Doing SGX right is about applying cryptographically defined provenance
and integrity models.
Our autonomous introspection technology uses SGX to protect the
platform at large but we are unique with respect to how the technology
is being applied.
Have a good day.
Dr. Greg
As always,
Dr. G.W. Wettstein, Ph.D. Enjellic Systems Development, LLC.
4206 N. 19th Ave. Specializing in information infra-structure
Fargo, ND 58102 development.
PH: 701-281-1686
FAX: 701-281-3949 EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"You and Uncle Pete drank the whole thing? That was a $250.00 bottle
of whisky.
Yeah, it was good."
-- Rick Engen
Resurrection.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-19 21:56 ` Jethro Beekman
2019-04-20 5:42 ` Thomas Gleixner
@ 2019-04-22 16:26 ` Andy Lutomirski
2019-04-23 21:15 ` Jethro Beekman
2019-05-10 17:23 ` Xing, Cedric
1 sibling, 2 replies; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-22 16:26 UTC (permalink / raw)
To: Jethro Beekman
Cc: Thomas Gleixner, Andy Lutomirski, Dr. Greg, Dave Hansen,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
> On Apr 19, 2019, at 2:56 PM, Jethro Beekman <jethro@fortanix.com> wrote:
>
>> On 2019-04-19 14:34, Thomas Gleixner wrote:
>> And how so? You create writeable AND executable memory. That's a nono and
>> you can argue in circles, that's not going to change with any of your
>> proposed changes.
>
>> On 2019-04-19 14:38, Thomas Gleixner wrote:
>> You are working around LSM nothing else and that's just not going to fly.
>
> Based on your comments, I'm still unsure if we're on the same page with
> regards to what I'm proposing.
>
> Here's a regular non-SGX flow that LSM would likely prevent:
>
> mmap(PROT_READ|PROT_WRITE)
> memcpy()
> mmap(PROT_READ|PROT_EXEC) <-- denied by LSM
>
> Or just something based on regular PT permissions:
>
> mmap(PROT_READ|PROT_EXEC)
> memcpy() <-- SIGSEGV
>
> Now, the equivalent for SGX:
>
> mmap(PROT_READ|PROT_WRITE)
> ioctl(EADD)
> mmap(PROT_READ|PROT_EXEC) <-- denied by LSM
>
> This works fine with v20 as-is. However, consider the equivalent of the
> PT-based flow:
>
> mmap(PROT_READ|PROT_EXEC)
> ioctl(EADD) <-- no error!
Indeed!
>
> It's not me that's working around the LSM, it's the SGX driver! It's
> writing to memory that's not marked writable! The fundamental issue here
> is that the SGX instruction set has several instructions that bypass the
> page table permission bits, and this is (naturally) confusing to any
> kind of reference monitor like the LSM framework. You can come up with
> similar scenarios that involve PROT_READ|PROT_WRITE|PROT_EXEC or
> ptrace(PTRACE_POKETEXT). So, clearly, the proper way to fix this failure
> of complete mediation is by enforcing appropriate page-table permissions
> even on the SGX instructions that don't do it themselves. Just make any
> implicit memory access look like a regular memory access and now
> everyone is on the same page (pun intended).
>
I agree that we should do this. But then what? Once this gets fixed,
the ioctl(EADD) fails and the driver becomes rather less useful, and
we feel rather silly if we’ve merged it in this state.
So I think we need a better EADD ioctl that explicitly does work on
PROT_READ|PROT_EXEC enclave memory but makes up for by validating the
*source* of the data. The effect will be similar to mapping a
labeled, appraised, etc file as PROT_EXEC. Maybe, in extreme
pseudocode:
fd = open(“/dev/sgx/enclave”);
ioctl(fd, SGX_CREATE_FROM_FILE, file_fd);
// fd now inherits the LSM label from the file, or is otherwise marked.
mmap(fd, PROT_READ|PROT_EXEC, ...);
I suppose that an alternative would be to delegate all the EADD calls
to a privileged daemon, but that’s nasty.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-22 16:24 ` Dr. Greg
@ 2019-04-22 16:48 ` Sean Christopherson
2019-04-22 16:55 ` Linus Torvalds
0 siblings, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-04-22 16:48 UTC (permalink / raw)
To: Dr. Greg
Cc: Thomas Gleixner, Jethro Beekman, Andy Lutomirski,
Andy Lutomirski, Dave Hansen, Jarkko Sakkinen, Linus Torvalds,
LKML, X86 ML, linux-sgx, Andrew Morton, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On Mon, Apr 22, 2019 at 11:24:11AM -0500, Dr. Greg wrote:
> On Mon, Apr 22, 2019 at 08:01:19AM -0700, Sean Christopherson wrote:
>
> Good morning to everyone, I hope the week is starting well.
>
> > On Sat, Apr 20, 2019 at 11:02:47AM -0500, Dr. Greg wrote:
> > > We understand and support the need for the LSM to trap these
> > > events, but what does LSM provenance mean if the platform is
> > > compromised? That is, technically, the target application for SGX
> > > technology.
>
> > No, it's not. Protecting the kernel/platform from a malicious
> > entity is outside the scope of SGX.
>
> You must have misinterpreted my statement, providing security
> guarantees in the face of a compromised platform is exactly what SGX
> was designed to do and is how Intel is marketing the technology.
Right, and loading a malicious enclave doesn't change those guarantees
(for other enclaves). Ergo, restricting which enclaves can execute is
orthogonal to the security provided by SGX.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-22 16:48 ` Sean Christopherson
@ 2019-04-22 16:55 ` Linus Torvalds
2019-04-22 17:17 ` Sean Christopherson
0 siblings, 1 reply; 285+ messages in thread
From: Linus Torvalds @ 2019-04-22 16:55 UTC (permalink / raw)
To: Sean Christopherson
Cc: Dr. Greg, Thomas Gleixner, Jethro Beekman, Andy Lutomirski,
Andy Lutomirski, Dave Hansen, Jarkko Sakkinen, LKML, X86 ML,
linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Mon, Apr 22, 2019 at 9:48 AM Sean Christopherson
<sean.j.christopherson@intel.com> wrote:
>
> Right, and loading a malicious enclave doesn't change those guarantees
> (for other enclaves). Ergo, restricting which enclaves can execute is
> orthogonal to the security provided by SGX.
But it is absolutely worth noting that TSX made a lot of attacks both
easier to _do_, and also easier to _hide_.
All while being basically completely worthless technology to everybody
except for some silly SAP benchmark.
So it is definitely worth at least discussing the downsides of SGX. If
it ends up being another technology that makes it easier to create
malware, without actually having a lot of _good_ software use it, the
patches to enable it should make damn sure that the upsides actually
outweigh the downsides.
And if the current setup basically is "you have to disable reasonable
SElinux protections that lots of distros use today", I think it's
entirely reasonable saying "the downsides are bigger than the
upsides".
Linus
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-22 16:55 ` Linus Torvalds
@ 2019-04-22 17:17 ` Sean Christopherson
2019-04-23 9:11 ` Dr. Greg
0 siblings, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-04-22 17:17 UTC (permalink / raw)
To: Linus Torvalds
Cc: Dr. Greg, Thomas Gleixner, Jethro Beekman, Andy Lutomirski,
Andy Lutomirski, Dave Hansen, Jarkko Sakkinen, LKML, X86 ML,
linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Mon, Apr 22, 2019 at 09:55:47AM -0700, Linus Torvalds wrote:
> On Mon, Apr 22, 2019 at 9:48 AM Sean Christopherson
> <sean.j.christopherson@intel.com> wrote:
> >
> > Right, and loading a malicious enclave doesn't change those guarantees
> > (for other enclaves). Ergo, restricting which enclaves can execute is
> > orthogonal to the security provided by SGX.
>
> But it is absolutely worth noting that TSX made a lot of attacks both
> easier to _do_, and also easier to _hide_.
>
> All while being basically completely worthless technology to everybody
> except for some silly SAP benchmark.
>
> So it is definitely worth at least discussing the downsides of SGX. If
> it ends up being another technology that makes it easier to create
> malware, without actually having a lot of _good_ software use it, the
> patches to enable it should make damn sure that the upsides actually
> outweigh the downsides.
>
> And if the current setup basically is "you have to disable reasonable
> SElinux protections that lots of distros use today", I think it's
> entirely reasonable saying "the downsides are bigger than the
> upsides".
I'm not arguing against SGX playing nice with SELinux/LSMs, actually the
opposite. I completely agree that enclaves should be subject to LSM
restrictions.
AIUI, Dr. Greg is proposing a framework that uses SGX's launch control
mechanism to restrict what enclaves can run. My point is that restricting
what enclaves can run is about protecting the kernel and/or platform, not
the enclaves themselves, i.e. using launch control instead of, or in
addition to, LSMs doesn't change the security guarantees of SGX.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 09/28] x86/sgx: Add ENCLS architectural error codes
2019-04-17 10:39 ` [PATCH v20 09/28] x86/sgx: Add ENCLS architectural error codes Jarkko Sakkinen
@ 2019-04-22 21:35 ` Sean Christopherson
0 siblings, 0 replies; 285+ messages in thread
From: Sean Christopherson @ 2019-04-22 21:35 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: linux-kernel, x86, linux-sgx, akpm, dave.hansen, nhorman,
npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
rientjes
On Wed, Apr 17, 2019 at 01:39:19PM +0300, Jarkko Sakkinen wrote:
> The SGX architecture defines an extensive set of error codes that are
> used by ENCL{S,U,V} instructions to provide software with (somewhat)
> precise error information. Though they are architectural, define the
> known error codes in a separate file from sgx_arch.h so that they can
> be exposed to userspace. For some ENCLS leafs, e.g. EINIT, returning
> the exact error code on failure can enable userspace to make informed
> decisions when an operation fails.
>
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
> Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> ---
Your SOB needs to be last. Several other patches have the same issue,
e.g. 10-13, 15 and 17.
See commit 24a2bb90741b ("docs: Clarify the usage and sign-off requirements
for Co-developed-by") in branch docs-next of git://git.lwn.net/linux.git.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver
2019-04-17 10:39 ` [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver Jarkko Sakkinen
@ 2019-04-22 21:58 ` Sean Christopherson
2019-04-23 23:29 ` Jethro Beekman
0 siblings, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-04-22 21:58 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: linux-kernel, x86, linux-sgx, akpm, dave.hansen, nhorman,
npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
rientjes, Jethro Beekman
+Cc Jethro
On Wed, Apr 17, 2019 at 01:39:25PM +0300, Jarkko Sakkinen wrote:
> Intel Software Guard eXtensions (SGX) is a set of CPU instructions that
> can be used by applications to set aside private regions of code and
> data. The code outside the enclave is disallowed to access the memory
> inside the enclave by the CPU access control.
>
> This commit adds the Linux SGX Enclave Driver that provides an ioctl API
> to manage enclaves. The address range for an enclave, commonly referred
> as ELRANGE in the documentation (e.g. Intel SDM), is reserved with
> mmap() against /dev/sgx/enclave. After that a set ioctls is used to
> build the enclave to the ELRANGE.
>
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
> Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
> Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
> Co-developed-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
> Signed-off-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
> Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
> Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
> ---
...
> +#ifdef CONFIG_ACPI
> +static struct acpi_device_id sgx_device_ids[] = {
> + {"INT0E0C", 0},
> + {"", 0},
> +};
> +MODULE_DEVICE_TABLE(acpi, sgx_device_ids);
> +#endif
> +
> +static struct platform_driver sgx_drv = {
> + .probe = sgx_drv_probe,
> + .remove = sgx_drv_remove,
> + .driver = {
> + .name = "sgx",
> + .acpi_match_table = ACPI_PTR(sgx_device_ids),
> + },
> +};
Where do we stand on removing the ACPI and platform_driver dependencies?
Can we get rid of them sooner rather than later?
Now that the core SGX code is approaching stability, I'd like to start
sending RFCs for the EPC virtualization and KVM bits to hash out that side
of things. The ACPI crud is the last chunk of code that would require
non-trivial changes to the core SGX code for the proposed virtualization
implementation. I'd strongly prefer to get it out of the way before
sending the KVM RFCs.
> +static int __init sgx_drv_subsys_init(void)
> +{
> + int ret;
> +
> + ret = bus_register(&sgx_bus_type);
> + if (ret)
> + return ret;
> +
> + ret = alloc_chrdev_region(&sgx_devt, 0, SGX_DRV_NR_DEVICES, "sgx");
> + if (ret < 0) {
> + bus_unregister(&sgx_bus_type);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void sgx_drv_subsys_exit(void)
> +{
> + bus_unregister(&sgx_bus_type);
> + unregister_chrdev_region(sgx_devt, SGX_DRV_NR_DEVICES);
> +}
> +
> +static int __init sgx_drv_init(void)
> +{
> + int ret;
> +
> + ret = sgx_drv_subsys_init();
> + if (ret)
> + return ret;
> +
> + ret = platform_driver_register(&sgx_drv);
> + if (ret)
> + sgx_drv_subsys_exit();
> +
> + return ret;
> +}
> +module_init(sgx_drv_init);
> +
> +static void __exit sgx_drv_exit(void)
> +{
> + platform_driver_unregister(&sgx_drv);
> + sgx_drv_subsys_exit();
> +}
> +module_exit(sgx_drv_exit);
^ permalink raw reply [flat|nested] 285+ messages in thread
* [RFC PATCH v1 0/3] An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (28 preceding siblings ...)
2019-04-18 17:10 ` [PATCH v20 00/28] Intel SGX1 support Dr. Greg
@ 2019-04-23 0:37 ` Cedric Xing
2019-04-24 6:26 ` [RFC PATCH v2 " Cedric Xing
` (3 more replies)
2019-04-23 0:37 ` [RFC PATCH v1 1/3] selftests/x86: Fixed Makefile for SGX selftest Cedric Xing
` (3 subsequent siblings)
33 siblings, 4 replies; 285+ messages in thread
From: Cedric Xing @ 2019-04-23 0:37 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, Hansen, Dave, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, andriy.shevchenko,
tglx, Svahn, Kai, bp, josh, luto, Kai, rientjes, Jarkko Sakkinen,
Cedric Xing
The current proposed __vdso_sgx_enter_enclave() requires enclaves to preserve
%rsp, which prohibits enclaves from allocating space on the untrusted stack.
However, there are existing enclaves (e.g. those built with current Intel SGX
SDK libraries) relying on the untrusted stack for passing parameters to
untrusted functions (aka. o-calls), which requires allocating space on the
untrusted stack by enclaves. And given its simplicity and convenience, it could
be desired by future SGX applications as well.
This patchset introduces a new ABI for __vdso_sgx_enter_enclave() to anchor its
stack frame on %rbp (instead of %rsp), so as to allow enclaves to "push" onto
the untrusted stack by decrementing the untrusted %rsp. Additionally, this new
__vdso_sgx_enter_enclave() will take one more parameter - a callback function,
to be invoked upon all enclave exits (both AEX and normal exits). The
callback function will be given the value of %rsp left off by the enclave,
so that data "pushed" by the enclave (if any) could be addressed/accessed.
Please note that the callback function is optional, and if not supplied
(i.e. null), __vdso_sgx_enter_enclave() will just return (i.e. behave the
same as the current implementation) after the enclave exits (or AEX
due to exceptions).
The SGX selftest is augmented to test out the new callback interface, and to
serve as a simple example to showcase how to use the callback interface in
practice.
Reference:
* This patchset is based upon SGX1 patch v20
(https://lkml.org/lkml/2019/4/17/344) by Jarkko Sakkinen
Cedric Xing (3):
selftests/x86: Fixed Makefile for SGX selftest
x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing
on untrusted stack
selftests/x86: Augment SGX selftest to test new
__vdso_sgx_enter_enclave() and its callback interface
arch/x86/entry/vdso/vsgx_enter_enclave.S | 156 ++++++++++++---------
arch/x86/include/uapi/asm/sgx.h | 14 +-
tools/testing/selftests/x86/Makefile | 12 +-
tools/testing/selftests/x86/sgx/Makefile | 45 +++---
tools/testing/selftests/x86/sgx/main.c | 123 +++++++++++++---
tools/testing/selftests/x86/sgx/sgx_call.S | 40 +++++-
6 files changed, 264 insertions(+), 126 deletions(-)
--
2.17.1
^ permalink raw reply [flat|nested] 285+ messages in thread
* [RFC PATCH v1 1/3] selftests/x86: Fixed Makefile for SGX selftest
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (29 preceding siblings ...)
2019-04-23 0:37 ` [RFC PATCH v1 0/3] An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack Cedric Xing
@ 2019-04-23 0:37 ` Cedric Xing
2019-04-23 0:37 ` [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack Cedric Xing
` (2 subsequent siblings)
33 siblings, 0 replies; 285+ messages in thread
From: Cedric Xing @ 2019-04-23 0:37 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, Hansen, Dave, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, andriy.shevchenko,
tglx, Svahn, Kai, bp, josh, luto, Kai, rientjes, Jarkko Sakkinen,
Cedric Xing
The original x86/sgx/Makefile doesn't work when 'x86/sgx' is specified as the
test target. This patch fixes that problem, along with minor changes to the
dependencies between 'x86' and 'x86/sgx' in selftests/x86/Makefile.
Signed-off-by: Cedric Xing <cedric.xing@intel.com>
---
tools/testing/selftests/x86/Makefile | 12 +++----
tools/testing/selftests/x86/sgx/Makefile | 45 +++++++++---------------
2 files changed, 22 insertions(+), 35 deletions(-)
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 4fc9a42f56ea..1294c5f5b6ca 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -70,11 +70,11 @@ all_32: $(BINARIES_32)
all_64: $(BINARIES_64)
-all_64: $(SUBDIRS_64)
- @for DIR in $(SUBDIRS_64); do \
- BUILD_TARGET=$(OUTPUT)/$$DIR; \
- mkdir $$BUILD_TARGET -p; \
- make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+all_64: | $(SUBDIRS_64)
+ @for DIR in $|; do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ mkdir $$BUILD_TARGET -p; \
+ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
done
EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64)
@@ -90,7 +90,7 @@ ifeq ($(CAN_BUILD_I386)$(CAN_BUILD_X86_64),01)
all: warn_32bit_failure
warn_32bit_failure:
- @echo "Warning: you seem to have a broken 32-bit build" 2>&1; \
+ @echo "Warning: you seem to have a broken 32-bit build" 2>&1; \
echo "environment. This will reduce test coverage of 64-bit" 2>&1; \
echo "kernels. If you are using a Debian-like distribution," 2>&1; \
echo "try:"; 2>&1; \
diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile
index 1fd6f2708e81..3af15d7c8644 100644
--- a/tools/testing/selftests/x86/sgx/Makefile
+++ b/tools/testing/selftests/x86/sgx/Makefile
@@ -2,47 +2,34 @@ top_srcdir = ../../../../..
include ../../lib.mk
-HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC
-ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \
+ifeq ($(shell $(CC) -dumpmachine | cut --delimiter=- -f1),x86_64)
+all: all_64
+endif
+
+HOST_CFLAGS := -Wall -Werror -g $(INCLUDES)
+ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIE \
-fno-stack-protector -mrdrnd $(INCLUDES)
TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx
all_64: $(TEST_CUSTOM_PROGS)
-$(TEST_CUSTOM_PROGS): $(OUTPUT)/main.o $(OUTPUT)/sgx_call.o \
- $(OUTPUT)/encl_piggy.o
+$(TEST_CUSTOM_PROGS): main.c sgx_call.S $(OUTPUT)/encl_piggy.o
$(CC) $(HOST_CFLAGS) -o $@ $^
-$(OUTPUT)/main.o: main.c
- $(CC) $(HOST_CFLAGS) -c $< -o $@
+$(OUTPUT)/encl_piggy.o: encl_piggy.S $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+ $(CC) $(HOST_CFLAGS) -I$(OUTPUT) -c $< -o $@
-$(OUTPUT)/sgx_call.o: sgx_call.S
- $(CC) $(HOST_CFLAGS) -c $< -o $@
-
-$(OUTPUT)/encl_piggy.o: $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
- $(CC) $(HOST_CFLAGS) -c encl_piggy.S -o $@
-
-$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf $(OUTPUT)/sgxsign
+$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf
objcopy --remove-section=.got.plt -O binary $< $@
-$(OUTPUT)/encl.elf: $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o
- $(CC) $(ENCL_CFLAGS) -T encl.lds -o $@ $^
+$(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S
+ $(CC) $(ENCL_CFLAGS) -T $^ -o $@
-$(OUTPUT)/encl.o: encl.c
- $(CC) $(ENCL_CFLAGS) -c $< -o $@
-
-$(OUTPUT)/encl_bootstrap.o: encl_bootstrap.S
- $(CC) $(ENCL_CFLAGS) -c $< -o $@
-
-$(OUTPUT)/encl.ss: $(OUTPUT)/encl.bin $(OUTPUT)/sgxsign
- $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+$(OUTPUT)/encl.ss: $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin
+ $^ $@
$(OUTPUT)/sgxsign: sgxsign.c
$(CC) -o $@ $< -lcrypto
-EXTRA_CLEAN := $(OUTPUT)/sgx-selftest $(OUTPUT)/sgx-selftest.o \
- $(OUTPUT)/sgx_call.o $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss \
- $(OUTPUT)/encl.elf $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o \
- $(OUTPUT)/sgxsign
-
-.PHONY: clean
+EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(addprefix $(OUTPUT)/, \
+ encl.elf encl.bin encl.ss encl_piggy.o sgxsign)
--
2.17.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (30 preceding siblings ...)
2019-04-23 0:37 ` [RFC PATCH v1 1/3] selftests/x86: Fixed Makefile for SGX selftest Cedric Xing
@ 2019-04-23 0:37 ` Cedric Xing
2019-04-23 1:25 ` Andy Lutomirski
2019-04-23 19:26 ` Sean Christopherson
2019-04-23 0:37 ` [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface Cedric Xing
2019-04-23 11:56 ` [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
33 siblings, 2 replies; 285+ messages in thread
From: Cedric Xing @ 2019-04-23 0:37 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, Hansen, Dave, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, andriy.shevchenko,
tglx, Svahn, Kai, bp, josh, luto, Kai, rientjes, Jarkko Sakkinen,
Cedric Xing
The previous __vdso_sgx_enter_enclave() requires enclaves to preserve %rsp,
which prohibits enclaves from allocating and passing parameters for
untrusted function calls (aka. o-calls).
This patch addresses the problem above by introducing a new ABI that preserves
%rbp instead of %rsp. Then __vdso_sgx_enter_enclave() can anchor its frame
using %rbp so that enclaves are allowed to allocate space on the untrusted
stack by decrementing %rsp. Please note that the stack space allocated in such
way will be part of __vdso_sgx_enter_enclave()'s frame so will be freed after
__vdso_sgx_enter_enclave() returns. Therefore, __vdso_sgx_enter_enclave() has
been changed to take a callback function as an optional parameter, which if
supplied, will be invoked upon enclave exits (both AEX (Asynchronous Enclave
eXit) and normal exits), with the value of %rsp left
off by the enclave as a parameter to the callback.
Here's the summary of API/ABI changes in this patch. More details could be
found in arch/x86/entry/vdso/vsgx_enter_enclave.S.
* 'struct sgx_enclave_exception' is renamed to 'struct sgx_enclave_exinfo'
because it is filled upon both AEX (i.e. exceptions) and normal enclave
exits.
* __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead of %rsp in
the previous implementation).
* __vdso_sgx_enter_enclave() takes one more parameter - a callback function to
be invoked upon enclave exits. This callback is optional, and if not
supplied, will cause __vdso_sgx_enter_enclave() to return upon enclave exits
(same behavior as previous implementation).
* The callback function is given as a parameter the value of %rsp at enclave
exit to address data "pushed" by the enclave. A positive value returned by
the callback will be treated as an ENCLU leaf for re-entering the enclave,
while a zero or negative value will be passed through as the return
value of __vdso_sgx_enter_enclave() to its caller. It's also safe to
leave callback by longjmp() or by throwing a C++ exception.
Signed-off-by: Cedric Xing <cedric.xing@intel.com>
---
arch/x86/entry/vdso/vsgx_enter_enclave.S | 156 ++++++++++++++---------
arch/x86/include/uapi/asm/sgx.h | 14 +-
2 files changed, 100 insertions(+), 70 deletions(-)
diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S
index fe0bf6671d6d..210f4366374a 100644
--- a/arch/x86/entry/vdso/vsgx_enter_enclave.S
+++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
@@ -14,88 +14,118 @@
.code64
.section .text, "ax"
-#ifdef SGX_KERNEL_DOC
/**
* __vdso_sgx_enter_enclave() - Enter an SGX enclave
*
* @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME
- * @tcs: **IN \%rbx** - TCS, must be non-NULL
- * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception' pointer
+ * @tcs: **IN 0x08(\%rsp)** - TCS, must be non-NULL
+ * @ex_info: **IN 0x10(\%rsp)** - Optional 'struct sgx_enclave_exinfo'
+ * pointer
+ * @callback: **IN 0x18(\%rsp)** - Optional callback function to be called on
+ * enclave exit or exception
*
* Return:
* **OUT \%eax** -
- * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is
- * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave faults
+ * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is not
+ * allowed, %-EFAULT if ENCLU or the enclave faults, or a non-positive value
+ * returned from ``callback`` (if one is supplied).
*
* **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant with the
- * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
- * input parameters must be passed via ``%eax``, ``%rbx`` and ``%rcx``, with
- * the return value passed via ``%eax``. All registers except ``%rsp`` must
- * be treated as volatile from the caller's perspective, including but not
- * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave
- * being run **must** preserve the untrusted ``%rsp`` and stack.
+ * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
+ * input parameters must be passed via ``%eax``, ``8(%rsp)``, ``0x10(%rsp)`` and
+ * ``0x18(%rsp)``, with the return value passed via ``%eax``. All other registers
+ * will be passed through to the enclave as is. All registers except ``%rbp``
+ * must be treated as volatile from the caller's perspective, including but not
+ * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave being
+ * run **must** preserve the untrusted ``%rbp``.
+ *
+ * ``callback`` has the following signature:
+ * int callback(long rdi, long rsi, long rdx,
+ * struct sgx_enclave_exinfo *ex_info, long r8, long r9,
+ * void *tcs, long ursp);
+ * ``callback`` **shall** follow x86_64 ABI. All GPRs **except** ``%rax``, ``%rbx``
+ * and ``rcx`` are passed through to ``callback``. ``%rdi``, ``%rsi``, ``%rdx``,
+ * ``%r8``, ``%r9``, along with the value of ``%rsp`` when the enclave exited/excepted,
+ * can be accessed directly as input parameters, while other GPRs can be
+ * accessed in assembly if needed.
+ * A positive value returned from ``callback`` will be treated as an ENCLU leaf
+ * (e.g. EENTER/ERESUME) to reenter the enclave, while 0 or a negative return
+ * value will be passed back to the caller of __vdso_sgx_enter_enclave().
+ * It is also **safe** to ``longjmp()`` out of ``callback``.
*/
-__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
- struct sgx_enclave_exception *ex_info)
-{
- if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
- return -EINVAL;
-
- if (!tcs)
- return -EINVAL;
-
- try {
- ENCLU[leaf];
- } catch (exception) {
- if (e)
- *e = exception;
- return -EFAULT;
- }
-
- return 0;
-}
-#endif
ENTRY(__vdso_sgx_enter_enclave)
- /* EENTER <= leaf <= ERESUME */
+ /* Prolog */
+ .cfi_startproc
+ push %rbp
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rbp, 0
+ mov %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+
+1: /* EENTER <= leaf <= ERESUME */
cmp $0x2, %eax
- jb bad_input
-
+ jb 6f
cmp $0x3, %eax
- ja bad_input
+ ja 6f
- /* TCS must be non-NULL */
- test %rbx, %rbx
- je bad_input
+ /* Load TCS and AEP */
+ mov 0x10(%rbp), %rbx
+ lea 2f(%rip), %rcx
- /* Save @exception_info */
- push %rcx
-
- /* Load AEP for ENCLU */
- lea 1f(%rip), %rcx
-1: enclu
-
- add $0x8, %rsp
- xor %eax, %eax
- ret
-
-bad_input:
- mov $(-EINVAL), %rax
- ret
-
-.pushsection .fixup, "ax"
- /* Re-load @exception_info and fill it (if it's non-NULL) */
-2: pop %rcx
- test %rcx, %rcx
- je 3f
+ /* Single ENCLU serving as both EENTER and AEP (ERESUME) */
+2: enclu
+ /* EEXIT path */
+ xor %ebx, %ebx
+3: mov 0x18(%rbp), %rcx
+ jrcxz 4f
mov %eax, EX_LEAF(%rcx)
- mov %di, EX_TRAPNR(%rcx)
- mov %si, EX_ERROR_CODE(%rcx)
+ jnc 4f
+ mov %di, EX_TRAPNR(%rcx)
+ mov %si, EX_ERROR_CODE(%rcx)
mov %rdx, EX_ADDRESS(%rcx)
-3: mov $(-EFAULT), %rax
+
+4: /* Call *callback if supplied */
+ mov 0x20(%rbp), %rax
+ test %rax, %rax
+ cmovz %rbx, %rax
+ jz 7f
+ /* Align stack and clear RFLAGS.DF per x86_64 ABI */
+ mov %rsp, %rbx
+ and $-0x10, %rsp
+ cld
+ /* Parameters for *callback */
+ push %rbx
+ push 0x10(%rbp)
+ /* Call via retpoline */
+ call 40f
+ /* Cleanup stack */
+ mov %rbx, %rsp
+ jmp 1b
+40: /* retpoline */
+ call 42f
+41: pause
+ lfence
+ jmp 41b
+42: mov %rax, (%rsp)
+ ret
+
+5: /* Exception path */
+ mov $-EFAULT, %ebx
+ stc
+ jmp 3b
+
+6: /* Unsupported ENCLU leaf */
+ cmp $0, %eax
+ jle 7f
+ mov $-EINVAL, %eax
+
+7: /* Epilog */
+ leave
+ .cfi_def_cfa %rsp, 8
ret
-.popsection
+ .cfi_endproc
-_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
+_ASM_VDSO_EXTABLE_HANDLE(2b, 5b)
ENDPROC(__vdso_sgx_enter_enclave)
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index 9ed690a38c70..50d2b5143e5e 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -24,7 +24,7 @@
/**
* struct sgx_enclave_create - parameter structure for the
- * %SGX_IOC_ENCLAVE_CREATE ioctl
+ * %SGX_IOC_ENCLAVE_CREATE ioctl
* @src: address for the SECS page data
*/
struct sgx_enclave_create {
@@ -33,7 +33,7 @@ struct sgx_enclave_create {
/**
* struct sgx_enclave_add_page - parameter structure for the
- * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl
+ * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl
* @addr: address within the ELRANGE
* @src: address for the page data
* @secinfo: address for the SECINFO data
@@ -49,7 +49,7 @@ struct sgx_enclave_add_page {
/**
* struct sgx_enclave_init - parameter structure for the
- * %SGX_IOC_ENCLAVE_INIT ioctl
+ * %SGX_IOC_ENCLAVE_INIT ioctl
* @sigstruct: address for the SIGSTRUCT data
*/
struct sgx_enclave_init {
@@ -66,16 +66,16 @@ struct sgx_enclave_set_attribute {
};
/**
- * struct sgx_enclave_exception - structure to report exceptions encountered in
- * __vdso_sgx_enter_enclave()
+ * struct sgx_enclave_exinfo - structure to report exceptions encountered in
+ * __vdso_sgx_enter_enclave()
*
- * @leaf: ENCLU leaf from \%eax at time of exception
+ * @leaf: ENCLU leaf from \%eax at time of exception/exit
* @trapnr: exception trap number, a.k.a. fault vector
* @error_code: exception error code
* @address: exception address, e.g. CR2 on a #PF
* @reserved: reserved for future use
*/
-struct sgx_enclave_exception {
+struct sgx_enclave_exinfo {
__u32 leaf;
__u16 trapnr;
__u16 error_code;
--
2.17.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (31 preceding siblings ...)
2019-04-23 0:37 ` [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack Cedric Xing
@ 2019-04-23 0:37 ` Cedric Xing
2019-04-23 1:29 ` Andy Lutomirski
2019-04-23 11:56 ` [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
33 siblings, 1 reply; 285+ messages in thread
From: Cedric Xing @ 2019-04-23 0:37 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, Hansen, Dave, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, andriy.shevchenko,
tglx, Svahn, Kai, bp, josh, luto, Kai, rientjes, Jarkko Sakkinen,
Cedric Xing
Given the changes to __vdso_sgx_enter_enclave(), the selftest is augmented to
test the newly added callback interface. This addtional test marks the whole
enclave range as PROT_READ, and calls mprotect() upon #PFs to add necessary PTE
permissions per PFEC (#PF Error Code) until the enclave finishes.
Signed-off-by: Cedric Xing <cedric.xing@intel.com>
---
tools/testing/selftests/x86/sgx/main.c | 123 ++++++++++++++++++---
tools/testing/selftests/x86/sgx/sgx_call.S | 40 ++++++-
2 files changed, 142 insertions(+), 21 deletions(-)
diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c
index e2265f841fb0..234cfbad14a5 100644
--- a/tools/testing/selftests/x86/sgx/main.c
+++ b/tools/testing/selftests/x86/sgx/main.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
@@ -18,6 +19,10 @@
#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h"
#include "../../../../../arch/x86/include/uapi/asm/sgx.h"
+#define _Q(x) __Q(x)
+#define __Q(x) #x
+#define ERRLN "Line " _Q(__LINE__)
+
static const uint64_t MAGIC = 0x1122334455667788ULL;
struct vdso_symtab {
@@ -138,7 +143,7 @@ static bool encl_create(int dev_fd, unsigned long bin_size,
base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED, dev_fd, 0);
if (base == MAP_FAILED) {
- perror("mmap");
+ perror(ERRLN);
return false;
}
@@ -224,24 +229,113 @@ static bool encl_load(struct sgx_secs *secs, unsigned long bin_size)
return false;
}
-void sgx_call(void *rdi, void *rsi, void *tcs,
- struct sgx_enclave_exception *exception,
- void *eenter);
+int sgx_call(void *rdi, void *rsi, long rdx, void *rcx, void *r8, void *r9,
+ void *tcs, struct sgx_enclave_exinfo *ei, void *cb, void *eenter);
+
+static void show_enclave_exinfo(const struct sgx_enclave_exinfo *exinfop,
+ const char *header)
+{
+ printf("%s: leaf:%d", header, exinfop->leaf);
+ if (exinfop->leaf != 4)
+ printf(" trap#:%d ec:%d addr:0x%llx\n", exinfop->trapnr,
+ exinfop->error_code, exinfop->address);
+ else printf("\n");
+}
+
+static void test1(void *eenter, struct sgx_secs *secs)
+{
+ uint64_t result = 0;
+ struct sgx_enclave_exinfo exinfo;
+
+ printf("[1] Entering the enclave without callback.\n");
+
+ printf("Input: 0x%lx\n Expect: Same as input\n", MAGIC);
+ sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL,
+ (void *)secs->base, &exinfo, NULL, eenter);
+ if (result != MAGIC) {
+ fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
+ exit(1);
+ }
+ printf(" Output: 0x%lx\n", result);
+
+ printf("Input: Null TCS\n Expect: #PF at EENTER\n");
+ sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL,
+ NULL, &exinfo, NULL, eenter);
+ show_enclave_exinfo(&exinfo, " Exit");
+ if (exinfo.leaf != 2 /*EENTER*/ || exinfo.trapnr != 14 /*#PF*/)
+ exit(1);
+}
+
+static int enclave_ex_callback(long rdi, long rsi, long rdx,
+ struct sgx_enclave_exinfo *ei, long r8, long r9, void *tcs, long ursp)
+{
+ show_enclave_exinfo(ei, " callback");
+
+ switch (ei->leaf)
+ {
+ case 4:
+ return 0;
+ case 3:
+ case 2:
+ if (ei->trapnr != 14 /*#PF*/ || (ei->error_code & 1) == 0) {
+ fprintf(stderr, ERRLN ": Unexpected exception\n");
+ exit(1);
+ }
+
+ if (mprotect((void*)(ei->address & -0x1000), 0x1000,
+ ((ei->error_code & 2) ? PROT_WRITE : 0) |
+ ((ei->error_code & 0x10) ? PROT_EXEC : 0) |
+ PROT_READ)) {
+ perror(ERRLN);
+ exit(1);
+ }
+
+ return ei->leaf == 2 ? -EAGAIN : ei->leaf;
+ }
+ return -EINVAL;
+}
+
+static void test2(void *eenter, struct sgx_secs *secs)
+{
+ uint64_t result = 0;
+ struct sgx_enclave_exinfo exinfo;
+
+ printf("[2] Entering the enclave with callback.\n");
+
+ printf("Input: 0x%lx\n Expect: Same as input\n", MAGIC);
+ sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL,
+ (void *)secs->base, &exinfo, enclave_ex_callback, eenter);
+ if (result != MAGIC) {
+ fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
+ exit(1);
+ }
+ printf(" Output: 0x%lx\n", result);
+
+ printf("Input: Read-only enclave (0x%lx-0x%lx)\n"
+ " Expect: #PFs to be fixed by callback\n",
+ secs->base, secs->base + (encl_bin_end - encl_bin) - 1);
+ if (mprotect((void*)secs->base, encl_bin_end - encl_bin, PROT_READ)) {
+ perror(ERRLN);
+ exit(1);
+ }
+ while (sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL,
+ (void*)secs->base, &exinfo, enclave_ex_callback,
+ eenter) == -EAGAIN);
+ show_enclave_exinfo(&exinfo, " Exit");
+ if (exinfo.leaf != 4 /*EEXIT*/)
+ exit(1);
+}
int main(int argc, char *argv[], char *envp[])
{
unsigned long bin_size = encl_bin_end - encl_bin;
unsigned long ss_size = encl_ss_end - encl_ss;
- struct sgx_enclave_exception exception;
Elf64_Sym *eenter_sym;
struct vdso_symtab symtab;
struct sgx_secs secs;
- uint64_t result = 0;
void *eenter;
void *addr;
- memset(&exception, 0, sizeof(exception));
-
addr = vdso_get_base_addr(envp);
if (!addr)
exit(1);
@@ -266,14 +360,7 @@ int main(int argc, char *argv[], char *envp[])
if (!encl_load(&secs, bin_size))
exit(1);
- printf("Input: 0x%lx\n", MAGIC);
- sgx_call((void *)&MAGIC, &result, (void *)secs.base, &exception,
- eenter);
- if (result != MAGIC) {
- fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
- exit(1);
- }
-
- printf("Output: 0x%lx\n", result);
- exit(0);
+ test1(eenter, &secs);
+ test2(eenter, &secs);
+ return 0;
}
diff --git a/tools/testing/selftests/x86/sgx/sgx_call.S b/tools/testing/selftests/x86/sgx/sgx_call.S
index 14bd0a044199..da8f687a60d2 100644
--- a/tools/testing/selftests/x86/sgx/sgx_call.S
+++ b/tools/testing/selftests/x86/sgx/sgx_call.S
@@ -7,9 +7,43 @@
.global sgx_call
sgx_call:
+ .cfi_startproc
+ push %r15
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r15, 0
+ push %r14
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r14, 0
+ push %r13
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r13, 0
+ push %r12
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r12, 0
push %rbx
- mov $0x02, %rax
- mov %rdx, %rbx
- call *%r8
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rbx, 0
+ push $0
+ .cfi_adjust_cfa_offset 8
+ push 0x48(%rsp)
+ .cfi_adjust_cfa_offset 8
+ push 0x48(%rsp)
+ .cfi_adjust_cfa_offset 8
+ push 0x48(%rsp)
+ .cfi_adjust_cfa_offset 8
+ mov $2, %eax
+ call *0x68(%rsp)
+ add $0x20, %rsp
+ .cfi_adjust_cfa_offset -0x20
pop %rbx
+ .cfi_adjust_cfa_offset -8
+ pop %r12
+ .cfi_adjust_cfa_offset -8
+ pop %r13
+ .cfi_adjust_cfa_offset -8
+ pop %r14
+ .cfi_adjust_cfa_offset -8
+ pop %r15
+ .cfi_adjust_cfa_offset -8
ret
+ .cfi_endproc
--
2.17.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-23 0:37 ` [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack Cedric Xing
@ 2019-04-23 1:25 ` Andy Lutomirski
2019-04-24 17:56 ` Xing, Cedric
2019-04-23 19:26 ` Sean Christopherson
1 sibling, 1 reply; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-23 1:25 UTC (permalink / raw)
To: Cedric Xing
Cc: LKML, X86 ML, linux-sgx, Andrew Morton, Dave, Sean J, nhorman,
npmccallum, Serge, Shay, Haitao, Andy Shevchenko,
Thomas Gleixner, Kai, Borislav Petkov, Josh Triplett,
Andrew Lutomirski, Kai, David Rientjes, Jarkko Sakkinen
On Mon, Apr 22, 2019 at 5:37 PM Cedric Xing <cedric.xing@intel.com> wrote:
>
> The previous __vdso_sgx_enter_enclave() requires enclaves to preserve %rsp,
> which prohibits enclaves from allocating and passing parameters for
> untrusted function calls (aka. o-calls).
>
> This patch addresses the problem above by introducing a new ABI that preserves
> %rbp instead of %rsp. Then __vdso_sgx_enter_enclave() can anchor its frame
> using %rbp so that enclaves are allowed to allocate space on the untrusted
> stack by decrementing %rsp. Please note that the stack space allocated in such
> way will be part of __vdso_sgx_enter_enclave()'s frame so will be freed after
> __vdso_sgx_enter_enclave() returns. Therefore, __vdso_sgx_enter_enclave() has
> been changed to take a callback function as an optional parameter, which if
> supplied, will be invoked upon enclave exits (both AEX (Asynchronous Enclave
> eXit) and normal exits), with the value of %rsp left
> off by the enclave as a parameter to the callback.
>
> Here's the summary of API/ABI changes in this patch. More details could be
> found in arch/x86/entry/vdso/vsgx_enter_enclave.S.
> * 'struct sgx_enclave_exception' is renamed to 'struct sgx_enclave_exinfo'
> because it is filled upon both AEX (i.e. exceptions) and normal enclave
> exits.
> * __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead of %rsp in
> the previous implementation).
> * __vdso_sgx_enter_enclave() takes one more parameter - a callback function to
> be invoked upon enclave exits. This callback is optional, and if not
> supplied, will cause __vdso_sgx_enter_enclave() to return upon enclave exits
> (same behavior as previous implementation).
> * The callback function is given as a parameter the value of %rsp at enclave
> exit to address data "pushed" by the enclave. A positive value returned by
> the callback will be treated as an ENCLU leaf for re-entering the enclave,
> while a zero or negative value will be passed through as the return
> value of __vdso_sgx_enter_enclave() to its caller. It's also safe to
> leave callback by longjmp() or by throwing a C++ exception.
>
> Signed-off-by: Cedric Xing <cedric.xing@intel.com>
> ---
> arch/x86/entry/vdso/vsgx_enter_enclave.S | 156 ++++++++++++++---------
> arch/x86/include/uapi/asm/sgx.h | 14 +-
> 2 files changed, 100 insertions(+), 70 deletions(-)
>
> diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> index fe0bf6671d6d..210f4366374a 100644
> --- a/arch/x86/entry/vdso/vsgx_enter_enclave.S
> +++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> @@ -14,88 +14,118 @@
> .code64
> .section .text, "ax"
>
> -#ifdef SGX_KERNEL_DOC
> /**
> * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> *
> * @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME
> - * @tcs: **IN \%rbx** - TCS, must be non-NULL
> - * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception' pointer
> + * @tcs: **IN 0x08(\%rsp)** - TCS, must be non-NULL
> + * @ex_info: **IN 0x10(\%rsp)** - Optional 'struct sgx_enclave_exinfo'
> + * pointer
> + * @callback: **IN 0x18(\%rsp)** - Optional callback function to be called on
> + * enclave exit or exception
> *
> * Return:
> * **OUT \%eax** -
> - * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is
> - * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave faults
> + * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is not
> + * allowed, %-EFAULT if ENCLU or the enclave faults, or a non-positive value
> + * returned from ``callback`` (if one is supplied).
> *
> * **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant with the
> - * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
> - * input parameters must be passed via ``%eax``, ``%rbx`` and ``%rcx``, with
> - * the return value passed via ``%eax``. All registers except ``%rsp`` must
> - * be treated as volatile from the caller's perspective, including but not
> - * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave
> - * being run **must** preserve the untrusted ``%rsp`` and stack.
> + * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
> + * input parameters must be passed via ``%eax``, ``8(%rsp)``, ``0x10(%rsp)`` and
> + * ``0x18(%rsp)``, with the return value passed via ``%eax``. All other registers
> + * will be passed through to the enclave as is. All registers except ``%rbp``
> + * must be treated as volatile from the caller's perspective, including but not
> + * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave being
> + * run **must** preserve the untrusted ``%rbp``.
> + *
> + * ``callback`` has the following signature:
> + * int callback(long rdi, long rsi, long rdx,
> + * struct sgx_enclave_exinfo *ex_info, long r8, long r9,
> + * void *tcs, long ursp);
> + * ``callback`` **shall** follow x86_64 ABI. All GPRs **except** ``%rax``, ``%rbx``
> + * and ``rcx`` are passed through to ``callback``. ``%rdi``, ``%rsi``, ``%rdx``,
> + * ``%r8``, ``%r9``, along with the value of ``%rsp`` when the enclave exited/excepted,
> + * can be accessed directly as input parameters, while other GPRs can be
> + * accessed in assembly if needed.
> + * A positive value returned from ``callback`` will be treated as an ENCLU leaf
> + * (e.g. EENTER/ERESUME) to reenter the enclave, while 0 or a negative return
"to reenter the enclave without popping the extra data pushed by the
enclave off the stack" or similar. We really don't want a situation
where someone puts all there "keep on going" logic in the callback and
the stack usage grows without bound.
> + * value will be passed back to the caller of __vdso_sgx_enter_enclave().
> + * It is also **safe** to ``longjmp()`` out of ``callback``.
I'm not sure that "safe" needs emphasis.
> */
> -__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
> - struct sgx_enclave_exception *ex_info)
> -{
> - if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
> - return -EINVAL;
> -
> - if (!tcs)
> - return -EINVAL;
> -
> - try {
> - ENCLU[leaf];
> - } catch (exception) {
> - if (e)
> - *e = exception;
> - return -EFAULT;
> - }
> -
> - return 0;
> -}
> -#endif
> ENTRY(__vdso_sgx_enter_enclave)
> - /* EENTER <= leaf <= ERESUME */
> + /* Prolog */
> + .cfi_startproc
> + push %rbp
> + .cfi_adjust_cfa_offset 8
> + .cfi_rel_offset %rbp, 0
> + mov %rsp, %rbp
> + .cfi_def_cfa_register %rbp
> +
> +1: /* EENTER <= leaf <= ERESUME */
> cmp $0x2, %eax
> - jb bad_input
> -
> + jb 6f
> cmp $0x3, %eax
> - ja bad_input
> + ja 6f
>
> - /* TCS must be non-NULL */
> - test %rbx, %rbx
> - je bad_input
> + /* Load TCS and AEP */
> + mov 0x10(%rbp), %rbx
> + lea 2f(%rip), %rcx
>
> - /* Save @exception_info */
> - push %rcx
> -
> - /* Load AEP for ENCLU */
> - lea 1f(%rip), %rcx
> -1: enclu
> -
> - add $0x8, %rsp
> - xor %eax, %eax
> - ret
> -
> -bad_input:
> - mov $(-EINVAL), %rax
> - ret
> -
> -.pushsection .fixup, "ax"
> - /* Re-load @exception_info and fill it (if it's non-NULL) */
> -2: pop %rcx
> - test %rcx, %rcx
> - je 3f
> + /* Single ENCLU serving as both EENTER and AEP (ERESUME) */
> +2: enclu
>
> + /* EEXIT path */
> + xor %ebx, %ebx
> +3: mov 0x18(%rbp), %rcx
> + jrcxz 4f
> mov %eax, EX_LEAF(%rcx)
> - mov %di, EX_TRAPNR(%rcx)
> - mov %si, EX_ERROR_CODE(%rcx)
> + jnc 4f
> + mov %di, EX_TRAPNR(%rcx)
> + mov %si, EX_ERROR_CODE(%rcx)
> mov %rdx, EX_ADDRESS(%rcx)
> -3: mov $(-EFAULT), %rax
> +
> +4: /* Call *callback if supplied */
> + mov 0x20(%rbp), %rax
> + test %rax, %rax
Maybe have a comment like "At this point, the effective return value
is in RBX. If there is no callback, then return it."
> + cmovz %rbx, %rax
> + jz 7f
> + /* Align stack and clear RFLAGS.DF per x86_64 ABI */
> + mov %rsp, %rbx
Whoa, this is too subtle here. Can you update the comment to clarify
that the uRSP value set by the enclave needs to be saved so that the
enclave can be resumed if needed?
> + and $-0x10, %rsp
> + cld
> + /* Parameters for *callback */
> + push %rbx
> + push 0x10(%rbp)
> + /* Call via retpoline */
> + call 40f
> + /* Cleanup stack */
> + mov %rbx, %rsp
To me, "Cleanup stack" makes me think that you're restoring the
original RSP, but you're actually just undoing in the stack alignment.
How about "Undo stack alignment"?
But I'm not seeing the code that causes a return value RAX <= 0 to just return.
> + jmp 1b
> +40: /* retpoline */
> + call 42f
> +41: pause
> + lfence
> + jmp 41b
> +42: mov %rax, (%rsp)
> + ret
> +
> +5: /* Exception path */
> + mov $-EFAULT, %ebx
> + stc
> + jmp 3b
> +
> +6: /* Unsupported ENCLU leaf */
> + cmp $0, %eax
> + jle 7f
> + mov $-EINVAL, %eax
> +
> +7: /* Epilog */
> + leave
> + .cfi_def_cfa %rsp, 8
> ret
> -.popsection
> + .cfi_endproc
>
> -_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
> +_ASM_VDSO_EXTABLE_HANDLE(2b, 5b)
--Andy
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface
2019-04-23 0:37 ` [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface Cedric Xing
@ 2019-04-23 1:29 ` Andy Lutomirski
2019-04-23 1:48 ` Sean Christopherson
2019-04-23 18:59 ` Sean Christopherson
0 siblings, 2 replies; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-23 1:29 UTC (permalink / raw)
To: Cedric Xing
Cc: LKML, X86 ML, linux-sgx, Andrew Morton, Dave, Sean J, nhorman,
npmccallum, Serge, Shay, Haitao, Andy Shevchenko,
Thomas Gleixner, Kai, Borislav Petkov, Josh Triplett,
Andrew Lutomirski, Kai, David Rientjes, Jarkko Sakkinen
On Mon, Apr 22, 2019 at 5:37 PM Cedric Xing <cedric.xing@intel.com> wrote:
>
> Given the changes to __vdso_sgx_enter_enclave(), the selftest is augmented to
> test the newly added callback interface. This addtional test marks the whole
> enclave range as PROT_READ, and calls mprotect() upon #PFs to add necessary PTE
> permissions per PFEC (#PF Error Code) until the enclave finishes.
Nifty.
What's not tested here is running this code with EFLAGS.TF set and
making sure that it unwinds correctly. Also, Jarkko, unless I missed
something, the vDSO extable code likely has a bug. If you run the
instruction right before ENCLU with EFLAGS.TF set, then do_debug()
will eat the SIGTRAP and skip to the exception handler. Similarly, if
you put an instruction breakpoint on ENCLU, it'll get skipped. Or is
the code actually correct and am I just remembering wrong?
--Andy
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface
2019-04-23 1:29 ` Andy Lutomirski
@ 2019-04-23 1:48 ` Sean Christopherson
2019-04-23 18:59 ` Sean Christopherson
1 sibling, 0 replies; 285+ messages in thread
From: Sean Christopherson @ 2019-04-23 1:48 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Cedric Xing, LKML, X86 ML, linux-sgx, Andrew Morton, Dave,
nhorman, npmccallum, Serge, Shay, Haitao, Andy Shevchenko,
Thomas Gleixner, Kai, Borislav Petkov, Josh Triplett, Kai,
David Rientjes, Jarkko Sakkinen
On Mon, Apr 22, 2019 at 06:29:06PM -0700, Andy Lutomirski wrote:
> On Mon, Apr 22, 2019 at 5:37 PM Cedric Xing <cedric.xing@intel.com> wrote:
> >
> > Given the changes to __vdso_sgx_enter_enclave(), the selftest is augmented to
> > test the newly added callback interface. This addtional test marks the whole
> > enclave range as PROT_READ, and calls mprotect() upon #PFs to add necessary PTE
> > permissions per PFEC (#PF Error Code) until the enclave finishes.
>
> Nifty.
>
> What's not tested here is running this code with EFLAGS.TF set and
> making sure that it unwinds correctly. Also, Jarkko, unless I missed
> something, the vDSO extable code likely has a bug. If you run the
> instruction right before ENCLU with EFLAGS.TF set, then do_debug()
> will eat the SIGTRAP and skip to the exception handler. Similarly, if
> you put an instruction breakpoint on ENCLU, it'll get skipped. Or is
> the code actually correct and am I just remembering wrong?
My money would be on the code being broken as opposed to you remembering
wrong. I'll take a look at it tomorrow.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-22 17:17 ` Sean Christopherson
@ 2019-04-23 9:11 ` Dr. Greg
0 siblings, 0 replies; 285+ messages in thread
From: Dr. Greg @ 2019-04-23 9:11 UTC (permalink / raw)
To: Sean Christopherson
Cc: Linus Torvalds, Thomas Gleixner, Jethro Beekman, Andy Lutomirski,
Andy Lutomirski, Dave Hansen, Jarkko Sakkinen, LKML, X86 ML,
linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Mon, Apr 22, 2019 at 10:17:15AM -0700, Sean Christopherson wrote:
Good morning to everyone.
> On Mon, Apr 22, 2019 at 09:55:47AM -0700, Linus Torvalds wrote:
> > On Mon, Apr 22, 2019 at 9:48 AM Sean Christopherson
> > <sean.j.christopherson@intel.com> wrote:
> > >
> > > Right, and loading a malicious enclave doesn't change those guarantees
> > > (for other enclaves). Ergo, restricting which enclaves can execute is
> > > orthogonal to the security provided by SGX.
> >
> > But it is absolutely worth noting that TSX made a lot of attacks both
> > easier to _do_, and also easier to _hide_.
> >
> > All while being basically completely worthless technology to everybody
> > except for some silly SAP benchmark.
> >
> > So it is definitely worth at least discussing the downsides of SGX. If
> > it ends up being another technology that makes it easier to create
> > malware, without actually having a lot of _good_ software use it, the
> > patches to enable it should make damn sure that the upsides actually
> > outweigh the downsides.
> >
> > And if the current setup basically is "you have to disable reasonable
> > SElinux protections that lots of distros use today", I think it's
> > entirely reasonable saying "the downsides are bigger than the
> > upsides".
> I'm not arguing against SGX playing nice with SELinux/LSMs, actually
> the opposite. I completely agree that enclaves should be subject to
> LSM restrictions.
As do we.
The point we have been making is that depending on the LSM's are
depending on the fact that the platform has not been compromised. SGX
is designed to provide a trusted execution environment in the face of
a compromised platform.
> AIUI, Dr. Greg is proposing a framework that uses SGX's launch
> control mechanism to restrict what enclaves can run. My point is
> that restricting what enclaves can run is about protecting the
> kernel and/or platform, not the enclaves themselves, i.e. using
> launch control instead of, or in addition to, LSMs doesn't change
> the security guarantees of SGX.
I believe current research suggests that this is not the case.
From the paper we have previously cited:
https://arxiv.org/pdf/1702.08719.pdf
In the second paragraph of the abstract:
"In this paper, we demonstrate fine-grained software-based
side-channel attacks from a malicious SGX enclave targeting co-located
enclaves. Our attack is the first malware running on real SGX
hardware, abusing SGX protection features to conceal itself.
Furthermore, we demonstrate our attack both in a native environment
and across multiple Docker containers".
To be perfectly clear, Dr. Greg, technically IDfusion, is not
proposing the use of SGX's launch control to restrict which enclaves
can run, although there are perfectly legitimate and required use
cases for that technology.
Dr. Greg is proposing that the kernel driver expend 1.2 pages of
kernel memory to implement, at the discretion of the platform owner,
cryptographically verified enclave initialization. The design we
proposed is the strongest guarantee that a platform owner can
implement, on FLC hardware, that only code and data of known
provenance can be loaded and executed.
There are only two companies that have written the entire stack of
software needed to make practical SGX applications work, us and Intel.
We can go into intimate detail on the issues involved but will embrace
bevity at this point.
Have a good day.
Dr. Greg
As always,
Dr. G.W. Wettstein, Ph.D. Enjellic Systems Development, LLC.
4206 N. 19th Ave. Specializing in information infra-structure
Fargo, ND 58102 development.
PH: 701-281-1686
FAX: 701-281-3949 EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"Because the innovator has for enemies all those who have done well
under the old conditions, and lukewarm defenders in those who may do
well under the new."
-- Niccolo Machiavelli
_The Prince_, Chapter VI
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-17 10:39 [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
` (32 preceding siblings ...)
2019-04-23 0:37 ` [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface Cedric Xing
@ 2019-04-23 11:56 ` Jarkko Sakkinen
2019-04-23 16:52 ` Andy Lutomirski
33 siblings, 1 reply; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-23 11:56 UTC (permalink / raw)
To: linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes
On Wed, Apr 17, 2019 at 01:39:10PM +0300, Jarkko Sakkinen wrote:
> Intel(R) SGX is a set of CPU instructions that can be used by applications
> to set aside private regions of code and data. The code outside the enclave
> is disallowed to access the memory inside the enclave by the CPU access
> control. In a way you can think that SGX provides inverted sandbox. It
> protects the application from a malicious host.
>
> There is a new hardware unit in the processor called Memory Encryption
> Engine (MEE) starting from the Skylake microacrhitecture. BIOS can define
> one or many MEE regions that can hold enclave data by configuring them with
> PRMRR registers.
>
> The MEE automatically encrypts the data leaving the processor package to
> the MEE regions. The data is encrypted using a random key whose life-time
> is exactly one power cycle.
>
> The current implementation requires that the firmware sets
> IA32_SGXLEPUBKEYHASH* MSRs as writable so that ultimately the kernel can
> decide what enclaves it wants run. The implementation does not create
> any bottlenecks to support read-only MSRs later on.
>
> You can tell if your CPU supports SGX by looking into /proc/cpuinfo:
>
> cat /proc/cpuinfo | grep sgx
>
> v20:
> * Fine-tune Kconfig messages and spacing and remove MMU_NOTIFIER
> dependency as MMU notifiers are no longer used in the driver.
> * Use mm_users instead of mm_count as refcount for mm_struct as mm_count
> only protects from deleting mm_struct, not removing its contents.
> * Sanitize EPC when the reclaimer thread starts by doing EREMOVE for all
> of them. They could be in initialized state when the kernel starts
> because it might be spawned by kexec().
> * Documentation overhaul.
> * Use a device /dev/sgx/provision for delivering the provision token
> instead of securityfs.
> * Create a reference to the enclave when already when opening
> /dev/sgx/enclave. The file is then associated with this enclave only.
> mmap() can be done at free at any point and always get a reference to
> the enclave. To summarize the file now represents the enclave.
>
> v19:
> * Took 3-4 months but in some sense this was more like a rewrite of most
> of the corners of the source code. If I've forgotten to deal with some
> feedback, please don't shout me. Make a remark and I will fix it for
> the next version. Hopefully there won't be this big turnovers anymore.
> * Validate SECS attributes properly against CPUID given attributes and
> against allowed attributes. SECS attributes are the ones that are
> enforced whereas SIGSTRUCT attributes tell what is required to run
> the enclave.
> * Add KSS (Key Sharing Support) to the enclave attributes.
> * Deny MAP_PRIVATE as an enclave is always a shared memory entity.
> * Revert back to shmem backing storage so that it can be easily shared
> by multiple processes.
> * Split the recognization of an ENCLS leaf failure by using three
> functions to detect it: encsl_faulted(), encls_returned_code() and
> sgx_failed(). encls_failed() is only caused by a spurious expections that
> should never happen. Thus, it is not defined as an inline function in
> order to easily insert a kprobe to it.
> * Move low-level enclave management routines, page fault handler and page
> reclaiming routines from driver to the core. These cannot be separated
> from each other as they are heavily interdependent. The rationale is that
> the core does not call any code from the driver.
> * Allow the driver to be compiled as a module now that it no code is using
> its routines and it only uses exported symbols. Now the driver is
> essentially just a thin ioctl layer.
> * Reworked the driver to maintain a list of mm_struct's. The VMA callbacks
> add new entries to this list as the process is forked. Each entry has
> its own refcount because they have a different life-cycle as the enclave
> does. In effect @tgid and @mm have been removed from struct sgx_encl
> and we allow forking by removing VM_DONTCOPY from vm flags.
> * Generate a cpu mask in the reclaimer from the cpu mask's of all
> mm_struct's. This will kick out the hardware threads out of the enclave
> from multiple processes. It is not a local variable because it would
> eat too much of the stack space but instead a field in struct
> sgx_encl.
> * Allow forking i.e. remove VM_DONTCOPY. I did not change the API
> because the old API scaled to the workload that Andy described. The
> codebase is now mostly API independent i.e. changing the API is a
> small task. For me the proper trigger to chanage it is a as concrete
> as possible workload that cannot be fulfilled. I hope you understand
> my thinking here. I don't want to change anything w/o proper basis
> but I'm ready to change anything if there is a proper basis. I do
> not have any kind of attachment to any particular type of API.
> * Add Sean's vDSO ENCLS(EENTER) patches and update selftest to use the
> new vDSO.
>
> v18:
> * Update the ioctl-number.txt.
> * Move the driver under arch/x86.
> * Add SGX features (SGX, SGX1, SGX2) to the disabled-features.h.
> * Rename the selftest as test_sgx (previously sgx-selftest).
> * In order to enable process accounting, swap EPC pages and PCMD's to a VMA
> instead of shmem.
> * Allow only to initialize and run enclaves with a subset of
> {DEBUG, MODE64BIT} set.
> * Add SGX_IOC_ENCLAVE_SET_ATTRIBUTE to allow an enclave to have privileged
> attributes e.g. PROVISIONKEY.
>
> v17:
> * Add a simple selftest.
> * Fix a null pointer dereference to section->pages when its
> allocation fails.
> * Add Sean's description of the exception handling to the documentation.
>
> v16:
> * Fixed SOB's in the commits that were a bit corrupted in v15.
> * Implemented exceptio handling properly to detect_sgx().
> * Use GENMASK() to define SGX_CPUID_SUB_LEAF_TYPE_MASK.
> * Updated the documentation to use rst definition lists.
> * Added the missing Documentation/x86/index.rst, which has a link to
> intel_sgx.rst. Now the SGX and uapi documentation is properly generated
> with 'make htmldocs'.
> * While enumerating EPC sections, if an undefined section is found, fail
> the driver initialization instead of continuing the initialization.
> * Issue a warning if there are more than %SGX_MAX_EPC_SECTIONS.
> * Remove copyright notice from arch/x86/include/asm/sgx.h.
> * Migrated from ioremap_cache() to memremap().
>
> v15:
> * Split into more digestable size patches.
> * Lots of small fixes and clean ups.
> * Signal a "plain" SIGSEGV on an EPCM violation.
>
> v14:
> * Change the comment about X86_FEATURE_SGX_LC from “SGX launch
> configuration” to “SGX launch control”.
> * Move the SGX-related CPU feature flags as part of the Linux defined
> virtual leaf 8.
> * Add SGX_ prefix to the constants defining the ENCLS leaf functions.
> * Use GENMASK*() and BIT*() in sgx_arch.h instead of raw hex numbers.
> * Refine the long description for CONFIG_INTEL_SGX_CORE.
> * Do not use pr_*_ratelimited() in the driver. The use of the rate limited
> versions is legacy cruft from the prototyping phase.
> * Detect sleep with SGX_INVALID_EINIT_TOKEN instead of counting power
> cycles.
> * Manually prefix with “sgx:” in the core SGX code instead of redefining
> pr_fmt.
> * Report if IA32_SGXLEPUBKEYHASHx MSRs are not writable in the driver
> instead of core because it is a driver requirement.
> * Change prompt to bool in the entry for CONFIG_INTEL_SGX_CORE because the
> default is ‘n’.
> * Rename struct sgx_epc_bank as struct sgx_epc_section in order to match
> the SDM.
> * Allocate struct sgx_epc_page instances one at a time.
> * Use “__iomem void *” pointers for the mapped EPC memory consistently.
> * Retry once on SGX_INVALID_TOKEN in sgx_einit() instead of counting power
> cycles.
> * Call enclave swapping operations directly from the driver instead of
> calling them .indirectly through struct sgx_epc_page_ops because indirect
> calls are not required yet as the patch set does not contain the KVM
> support.
> * Added special signal SEGV_SGXERR to notify about SGX EPCM violation
> errors.
>
> v13:
> * Always use SGX_CPUID constant instead of a hardcoded value.
> * Simplified and documented the macros and functions for ENCLS leaves.
> * Enable sgx_free_page() to free active enclave pages on demand
> in order to allow sgx_invalidate() to delete enclave pages.
> It no longer performs EREMOVE if a page is in the process of
> being reclaimed.
> * Use PM notifier per enclave so that we don't have to traverse
> the global list of active EPC pages to find enclaves.
> * Removed unused SGX_LE_ROLLBACK constant from uapi/asm/sgx.h
> * Always use ioremap() to map EPC banks as we only support 64-bit kernel.
> * Invalidate IA32_SGXLEPUBKEYHASH cache used by sgx_einit() when going
> to sleep.
>
> v12:
> * Split to more narrow scoped commits in order to ease the review process and
> use co-developed-by tag for co-authors of commits instead of listing them in
> the source files.
> * Removed cruft EXPORT_SYMBOL() declarations and converted to static variables.
> * Removed in-kernel LE i.e. this version of the SGX software stack only
> supports unlocked IA32_SGXLEPUBKEYHASHx MSRs.
> * Refined documentation on launching enclaves, swapping and enclave
> construction.
> * Refined sgx_arch.h to include alignment information for every struct that
> requires it and removed structs that are not needed without an LE.
> * Got rid of SGX_CPUID.
> * SGX detection now prints log messages about firmware configuration issues.
>
> v11:
> * Polished ENCLS wrappers with refined exception handling.
> * ksgxswapd was not stopped (regression in v5) in
> sgx_page_cache_teardown(), which causes a leaked kthread after driver
> deinitialization.
> * Shutdown sgx_le_proxy when going to suspend because its EPC pages will be
> invalidated when resuming, which will cause it not function properly
> anymore.
> * Set EINITTOKEN.VALID to zero for a token that is passed when
> SGXLEPUBKEYHASH matches MRSIGNER as alloc_page() does not give a zero
> page.
> * Fixed the check in sgx_edbgrd() for a TCS page. Allowed to read offsets
> around the flags field, which causes a #GP. Only flags read is readable.
> * On read access memcpy() call inside sgx_vma_access() had src and dest
> parameters in wrong order.
> * The build issue with CONFIG_KASAN is now fixed. Added undefined symbols
> to LE even if “KASAN_SANITIZE := false” was set in the makefile.
> * Fixed a regression in the #PF handler. If a page has
> SGX_ENCL_PAGE_RESERVED flag the #PF handler should unconditionally fail.
> It did not, which caused weird races when trying to change other parts of
> swapping code.
> * EPC management has been refactored to a flat LRU cache and moved to
> arch/x86. The swapper thread reads a cluster of EPC pages and swaps all
> of them. It can now swap from multiple enclaves in the same round.
> * For the sake of consistency with SGX_IOC_ENCLAVE_ADD_PAGE, return -EINVAL
> when an enclave is already initialized or dead instead of zero.
>
> v10:
> * Cleaned up anon inode based IPC between the ring-0 and ring-3 parts
> of the driver.
> * Unset the reserved flag from an enclave page if EDBGRD/WR fails
> (regression in v6).
> * Close the anon inode when LE is stopped (regression in v9).
> * Update the documentation with a more detailed description of SGX.
>
> v9:
> * Replaced kernel-LE IPC based on pipes with an anonymous inode.
> The driver does not require anymore new exports.
>
> v8:
> * Check that public key MSRs match the LE public key hash in the
> driver initialization when the MSRs are read-only.
> * Fix the race in VA slot allocation by checking the fullness
> immediately after succeesful allocation.
> * Fix the race in hash mrsigner calculation between the launch
> enclave and user enclaves by having a separate lock for hash
> calculation.
>
> v7:
> * Fixed offset calculation in sgx_edbgr/wr(). Address was masked with PAGE_MASK
> when it should have been masked with ~PAGE_MASK.
> * Fixed a memory leak in sgx_ioc_enclave_create().
> * Simplified swapping code by using a pointer array for a cluster
> instead of a linked list.
> * Squeezed struct sgx_encl_page to 32 bytes.
> * Fixed deferencing of an RSA key on OpenSSL 1.1.0.
> * Modified TC's CMAC to use kernel AES-NI. Restructured the code
> a bit in order to better align with kernel conventions.
>
> v6:
> * Fixed semaphore underrun when accessing /dev/sgx from the launch enclave.
> * In sgx_encl_create() s/IS_ERR(secs)/IS_ERR(encl)/.
> * Removed virtualization chapter from the documentation.
> * Changed the default filename for the signing key as signing_key.pem.
> * Reworked EPC management in a way that instead of a linked list of
> struct sgx_epc_page instances there is an array of integers that
> encodes address and bank of an EPC page (the same data as 'pa' field
> earlier). The locking has been moved to the EPC bank level instead
> of a global lock.
> * Relaxed locking requirements for EPC management. EPC pages can be
> released back to the EPC bank concurrently.
> * Cleaned up ptrace() code.
> * Refined commit messages for new architectural constants.
> * Sorted includes in every source file.
> * Sorted local variable declarations according to the line length in
> every function.
> * Style fixes based on Darren's comments to sgx_le.c.
>
> v5:
> * Described IPC between the Launch Enclave and kernel in the commit messages.
> * Fixed all relevant checkpatch.pl issues that I have forgot fix in earlier
> versions except those that exist in the imported TinyCrypt code.
> * Fixed spelling mistakes in the documentation.
> * Forgot to check the return value of sgx_drv_subsys_init().
> * Encapsulated properly page cache init and teardown.
> * Collect epc pages to a temp list in sgx_add_epc_bank
> * Removed SGX_ENCLAVE_INIT_ARCH constant.
>
> v4:
> * Tied life-cycle of the sgx_le_proxy process to /dev/sgx.
> * Removed __exit annotation from sgx_drv_subsys_exit().
> * Fixed a leak of a backing page in sgx_process_add_page_req() in the
> case when vm_insert_pfn() fails.
> * Removed unused symbol exports for sgx_page_cache.c.
> * Updated sgx_alloc_page() to require encl parameter and documented the
> behavior (Sean Christopherson).
> * Refactored a more lean API for sgx_encl_find() and documented the behavior.
> * Moved #PF handler to sgx_fault.c.
> * Replaced subsys_system_register() with plain bus_register().
> * Retry EINIT 2nd time only if MSRs are not locked.
>
> v3:
> * Check that FEATURE_CONTROL_LOCKED and FEATURE_CONTROL_SGX_ENABLE are set.
> * Return -ERESTARTSYS in __sgx_encl_add_page() when sgx_alloc_page() fails.
> * Use unused bits in epc_page->pa to store the bank number.
> * Removed #ifdef for WQ_NONREENTRANT.
> * If mmu_notifier_register() fails with -EINTR, return -ERESTARTSYS.
> * Added --remove-section=.got.plt to objcopy flags in order to prevent a
> dummy .got.plt, which will cause an inconsistent size for the LE.
> * Documented sgx_encl_* functions.
> * Added remark about AES implementation used inside the LE.
> * Removed redundant sgx_sys_exit() from le/main.c.
> * Fixed struct sgx_secinfo alignment from 128 to 64 bytes.
> * Validate miscselect in sgx_encl_create().
> * Fixed SSA frame size calculation to take the misc region into account.
> * Implemented consistent exception handling to __encls() and __encls_ret().
> * Implemented a proper device model in order to allow sysfs attributes
> and in-kernel API.
> * Cleaned up various "find enclave" implementations to the unified
> sgx_encl_find().
> * Validate that vm_pgoff is zero.
> * Discard backing pages with shmem_truncate_range() after EADD.
> * Added missing EEXTEND operations to LE signing and launch.
> * Fixed SSA size for GPRS region from 168 to 184 bytes.
> * Fixed the checks for TCS flags. Now DBGOPTIN is allowed.
> * Check that TCS addresses are in ELRANGE and not just page aligned.
> * Require kernel to be compiled with X64_64 and CPU_SUP_INTEL.
> * Fixed an incorrect value for SGX_ATTR_DEBUG from 0x01 to 0x02.
>
> v2:
> * get_rand_uint32() changed the value of the pointer instead of value
> where it is pointing at.
> * Launch enclave incorrectly used sigstruct attributes-field instead of
> enclave attributes-field.
> * Removed unused struct sgx_add_page_req from sgx_ioctl.c
> * Removed unused sgx_has_sgx2.
> * Updated arch/x86/include/asm/sgx.h so that it provides stub
> implementations when sgx in not enabled.
> * Removed cruft rdmsr-calls from sgx_set_pubkeyhash_msrs().
> * return -ENOMEM in sgx_alloc_page() when VA pages consume too much space
> * removed unused global sgx_nr_pids
> * moved sgx_encl_release to sgx_encl.c
> * return -ERESTARTSYS instead of -EINTR in sgx_encl_init()
>
> Jarkko Sakkinen (11):
> x86/sgx: Add ENCLS architectural error codes
> x86/sgx: Add SGX1 and SGX2 architectural data structures
> x86/sgx: Add wrappers for ENCLS leaf functions
> x86/sgx: Add functions to allocate and free EPC pages
> x86/sgx: Add the Linux SGX Enclave Driver
> x86/sgx: Add provisioning
> x86/sgx: Add swapping code to the core and SGX driver
> x86/sgx: ptrace() support for the SGX driver
> selftests/x86: Add a selftest for SGX
> x86/sgx: Update MAINTAINERS
> docs: x86/sgx: Document the enclave API
>
> Kai Huang (2):
> x86/cpufeatures: Add Intel-defined SGX feature bit
> x86/cpufeatures: Add Intel-defined SGX_LC feature bit
>
> Sean Christopherson (15):
> x86/cpufeatures: Add SGX sub-features (as Linux-defined bits)
> x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition
> x86/msr: Add SGX Launch Control MSR definitions
> x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit
> x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
> x86/cpu/intel: Detect SGX support and update caps appropriately
> x86/sgx: Enumerate and track EPC sections
> x86/sgx: Add sgx_einit() for initializing enclaves
> x86/vdso: Add support for exception fixup in vDSO functions
> x86/fault: Add helper function to sanitize error code
> x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling
> x86/traps: Attempt to fixup exceptions in vDSO before signaling
> x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave
> transitions
> docs: x86/sgx: Add Architecture documentation
> docs: x86/sgx: Document kernel internals
>
> Documentation/index.rst | 1 +
> Documentation/ioctl/ioctl-number.txt | 1 +
> Documentation/x86/index.rst | 10 +
> Documentation/x86/sgx/1.Architecture.rst | 431 +++++++++
> Documentation/x86/sgx/2.Kernel-internals.rst | 56 ++
> Documentation/x86/sgx/3.API.rst | 27 +
> Documentation/x86/sgx/index.rst | 18 +
> MAINTAINERS | 12 +
> arch/x86/Kconfig | 27 +
> arch/x86/entry/vdso/Makefile | 6 +-
> arch/x86/entry/vdso/extable.c | 37 +
> arch/x86/entry/vdso/extable.h | 29 +
> arch/x86/entry/vdso/vdso-layout.lds.S | 9 +-
> arch/x86/entry/vdso/vdso.lds.S | 1 +
> arch/x86/entry/vdso/vdso2c.h | 58 +-
> arch/x86/entry/vdso/vsgx_enter_enclave.S | 101 +++
> arch/x86/include/asm/cpufeatures.h | 24 +-
> arch/x86/include/asm/disabled-features.h | 14 +-
> arch/x86/include/asm/msr-index.h | 8 +
> arch/x86/include/asm/traps.h | 1 +
> arch/x86/include/asm/vdso.h | 5 +
> arch/x86/include/uapi/asm/sgx.h | 86 ++
> arch/x86/include/uapi/asm/sgx_errno.h | 91 ++
> arch/x86/kernel/cpu/Makefile | 1 +
> arch/x86/kernel/cpu/intel.c | 39 +
> arch/x86/kernel/cpu/scattered.c | 2 +
> arch/x86/kernel/cpu/sgx/Makefile | 2 +
> arch/x86/kernel/cpu/sgx/arch.h | 424 +++++++++
> arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
> arch/x86/kernel/cpu/sgx/driver/driver.h | 38 +
> arch/x86/kernel/cpu/sgx/driver/ioctl.c | 850 ++++++++++++++++++
> arch/x86/kernel/cpu/sgx/driver/main.c | 368 ++++++++
> arch/x86/kernel/cpu/sgx/encl.c | 709 +++++++++++++++
> arch/x86/kernel/cpu/sgx/encl.h | 136 +++
> arch/x86/kernel/cpu/sgx/encls.c | 22 +
> arch/x86/kernel/cpu/sgx/encls.h | 244 +++++
> arch/x86/kernel/cpu/sgx/main.c | 360 ++++++++
> arch/x86/kernel/cpu/sgx/reclaim.c | 482 ++++++++++
> arch/x86/kernel/cpu/sgx/sgx.h | 90 ++
> arch/x86/kernel/traps.c | 14 +
> arch/x86/mm/fault.c | 44 +-
> tools/arch/x86/include/asm/cpufeatures.h | 21 +-
> tools/testing/selftests/x86/Makefile | 10 +
> tools/testing/selftests/x86/sgx/Makefile | 48 +
> tools/testing/selftests/x86/sgx/defines.h | 39 +
> tools/testing/selftests/x86/sgx/encl.c | 20 +
> tools/testing/selftests/x86/sgx/encl.lds | 33 +
> .../selftests/x86/sgx/encl_bootstrap.S | 94 ++
> tools/testing/selftests/x86/sgx/encl_piggy.S | 18 +
> tools/testing/selftests/x86/sgx/encl_piggy.h | 14 +
> tools/testing/selftests/x86/sgx/main.c | 279 ++++++
> tools/testing/selftests/x86/sgx/sgx_call.S | 15 +
> tools/testing/selftests/x86/sgx/sgxsign.c | 508 +++++++++++
> .../testing/selftests/x86/sgx/signing_key.pem | 39 +
> 54 files changed, 5987 insertions(+), 32 deletions(-)
> create mode 100644 Documentation/x86/index.rst
> create mode 100644 Documentation/x86/sgx/1.Architecture.rst
> create mode 100644 Documentation/x86/sgx/2.Kernel-internals.rst
> create mode 100644 Documentation/x86/sgx/3.API.rst
> create mode 100644 Documentation/x86/sgx/index.rst
> create mode 100644 arch/x86/entry/vdso/extable.c
> create mode 100644 arch/x86/entry/vdso/extable.h
> create mode 100644 arch/x86/entry/vdso/vsgx_enter_enclave.S
> create mode 100644 arch/x86/include/uapi/asm/sgx.h
> create mode 100644 arch/x86/include/uapi/asm/sgx_errno.h
> create mode 100644 arch/x86/kernel/cpu/sgx/Makefile
> create mode 100644 arch/x86/kernel/cpu/sgx/arch.h
> create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
> create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
> create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
> create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
> create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
> create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
> create mode 100644 arch/x86/kernel/cpu/sgx/encls.c
> create mode 100644 arch/x86/kernel/cpu/sgx/encls.h
> create mode 100644 arch/x86/kernel/cpu/sgx/main.c
> create mode 100644 arch/x86/kernel/cpu/sgx/reclaim.c
> create mode 100644 arch/x86/kernel/cpu/sgx/sgx.h
> create mode 100644 tools/testing/selftests/x86/sgx/Makefile
> create mode 100644 tools/testing/selftests/x86/sgx/defines.h
> create mode 100644 tools/testing/selftests/x86/sgx/encl.c
> create mode 100644 tools/testing/selftests/x86/sgx/encl.lds
> create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S
> create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.S
> create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h
> create mode 100644 tools/testing/selftests/x86/sgx/main.c
> create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
> create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c
> create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem
>
> --
> 2.19.1
>
I'm on leave for this week and next week's Monday if you wonder why I'm
so passive in the discussion. Looking at the things next week's Tue.
Just a quick comment about Andy's proposal. Probably pretty DSO like
ELF blob could work with an addition of a section called ".tcs" for
entry points. They need to be recognized so that the loader can add
them as TCS pages.
My self-test already is a PoC for enclave binary with a custom linker
script to define the binary format. Too simplistic for a "generic"
case but still a starting point.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 16/28] x86/sgx: Add provisioning
2019-04-19 3:06 ` Huang, Kai
@ 2019-04-23 14:33 ` Jarkko Sakkinen
0 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-23 14:33 UTC (permalink / raw)
To: Huang, Kai
Cc: linux-kernel, linux-sgx, x86, Svahn, Kai, nhorman, jmorris,
Christopherson, Sean J, josh, tglx, Ayoun, Serge, Huang, Haitao,
linux-security-module, akpm, npmccallum, rientjes, luto,
Katz-zamir, Shay, Hansen, Dave, bp, serge, andriy.shevchenko
On Fri, Apr 19, 2019 at 03:06:48AM +0000, Huang, Kai wrote:
> On Wed, 2019-04-17 at 13:39 +0300, Jarkko Sakkinen wrote:
> > In order to provide a mechanism for devilering provisoning rights:
> >
> > 1. Add a new device file /dev/sgx/provision that works as a token for
> > allowing an enclave to have the provisioning privileges.
> > 2. Add a new ioctl called SGX_IOC_ENCLAVE_SET_ATTRIBUTE that accepts the
> > following data structure:
> >
> > struct sgx_enclave_set_attribute {
> > __u64 addr;
> > __u64 attribute_fd;
> > };
> >
> > A daemon could sit on top of /dev/sgx/provision and send a file
> > descriptor of this file to a process that needs to be able to provision
> > enclaves.
> >
> > The way this API is used is straight-forward. Lets assume that dev_fd is
> > a handle to /dev/sgx/enclave and prov_fd is a handle to
> > /dev/sgx/provision. You would allow SGX_IOC_ENCLAVE_CREATE to
> > initialize an enclave with the PROVISIONKEY attribute by
> >
> > params.addr = <enclave address>;
> > params.token_fd = prov_fd;
> >
> Should be params.attribute_fd = prov_fd;
>
> Thanks,
> -Kai
Thanks.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-23 11:56 ` [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
@ 2019-04-23 16:52 ` Andy Lutomirski
2019-04-24 12:17 ` Jarkko Sakkinen
0 siblings, 1 reply; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-23 16:52 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: LKML, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
Andrew Lutomirski, Huang, Kai, David Rientjes
On Tue, Apr 23, 2019 at 4:56 AM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
>
> On Wed, Apr 17, 2019 at 01:39:10PM +0300, Jarkko Sakkinen wrote:
> > Intel(R) SGX is a set of CPU instructions that can be used by applications
> > to set aside private regions of code and data. The code outside the enclave
> > is disallowed to access the memory inside the enclave by the CPU access
> > control. In a way you can think that SGX provides inverted sandbox. It
> > protects the application from a malicious host.
> >
> > There is a new hardware unit in the processor called Memory Encryption
> > Engine (MEE) starting from the Skylake microacrhitecture. BIOS can define
> > one or many MEE regions that can hold enclave data by configuring them with
> > PRMRR registers.
> >
> > The MEE automatically encrypts the data leaving the processor package to
> > the MEE regions. The data is encrypted using a random key whose life-time
> > is exactly one power cycle.
> >
> > The current implementation requires that the firmware sets
> > IA32_SGXLEPUBKEYHASH* MSRs as writable so that ultimately the kernel can
> > decide what enclaves it wants run. The implementation does not create
> > any bottlenecks to support read-only MSRs later on.
> >
> > You can tell if your CPU supports SGX by looking into /proc/cpuinfo:
> >
> > cat /proc/cpuinfo | grep sgx
> >
> > v20:
> > * Fine-tune Kconfig messages and spacing and remove MMU_NOTIFIER
> > dependency as MMU notifiers are no longer used in the driver.
> > * Use mm_users instead of mm_count as refcount for mm_struct as mm_count
> > only protects from deleting mm_struct, not removing its contents.
> > * Sanitize EPC when the reclaimer thread starts by doing EREMOVE for all
> > of them. They could be in initialized state when the kernel starts
> > because it might be spawned by kexec().
> > * Documentation overhaul.
> > * Use a device /dev/sgx/provision for delivering the provision token
> > instead of securityfs.
> > * Create a reference to the enclave when already when opening
> > /dev/sgx/enclave. The file is then associated with this enclave only.
> > mmap() can be done at free at any point and always get a reference to
> > the enclave. To summarize the file now represents the enclave.
> >
> > v19:
> > * Took 3-4 months but in some sense this was more like a rewrite of most
> > of the corners of the source code. If I've forgotten to deal with some
> > feedback, please don't shout me. Make a remark and I will fix it for
> > the next version. Hopefully there won't be this big turnovers anymore.
> > * Validate SECS attributes properly against CPUID given attributes and
> > against allowed attributes. SECS attributes are the ones that are
> > enforced whereas SIGSTRUCT attributes tell what is required to run
> > the enclave.
> > * Add KSS (Key Sharing Support) to the enclave attributes.
> > * Deny MAP_PRIVATE as an enclave is always a shared memory entity.
> > * Revert back to shmem backing storage so that it can be easily shared
> > by multiple processes.
> > * Split the recognization of an ENCLS leaf failure by using three
> > functions to detect it: encsl_faulted(), encls_returned_code() and
> > sgx_failed(). encls_failed() is only caused by a spurious expections that
> > should never happen. Thus, it is not defined as an inline function in
> > order to easily insert a kprobe to it.
> > * Move low-level enclave management routines, page fault handler and page
> > reclaiming routines from driver to the core. These cannot be separated
> > from each other as they are heavily interdependent. The rationale is that
> > the core does not call any code from the driver.
> > * Allow the driver to be compiled as a module now that it no code is using
> > its routines and it only uses exported symbols. Now the driver is
> > essentially just a thin ioctl layer.
> > * Reworked the driver to maintain a list of mm_struct's. The VMA callbacks
> > add new entries to this list as the process is forked. Each entry has
> > its own refcount because they have a different life-cycle as the enclave
> > does. In effect @tgid and @mm have been removed from struct sgx_encl
> > and we allow forking by removing VM_DONTCOPY from vm flags.
> > * Generate a cpu mask in the reclaimer from the cpu mask's of all
> > mm_struct's. This will kick out the hardware threads out of the enclave
> > from multiple processes. It is not a local variable because it would
> > eat too much of the stack space but instead a field in struct
> > sgx_encl.
> > * Allow forking i.e. remove VM_DONTCOPY. I did not change the API
> > because the old API scaled to the workload that Andy described. The
> > codebase is now mostly API independent i.e. changing the API is a
> > small task. For me the proper trigger to chanage it is a as concrete
> > as possible workload that cannot be fulfilled. I hope you understand
> > my thinking here. I don't want to change anything w/o proper basis
> > but I'm ready to change anything if there is a proper basis. I do
> > not have any kind of attachment to any particular type of API.
> > * Add Sean's vDSO ENCLS(EENTER) patches and update selftest to use the
> > new vDSO.
> >
> > v18:
> > * Update the ioctl-number.txt.
> > * Move the driver under arch/x86.
> > * Add SGX features (SGX, SGX1, SGX2) to the disabled-features.h.
> > * Rename the selftest as test_sgx (previously sgx-selftest).
> > * In order to enable process accounting, swap EPC pages and PCMD's to a VMA
> > instead of shmem.
> > * Allow only to initialize and run enclaves with a subset of
> > {DEBUG, MODE64BIT} set.
> > * Add SGX_IOC_ENCLAVE_SET_ATTRIBUTE to allow an enclave to have privileged
> > attributes e.g. PROVISIONKEY.
> >
> > v17:
> > * Add a simple selftest.
> > * Fix a null pointer dereference to section->pages when its
> > allocation fails.
> > * Add Sean's description of the exception handling to the documentation.
> >
> > v16:
> > * Fixed SOB's in the commits that were a bit corrupted in v15.
> > * Implemented exceptio handling properly to detect_sgx().
> > * Use GENMASK() to define SGX_CPUID_SUB_LEAF_TYPE_MASK.
> > * Updated the documentation to use rst definition lists.
> > * Added the missing Documentation/x86/index.rst, which has a link to
> > intel_sgx.rst. Now the SGX and uapi documentation is properly generated
> > with 'make htmldocs'.
> > * While enumerating EPC sections, if an undefined section is found, fail
> > the driver initialization instead of continuing the initialization.
> > * Issue a warning if there are more than %SGX_MAX_EPC_SECTIONS.
> > * Remove copyright notice from arch/x86/include/asm/sgx.h.
> > * Migrated from ioremap_cache() to memremap().
> >
> > v15:
> > * Split into more digestable size patches.
> > * Lots of small fixes and clean ups.
> > * Signal a "plain" SIGSEGV on an EPCM violation.
> >
> > v14:
> > * Change the comment about X86_FEATURE_SGX_LC from “SGX launch
> > configuration” to “SGX launch control”.
> > * Move the SGX-related CPU feature flags as part of the Linux defined
> > virtual leaf 8.
> > * Add SGX_ prefix to the constants defining the ENCLS leaf functions.
> > * Use GENMASK*() and BIT*() in sgx_arch.h instead of raw hex numbers.
> > * Refine the long description for CONFIG_INTEL_SGX_CORE.
> > * Do not use pr_*_ratelimited() in the driver. The use of the rate limited
> > versions is legacy cruft from the prototyping phase.
> > * Detect sleep with SGX_INVALID_EINIT_TOKEN instead of counting power
> > cycles.
> > * Manually prefix with “sgx:” in the core SGX code instead of redefining
> > pr_fmt.
> > * Report if IA32_SGXLEPUBKEYHASHx MSRs are not writable in the driver
> > instead of core because it is a driver requirement.
> > * Change prompt to bool in the entry for CONFIG_INTEL_SGX_CORE because the
> > default is ‘n’.
> > * Rename struct sgx_epc_bank as struct sgx_epc_section in order to match
> > the SDM.
> > * Allocate struct sgx_epc_page instances one at a time.
> > * Use “__iomem void *” pointers for the mapped EPC memory consistently.
> > * Retry once on SGX_INVALID_TOKEN in sgx_einit() instead of counting power
> > cycles.
> > * Call enclave swapping operations directly from the driver instead of
> > calling them .indirectly through struct sgx_epc_page_ops because indirect
> > calls are not required yet as the patch set does not contain the KVM
> > support.
> > * Added special signal SEGV_SGXERR to notify about SGX EPCM violation
> > errors.
> >
> > v13:
> > * Always use SGX_CPUID constant instead of a hardcoded value.
> > * Simplified and documented the macros and functions for ENCLS leaves.
> > * Enable sgx_free_page() to free active enclave pages on demand
> > in order to allow sgx_invalidate() to delete enclave pages.
> > It no longer performs EREMOVE if a page is in the process of
> > being reclaimed.
> > * Use PM notifier per enclave so that we don't have to traverse
> > the global list of active EPC pages to find enclaves.
> > * Removed unused SGX_LE_ROLLBACK constant from uapi/asm/sgx.h
> > * Always use ioremap() to map EPC banks as we only support 64-bit kernel.
> > * Invalidate IA32_SGXLEPUBKEYHASH cache used by sgx_einit() when going
> > to sleep.
> >
> > v12:
> > * Split to more narrow scoped commits in order to ease the review process and
> > use co-developed-by tag for co-authors of commits instead of listing them in
> > the source files.
> > * Removed cruft EXPORT_SYMBOL() declarations and converted to static variables.
> > * Removed in-kernel LE i.e. this version of the SGX software stack only
> > supports unlocked IA32_SGXLEPUBKEYHASHx MSRs.
> > * Refined documentation on launching enclaves, swapping and enclave
> > construction.
> > * Refined sgx_arch.h to include alignment information for every struct that
> > requires it and removed structs that are not needed without an LE.
> > * Got rid of SGX_CPUID.
> > * SGX detection now prints log messages about firmware configuration issues.
> >
> > v11:
> > * Polished ENCLS wrappers with refined exception handling.
> > * ksgxswapd was not stopped (regression in v5) in
> > sgx_page_cache_teardown(), which causes a leaked kthread after driver
> > deinitialization.
> > * Shutdown sgx_le_proxy when going to suspend because its EPC pages will be
> > invalidated when resuming, which will cause it not function properly
> > anymore.
> > * Set EINITTOKEN.VALID to zero for a token that is passed when
> > SGXLEPUBKEYHASH matches MRSIGNER as alloc_page() does not give a zero
> > page.
> > * Fixed the check in sgx_edbgrd() for a TCS page. Allowed to read offsets
> > around the flags field, which causes a #GP. Only flags read is readable.
> > * On read access memcpy() call inside sgx_vma_access() had src and dest
> > parameters in wrong order.
> > * The build issue with CONFIG_KASAN is now fixed. Added undefined symbols
> > to LE even if “KASAN_SANITIZE := false” was set in the makefile.
> > * Fixed a regression in the #PF handler. If a page has
> > SGX_ENCL_PAGE_RESERVED flag the #PF handler should unconditionally fail.
> > It did not, which caused weird races when trying to change other parts of
> > swapping code.
> > * EPC management has been refactored to a flat LRU cache and moved to
> > arch/x86. The swapper thread reads a cluster of EPC pages and swaps all
> > of them. It can now swap from multiple enclaves in the same round.
> > * For the sake of consistency with SGX_IOC_ENCLAVE_ADD_PAGE, return -EINVAL
> > when an enclave is already initialized or dead instead of zero.
> >
> > v10:
> > * Cleaned up anon inode based IPC between the ring-0 and ring-3 parts
> > of the driver.
> > * Unset the reserved flag from an enclave page if EDBGRD/WR fails
> > (regression in v6).
> > * Close the anon inode when LE is stopped (regression in v9).
> > * Update the documentation with a more detailed description of SGX.
> >
> > v9:
> > * Replaced kernel-LE IPC based on pipes with an anonymous inode.
> > The driver does not require anymore new exports.
> >
> > v8:
> > * Check that public key MSRs match the LE public key hash in the
> > driver initialization when the MSRs are read-only.
> > * Fix the race in VA slot allocation by checking the fullness
> > immediately after succeesful allocation.
> > * Fix the race in hash mrsigner calculation between the launch
> > enclave and user enclaves by having a separate lock for hash
> > calculation.
> >
> > v7:
> > * Fixed offset calculation in sgx_edbgr/wr(). Address was masked with PAGE_MASK
> > when it should have been masked with ~PAGE_MASK.
> > * Fixed a memory leak in sgx_ioc_enclave_create().
> > * Simplified swapping code by using a pointer array for a cluster
> > instead of a linked list.
> > * Squeezed struct sgx_encl_page to 32 bytes.
> > * Fixed deferencing of an RSA key on OpenSSL 1.1.0.
> > * Modified TC's CMAC to use kernel AES-NI. Restructured the code
> > a bit in order to better align with kernel conventions.
> >
> > v6:
> > * Fixed semaphore underrun when accessing /dev/sgx from the launch enclave.
> > * In sgx_encl_create() s/IS_ERR(secs)/IS_ERR(encl)/.
> > * Removed virtualization chapter from the documentation.
> > * Changed the default filename for the signing key as signing_key.pem.
> > * Reworked EPC management in a way that instead of a linked list of
> > struct sgx_epc_page instances there is an array of integers that
> > encodes address and bank of an EPC page (the same data as 'pa' field
> > earlier). The locking has been moved to the EPC bank level instead
> > of a global lock.
> > * Relaxed locking requirements for EPC management. EPC pages can be
> > released back to the EPC bank concurrently.
> > * Cleaned up ptrace() code.
> > * Refined commit messages for new architectural constants.
> > * Sorted includes in every source file.
> > * Sorted local variable declarations according to the line length in
> > every function.
> > * Style fixes based on Darren's comments to sgx_le.c.
> >
> > v5:
> > * Described IPC between the Launch Enclave and kernel in the commit messages.
> > * Fixed all relevant checkpatch.pl issues that I have forgot fix in earlier
> > versions except those that exist in the imported TinyCrypt code.
> > * Fixed spelling mistakes in the documentation.
> > * Forgot to check the return value of sgx_drv_subsys_init().
> > * Encapsulated properly page cache init and teardown.
> > * Collect epc pages to a temp list in sgx_add_epc_bank
> > * Removed SGX_ENCLAVE_INIT_ARCH constant.
> >
> > v4:
> > * Tied life-cycle of the sgx_le_proxy process to /dev/sgx.
> > * Removed __exit annotation from sgx_drv_subsys_exit().
> > * Fixed a leak of a backing page in sgx_process_add_page_req() in the
> > case when vm_insert_pfn() fails.
> > * Removed unused symbol exports for sgx_page_cache.c.
> > * Updated sgx_alloc_page() to require encl parameter and documented the
> > behavior (Sean Christopherson).
> > * Refactored a more lean API for sgx_encl_find() and documented the behavior.
> > * Moved #PF handler to sgx_fault.c.
> > * Replaced subsys_system_register() with plain bus_register().
> > * Retry EINIT 2nd time only if MSRs are not locked.
> >
> > v3:
> > * Check that FEATURE_CONTROL_LOCKED and FEATURE_CONTROL_SGX_ENABLE are set.
> > * Return -ERESTARTSYS in __sgx_encl_add_page() when sgx_alloc_page() fails.
> > * Use unused bits in epc_page->pa to store the bank number.
> > * Removed #ifdef for WQ_NONREENTRANT.
> > * If mmu_notifier_register() fails with -EINTR, return -ERESTARTSYS.
> > * Added --remove-section=.got.plt to objcopy flags in order to prevent a
> > dummy .got.plt, which will cause an inconsistent size for the LE.
> > * Documented sgx_encl_* functions.
> > * Added remark about AES implementation used inside the LE.
> > * Removed redundant sgx_sys_exit() from le/main.c.
> > * Fixed struct sgx_secinfo alignment from 128 to 64 bytes.
> > * Validate miscselect in sgx_encl_create().
> > * Fixed SSA frame size calculation to take the misc region into account.
> > * Implemented consistent exception handling to __encls() and __encls_ret().
> > * Implemented a proper device model in order to allow sysfs attributes
> > and in-kernel API.
> > * Cleaned up various "find enclave" implementations to the unified
> > sgx_encl_find().
> > * Validate that vm_pgoff is zero.
> > * Discard backing pages with shmem_truncate_range() after EADD.
> > * Added missing EEXTEND operations to LE signing and launch.
> > * Fixed SSA size for GPRS region from 168 to 184 bytes.
> > * Fixed the checks for TCS flags. Now DBGOPTIN is allowed.
> > * Check that TCS addresses are in ELRANGE and not just page aligned.
> > * Require kernel to be compiled with X64_64 and CPU_SUP_INTEL.
> > * Fixed an incorrect value for SGX_ATTR_DEBUG from 0x01 to 0x02.
> >
> > v2:
> > * get_rand_uint32() changed the value of the pointer instead of value
> > where it is pointing at.
> > * Launch enclave incorrectly used sigstruct attributes-field instead of
> > enclave attributes-field.
> > * Removed unused struct sgx_add_page_req from sgx_ioctl.c
> > * Removed unused sgx_has_sgx2.
> > * Updated arch/x86/include/asm/sgx.h so that it provides stub
> > implementations when sgx in not enabled.
> > * Removed cruft rdmsr-calls from sgx_set_pubkeyhash_msrs().
> > * return -ENOMEM in sgx_alloc_page() when VA pages consume too much space
> > * removed unused global sgx_nr_pids
> > * moved sgx_encl_release to sgx_encl.c
> > * return -ERESTARTSYS instead of -EINTR in sgx_encl_init()
> >
> > Jarkko Sakkinen (11):
> > x86/sgx: Add ENCLS architectural error codes
> > x86/sgx: Add SGX1 and SGX2 architectural data structures
> > x86/sgx: Add wrappers for ENCLS leaf functions
> > x86/sgx: Add functions to allocate and free EPC pages
> > x86/sgx: Add the Linux SGX Enclave Driver
> > x86/sgx: Add provisioning
> > x86/sgx: Add swapping code to the core and SGX driver
> > x86/sgx: ptrace() support for the SGX driver
> > selftests/x86: Add a selftest for SGX
> > x86/sgx: Update MAINTAINERS
> > docs: x86/sgx: Document the enclave API
> >
> > Kai Huang (2):
> > x86/cpufeatures: Add Intel-defined SGX feature bit
> > x86/cpufeatures: Add Intel-defined SGX_LC feature bit
> >
> > Sean Christopherson (15):
> > x86/cpufeatures: Add SGX sub-features (as Linux-defined bits)
> > x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition
> > x86/msr: Add SGX Launch Control MSR definitions
> > x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit
> > x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
> > x86/cpu/intel: Detect SGX support and update caps appropriately
> > x86/sgx: Enumerate and track EPC sections
> > x86/sgx: Add sgx_einit() for initializing enclaves
> > x86/vdso: Add support for exception fixup in vDSO functions
> > x86/fault: Add helper function to sanitize error code
> > x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling
> > x86/traps: Attempt to fixup exceptions in vDSO before signaling
> > x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave
> > transitions
> > docs: x86/sgx: Add Architecture documentation
> > docs: x86/sgx: Document kernel internals
> >
> > Documentation/index.rst | 1 +
> > Documentation/ioctl/ioctl-number.txt | 1 +
> > Documentation/x86/index.rst | 10 +
> > Documentation/x86/sgx/1.Architecture.rst | 431 +++++++++
> > Documentation/x86/sgx/2.Kernel-internals.rst | 56 ++
> > Documentation/x86/sgx/3.API.rst | 27 +
> > Documentation/x86/sgx/index.rst | 18 +
> > MAINTAINERS | 12 +
> > arch/x86/Kconfig | 27 +
> > arch/x86/entry/vdso/Makefile | 6 +-
> > arch/x86/entry/vdso/extable.c | 37 +
> > arch/x86/entry/vdso/extable.h | 29 +
> > arch/x86/entry/vdso/vdso-layout.lds.S | 9 +-
> > arch/x86/entry/vdso/vdso.lds.S | 1 +
> > arch/x86/entry/vdso/vdso2c.h | 58 +-
> > arch/x86/entry/vdso/vsgx_enter_enclave.S | 101 +++
> > arch/x86/include/asm/cpufeatures.h | 24 +-
> > arch/x86/include/asm/disabled-features.h | 14 +-
> > arch/x86/include/asm/msr-index.h | 8 +
> > arch/x86/include/asm/traps.h | 1 +
> > arch/x86/include/asm/vdso.h | 5 +
> > arch/x86/include/uapi/asm/sgx.h | 86 ++
> > arch/x86/include/uapi/asm/sgx_errno.h | 91 ++
> > arch/x86/kernel/cpu/Makefile | 1 +
> > arch/x86/kernel/cpu/intel.c | 39 +
> > arch/x86/kernel/cpu/scattered.c | 2 +
> > arch/x86/kernel/cpu/sgx/Makefile | 2 +
> > arch/x86/kernel/cpu/sgx/arch.h | 424 +++++++++
> > arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
> > arch/x86/kernel/cpu/sgx/driver/driver.h | 38 +
> > arch/x86/kernel/cpu/sgx/driver/ioctl.c | 850 ++++++++++++++++++
> > arch/x86/kernel/cpu/sgx/driver/main.c | 368 ++++++++
> > arch/x86/kernel/cpu/sgx/encl.c | 709 +++++++++++++++
> > arch/x86/kernel/cpu/sgx/encl.h | 136 +++
> > arch/x86/kernel/cpu/sgx/encls.c | 22 +
> > arch/x86/kernel/cpu/sgx/encls.h | 244 +++++
> > arch/x86/kernel/cpu/sgx/main.c | 360 ++++++++
> > arch/x86/kernel/cpu/sgx/reclaim.c | 482 ++++++++++
> > arch/x86/kernel/cpu/sgx/sgx.h | 90 ++
> > arch/x86/kernel/traps.c | 14 +
> > arch/x86/mm/fault.c | 44 +-
> > tools/arch/x86/include/asm/cpufeatures.h | 21 +-
> > tools/testing/selftests/x86/Makefile | 10 +
> > tools/testing/selftests/x86/sgx/Makefile | 48 +
> > tools/testing/selftests/x86/sgx/defines.h | 39 +
> > tools/testing/selftests/x86/sgx/encl.c | 20 +
> > tools/testing/selftests/x86/sgx/encl.lds | 33 +
> > .../selftests/x86/sgx/encl_bootstrap.S | 94 ++
> > tools/testing/selftests/x86/sgx/encl_piggy.S | 18 +
> > tools/testing/selftests/x86/sgx/encl_piggy.h | 14 +
> > tools/testing/selftests/x86/sgx/main.c | 279 ++++++
> > tools/testing/selftests/x86/sgx/sgx_call.S | 15 +
> > tools/testing/selftests/x86/sgx/sgxsign.c | 508 +++++++++++
> > .../testing/selftests/x86/sgx/signing_key.pem | 39 +
> > 54 files changed, 5987 insertions(+), 32 deletions(-)
> > create mode 100644 Documentation/x86/index.rst
> > create mode 100644 Documentation/x86/sgx/1.Architecture.rst
> > create mode 100644 Documentation/x86/sgx/2.Kernel-internals.rst
> > create mode 100644 Documentation/x86/sgx/3.API.rst
> > create mode 100644 Documentation/x86/sgx/index.rst
> > create mode 100644 arch/x86/entry/vdso/extable.c
> > create mode 100644 arch/x86/entry/vdso/extable.h
> > create mode 100644 arch/x86/entry/vdso/vsgx_enter_enclave.S
> > create mode 100644 arch/x86/include/uapi/asm/sgx.h
> > create mode 100644 arch/x86/include/uapi/asm/sgx_errno.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/Makefile
> > create mode 100644 arch/x86/kernel/cpu/sgx/arch.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/encls.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/encls.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/main.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/reclaim.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/sgx.h
> > create mode 100644 tools/testing/selftests/x86/sgx/Makefile
> > create mode 100644 tools/testing/selftests/x86/sgx/defines.h
> > create mode 100644 tools/testing/selftests/x86/sgx/encl.c
> > create mode 100644 tools/testing/selftests/x86/sgx/encl.lds
> > create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S
> > create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.S
> > create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h
> > create mode 100644 tools/testing/selftests/x86/sgx/main.c
> > create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
> > create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c
> > create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem
> >
> > --
> > 2.19.1
> >
>
> I'm on leave for this week and next week's Monday if you wonder why I'm
> so passive in the discussion. Looking at the things next week's Tue.
>
> Just a quick comment about Andy's proposal. Probably pretty DSO like
> ELF blob could work with an addition of a section called ".tcs" for
> entry points. They need to be recognized so that the loader can add
> them as TCS pages.
Hmm.
There's a decent argument for specifically supporting whatever format
Windows uses. There's also an argument for allowing one or more
enclaves to be bundled in a regular ELF DSO. FWIW, there's no
fundamental reason we can't support more than one type of enclave
format.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface
2019-04-23 1:29 ` Andy Lutomirski
2019-04-23 1:48 ` Sean Christopherson
@ 2019-04-23 18:59 ` Sean Christopherson
2019-04-23 19:07 ` Andy Lutomirski
1 sibling, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-04-23 18:59 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Cedric Xing, LKML, X86 ML, linux-sgx, Andrew Morton, Dave,
nhorman, npmccallum, Serge, Shay, Haitao, Andy Shevchenko,
Thomas Gleixner, Kai, Borislav Petkov, Josh Triplett, Kai,
David Rientjes, Jarkko Sakkinen
On Mon, Apr 22, 2019 at 06:29:06PM -0700, Andy Lutomirski wrote:
> What's not tested here is running this code with EFLAGS.TF set and
> making sure that it unwinds correctly. Also, Jarkko, unless I missed
> something, the vDSO extable code likely has a bug. If you run the
> instruction right before ENCLU with EFLAGS.TF set, then do_debug()
> will eat the SIGTRAP and skip to the exception handler. Similarly, if
> you put an instruction breakpoint on ENCLU, it'll get skipped. Or is
> the code actually correct and am I just remembering wrong?
The code is indeed broken, and I don't see a sane way to make it not
broken other than to never do vDSO fixup on #DB or #BP. But that's
probably the right thing to do anyways since an attached debugger is
likely the intended recipient the 99.9999999% of the time.
The crux of the matter is that it's impossible to identify whether or
not a #DB/#BP originated from within an enclave, e.g. an INT3 in an
enclave will look identical to an INT3 at the AEP. Even if hardware
provided a magic flag, #DB still has scenarios where the intended
recipient is ambiguous, e.g. data breakpoint encountered in the enclave
but on an address outside of the enclave, breakpoint encountered in the
enclave and a code breakpoint on the AEP, etc...
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface
2019-04-23 18:59 ` Sean Christopherson
@ 2019-04-23 19:07 ` Andy Lutomirski
2019-04-23 20:11 ` Sean Christopherson
0 siblings, 1 reply; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-23 19:07 UTC (permalink / raw)
To: Sean Christopherson
Cc: Andy Lutomirski, Cedric Xing, LKML, X86 ML, linux-sgx,
Andrew Morton, Dave, nhorman, npmccallum, Serge, Shay, Haitao,
Andy Shevchenko, Thomas Gleixner, Kai, Borislav Petkov,
Josh Triplett, Kai, David Rientjes, Jarkko Sakkinen
On Tue, Apr 23, 2019 at 11:59 AM Sean Christopherson
<sean.j.christopherson@intel.com> wrote:
>
> On Mon, Apr 22, 2019 at 06:29:06PM -0700, Andy Lutomirski wrote:
> > What's not tested here is running this code with EFLAGS.TF set and
> > making sure that it unwinds correctly. Also, Jarkko, unless I missed
> > something, the vDSO extable code likely has a bug. If you run the
> > instruction right before ENCLU with EFLAGS.TF set, then do_debug()
> > will eat the SIGTRAP and skip to the exception handler. Similarly, if
> > you put an instruction breakpoint on ENCLU, it'll get skipped. Or is
> > the code actually correct and am I just remembering wrong?
>
> The code is indeed broken, and I don't see a sane way to make it not
> broken other than to never do vDSO fixup on #DB or #BP. But that's
> probably the right thing to do anyways since an attached debugger is
> likely the intended recipient the 99.9999999% of the time.
>
> The crux of the matter is that it's impossible to identify whether or
> not a #DB/#BP originated from within an enclave, e.g. an INT3 in an
> enclave will look identical to an INT3 at the AEP. Even if hardware
> provided a magic flag, #DB still has scenarios where the intended
> recipient is ambiguous, e.g. data breakpoint encountered in the enclave
> but on an address outside of the enclave, breakpoint encountered in the
> enclave and a code breakpoint on the AEP, etc...
Ugh. It sounds like ignoring the fixup for #DB is the right call.
But what happens if the enclave contains an INT3 or ICEBP instruction?
Are they magically promoted to #GP, perhaps?
As a maybe possible alternative, if we made it so that the AEX address
was not the same as the ENCLU, could we usefully distinguish these
exceptions based on RIP? I suppose it's also worth considering
whether page faults from *inside* the enclave should result in SIGSEGV
or result in a fixup. We certainly want page faults from the ENCLU
instruction itself to get fixed up, but maybe we want most exceptions
inside the enclave to work a bit differently. Of course, if we do
this, we need to make sure that the semantics of returning from the
signal handler are reasonable.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-23 0:37 ` [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack Cedric Xing
2019-04-23 1:25 ` Andy Lutomirski
@ 2019-04-23 19:26 ` Sean Christopherson
2019-04-23 19:44 ` Andy Lutomirski
1 sibling, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-04-23 19:26 UTC (permalink / raw)
To: Cedric Xing
Cc: linux-kernel, x86, linux-sgx, akpm, Hansen, Dave, Christopherson,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, andriy.shevchenko, tglx, Svahn, Kai, bp, josh, luto, Kai,
rientjes, Jarkko Sakkinen
On Mon, Apr 22, 2019 at 05:37:24PM -0700, Cedric Xing wrote:
> The previous __vdso_sgx_enter_enclave() requires enclaves to preserve %rsp,
> which prohibits enclaves from allocating and passing parameters for
> untrusted function calls (aka. o-calls).
>
> This patch addresses the problem above by introducing a new ABI that preserves
> %rbp instead of %rsp. Then __vdso_sgx_enter_enclave() can anchor its frame
> using %rbp so that enclaves are allowed to allocate space on the untrusted
> stack by decrementing %rsp. Please note that the stack space allocated in such
> way will be part of __vdso_sgx_enter_enclave()'s frame so will be freed after
> __vdso_sgx_enter_enclave() returns. Therefore, __vdso_sgx_enter_enclave() has
> been changed to take a callback function as an optional parameter, which if
> supplied, will be invoked upon enclave exits (both AEX (Asynchronous Enclave
> eXit) and normal exits), with the value of %rsp left
> off by the enclave as a parameter to the callback.
>
> Here's the summary of API/ABI changes in this patch. More details could be
> found in arch/x86/entry/vdso/vsgx_enter_enclave.S.
> * 'struct sgx_enclave_exception' is renamed to 'struct sgx_enclave_exinfo'
> because it is filled upon both AEX (i.e. exceptions) and normal enclave
> exits.
> * __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead of %rsp in
> the previous implementation).
> * __vdso_sgx_enter_enclave() takes one more parameter - a callback function to
> be invoked upon enclave exits. This callback is optional, and if not
> supplied, will cause __vdso_sgx_enter_enclave() to return upon enclave exits
> (same behavior as previous implementation).
> * The callback function is given as a parameter the value of %rsp at enclave
> exit to address data "pushed" by the enclave. A positive value returned by
> the callback will be treated as an ENCLU leaf for re-entering the enclave,
> while a zero or negative value will be passed through as the return
> value of __vdso_sgx_enter_enclave() to its caller. It's also safe to
> leave callback by longjmp() or by throwing a C++ exception.
>
> Signed-off-by: Cedric Xing <cedric.xing@intel.com>
> ---
> arch/x86/entry/vdso/vsgx_enter_enclave.S | 156 ++++++++++++++---------
> arch/x86/include/uapi/asm/sgx.h | 14 +-
> 2 files changed, 100 insertions(+), 70 deletions(-)
>
> diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> index fe0bf6671d6d..210f4366374a 100644
> --- a/arch/x86/entry/vdso/vsgx_enter_enclave.S
> +++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> @@ -14,88 +14,118 @@
> .code64
> .section .text, "ax"
>
> -#ifdef SGX_KERNEL_DOC
This #ifdef and the pseudo-C code below has a functional purpose. From
the original commit:
Note, the C-like pseudocode describing the assembly routine is wrapped
in a non-existent macro instead of in a comment to trick kernel-doc into
auto-parsing the documentation and function prototype. This is a double
win as the pseudocode is intended to aid kernel developers, not userland
enclave developers.
We don't need full pseudocode, but a C-like prototype is necessary to get
the kernel-doc comment parsed correctly.
I'll try to look at the actual code later today.
> /**
> * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> *
> * @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME
> - * @tcs: **IN \%rbx** - TCS, must be non-NULL
> - * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception' pointer
> + * @tcs: **IN 0x08(\%rsp)** - TCS, must be non-NULL
> + * @ex_info: **IN 0x10(\%rsp)** - Optional 'struct sgx_enclave_exinfo'
> + * pointer
> + * @callback: **IN 0x18(\%rsp)** - Optional callback function to be called on
> + * enclave exit or exception
> *
> * Return:
> * **OUT \%eax** -
> - * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is
> - * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave faults
> + * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is not
> + * allowed, %-EFAULT if ENCLU or the enclave faults, or a non-positive value
> + * returned from ``callback`` (if one is supplied).
> *
> * **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant with the
> - * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
> - * input parameters must be passed via ``%eax``, ``%rbx`` and ``%rcx``, with
> - * the return value passed via ``%eax``. All registers except ``%rsp`` must
> - * be treated as volatile from the caller's perspective, including but not
> - * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave
> - * being run **must** preserve the untrusted ``%rsp`` and stack.
> + * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
> + * input parameters must be passed via ``%eax``, ``8(%rsp)``, ``0x10(%rsp)`` and
> + * ``0x18(%rsp)``, with the return value passed via ``%eax``. All other registers
> + * will be passed through to the enclave as is. All registers except ``%rbp``
> + * must be treated as volatile from the caller's perspective, including but not
> + * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave being
> + * run **must** preserve the untrusted ``%rbp``.
> + *
> + * ``callback`` has the following signature:
> + * int callback(long rdi, long rsi, long rdx,
> + * struct sgx_enclave_exinfo *ex_info, long r8, long r9,
> + * void *tcs, long ursp);
> + * ``callback`` **shall** follow x86_64 ABI. All GPRs **except** ``%rax``, ``%rbx``
> + * and ``rcx`` are passed through to ``callback``. ``%rdi``, ``%rsi``, ``%rdx``,
> + * ``%r8``, ``%r9``, along with the value of ``%rsp`` when the enclave exited/excepted,
> + * can be accessed directly as input parameters, while other GPRs can be
> + * accessed in assembly if needed.
> + * A positive value returned from ``callback`` will be treated as an ENCLU leaf
> + * (e.g. EENTER/ERESUME) to reenter the enclave, while 0 or a negative return
> + * value will be passed back to the caller of __vdso_sgx_enter_enclave().
> + * It is also **safe** to ``longjmp()`` out of ``callback``.
> */
> -__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
> - struct sgx_enclave_exception *ex_info)
> -{
> - if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
> - return -EINVAL;
> -
> - if (!tcs)
> - return -EINVAL;
> -
> - try {
> - ENCLU[leaf];
> - } catch (exception) {
> - if (e)
> - *e = exception;
> - return -EFAULT;
> - }
> -
> - return 0;
> -}
> -#endif
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-23 19:26 ` Sean Christopherson
@ 2019-04-23 19:44 ` Andy Lutomirski
0 siblings, 0 replies; 285+ messages in thread
From: Andy Lutomirski @ 2019-04-23 19:44 UTC (permalink / raw)
To: Sean Christopherson
Cc: Cedric Xing, linux-kernel, x86, linux-sgx, akpm, Hansen, Dave,
Christopherson, nhorman, npmccallum, Ayoun, Serge, Katz-zamir,
Shay, Huang, Haitao, andriy.shevchenko, tglx, Svahn, Kai, bp,
josh, luto, Kai, rientjes, Jarkko Sakkinen
> On Apr 23, 2019, at 12:26 PM, Sean Christopherson <sean.j.christopherson@intel.com> wrote:
>
>> On Mon, Apr 22, 2019 at 05:37:24PM -0700, Cedric Xing wrote:
>> The previous __vdso_sgx_enter_enclave() requires enclaves to preserve %rsp,
>> which prohibits enclaves from allocating and passing parameters for
>> untrusted function calls (aka. o-calls).
>>
>> This patch addresses the problem above by introducing a new ABI that preserves
>> %rbp instead of %rsp. Then __vdso_sgx_enter_enclave() can anchor its frame
>> using %rbp so that enclaves are allowed to allocate space on the untrusted
>> stack by decrementing %rsp. Please note that the stack space allocated in such
>> way will be part of __vdso_sgx_enter_enclave()'s frame so will be freed after
>> __vdso_sgx_enter_enclave() returns. Therefore, __vdso_sgx_enter_enclave() has
>> been changed to take a callback function as an optional parameter, which if
>> supplied, will be invoked upon enclave exits (both AEX (Asynchronous Enclave
>> eXit) and normal exits), with the value of %rsp left
>> off by the enclave as a parameter to the callback.
>>
>> Here's the summary of API/ABI changes in this patch. More details could be
>> found in arch/x86/entry/vdso/vsgx_enter_enclave.S.
>> * 'struct sgx_enclave_exception' is renamed to 'struct sgx_enclave_exinfo'
>> because it is filled upon both AEX (i.e. exceptions) and normal enclave
>> exits.
>> * __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead of %rsp in
>> the previous implementation).
>> * __vdso_sgx_enter_enclave() takes one more parameter - a callback function to
>> be invoked upon enclave exits. This callback is optional, and if not
>> supplied, will cause __vdso_sgx_enter_enclave() to return upon enclave exits
>> (same behavior as previous implementation).
>> * The callback function is given as a parameter the value of %rsp at enclave
>> exit to address data "pushed" by the enclave. A positive value returned by
>> the callback will be treated as an ENCLU leaf for re-entering the enclave,
>> while a zero or negative value will be passed through as the return
>> value of __vdso_sgx_enter_enclave() to its caller. It's also safe to
>> leave callback by longjmp() or by throwing a C++ exception.
>>
>> Signed-off-by: Cedric Xing <cedric.xing@intel.com>
>> ---
>> arch/x86/entry/vdso/vsgx_enter_enclave.S | 156 ++++++++++++++---------
>> arch/x86/include/uapi/asm/sgx.h | 14 +-
>> 2 files changed, 100 insertions(+), 70 deletions(-)
>>
>> diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S
>> index fe0bf6671d6d..210f4366374a 100644
>> --- a/arch/x86/entry/vdso/vsgx_enter_enclave.S
>> +++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
>> @@ -14,88 +14,118 @@
>> .code64
>> .section .text, "ax"
>>
>> -#ifdef SGX_KERNEL_DOC
>
> This #ifdef and the pseudo-C code below has a functional purpose. From
> the original commit:
>
> Note, the C-like pseudocode describing the assembly routine is wrapped
> in a non-existent macro instead of in a comment to trick kernel-doc into
> auto-parsing the documentation and function prototype. This is a double
> win as the pseudocode is intended to aid kernel developers, not userland
> enclave developers.
>
> We don't need full pseudocode, but a C-like prototype is necessary to get
> the kernel-doc comment parsed correctly.
That should be explained in a comment :)
—Andy
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v1 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface
2019-04-23 19:07 ` Andy Lutomirski
@ 2019-04-23 20:11 ` Sean Christopherson
0 siblings, 0 replies; 285+ messages in thread
From: Sean Christopherson @ 2019-04-23 20:11 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Cedric Xing, LKML, X86 ML, linux-sgx, Andrew Morton, Dave,
nhorman, npmccallum, Serge, Shay, Haitao, Andy Shevchenko,
Thomas Gleixner, Kai, Borislav Petkov, Josh Triplett, Kai,
David Rientjes, Jarkko Sakkinen
On Tue, Apr 23, 2019 at 12:07:26PM -0700, Andy Lutomirski wrote:
> On Tue, Apr 23, 2019 at 11:59 AM Sean Christopherson
> <sean.j.christopherson@intel.com> wrote:
> >
> > On Mon, Apr 22, 2019 at 06:29:06PM -0700, Andy Lutomirski wrote:
> > > What's not tested here is running this code with EFLAGS.TF set and
> > > making sure that it unwinds correctly. Also, Jarkko, unless I missed
> > > something, the vDSO extable code likely has a bug. If you run the
> > > instruction right before ENCLU with EFLAGS.TF set, then do_debug()
> > > will eat the SIGTRAP and skip to the exception handler. Similarly, if
> > > you put an instruction breakpoint on ENCLU, it'll get skipped. Or is
> > > the code actually correct and am I just remembering wrong?
> >
> > The code is indeed broken, and I don't see a sane way to make it not
> > broken other than to never do vDSO fixup on #DB or #BP. But that's
> > probably the right thing to do anyways since an attached debugger is
> > likely the intended recipient the 99.9999999% of the time.
> >
> > The crux of the matter is that it's impossible to identify whether or
> > not a #DB/#BP originated from within an enclave, e.g. an INT3 in an
> > enclave will look identical to an INT3 at the AEP. Even if hardware
> > provided a magic flag, #DB still has scenarios where the intended
> > recipient is ambiguous, e.g. data breakpoint encountered in the enclave
> > but on an address outside of the enclave, breakpoint encountered in the
> > enclave and a code breakpoint on the AEP, etc...
>
> Ugh. It sounds like ignoring the fixup for #DB is the right call.
> But what happens if the enclave contains an INT3 or ICEBP instruction?
> Are they magically promoted to #GP, perhaps?
#UD for opt-out, a.k.a. non-debug, enclaves. Delivered "normally" for
opt-in debug enclaves, except they're fault-like instead of trap-like.
> As a maybe possible alternative, if we made it so that the AEX address
> was not the same as the ENCLU, could we usefully distinguish these
> exceptions based on RIP?
Not really, because a user could set a code breakpoint on the AEX or
insert an INT3, e.g. to break on exit from the enclave.
Theoretically the kernel could cross-reference addresses to determine
whether or not a DRx match occurred on an enclave address, but a) that'd
be pretty ugly to implement and b) there would still be ambiguity, e.g. if
there's a code breakpoint on the AEX and a #DB occurs in the enclave, then
DR6 will record both the in-enclave DRx match and the AEX (non-enclave)
DRx match.
> I suppose it's also worth considering
> whether page faults from *inside* the enclave should result in SIGSEGV
> or result in a fixup. We certainly want page faults from the ENCLU
> instruction itself to get fixed up, but maybe we want most exceptions
> inside the enclave to work a bit differently. Of course, if we do
> this, we need to make sure that the semantics of returning from the
> signal handler are reasonable.
Hmm, I'm pretty sure any fault that is 100% in the domain of the enclave
should result in fixup.
Here are a few use cases off the top of my head that would require the
enclave's runtime to intercept the signal, either to reenter the enclave
or to feed the fault into the enclave's handler:
- Handle EPC invalidation, e.g. due to VM migration, while a thread is
in the enclave since the resulting #PF can occur inside the enclave.
- During enclave development, configure the runtime to call into the
enclave on any exception so that the enclave can dump register state.
- Implement copy-on-write or lazy allocation using SGX2 instructions,
which would require feeding the #PF back into the enclave. Purely
theoretical AFAIK, but lazy allocation in particular could be
interesting, e.g. don't allocate .bss pages at startup time.
- An enclave and its runtime might feed #UDs back into the enclave,
e.g. to run an unmodified binary in an enclave by wrapping it in a
shim of sorts.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-22 16:26 ` Andy Lutomirski
@ 2019-04-23 21:15 ` Jethro Beekman
2019-05-10 17:23 ` Xing, Cedric
1 sibling, 0 replies; 285+ messages in thread
From: Jethro Beekman @ 2019-04-23 21:15 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Thomas Gleixner, Dr. Greg, Dave Hansen, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
[-- Attachment #1: Type: text/plain, Size: 5839 bytes --]
On 2019-04-22 09:26, Andy Lutomirski wrote:
>> On Apr 19, 2019, at 2:56 PM, Jethro Beekman <jethro@fortanix.com> wrote:
>> This works fine with v20 as-is. However, consider the equivalent of the
>> PT-based flow:
>>
>> mmap(PROT_READ|PROT_EXEC)
>> ioctl(EADD) <-- no error!
>
> Indeed!
>
>>
>> It's not me that's working around the LSM, it's the SGX driver! It's
>> writing to memory that's not marked writable! The fundamental issue here
>> is that the SGX instruction set has several instructions that bypass the
>> page table permission bits, and this is (naturally) confusing to any
>> kind of reference monitor like the LSM framework. You can come up with
>> similar scenarios that involve PROT_READ|PROT_WRITE|PROT_EXEC or
>> ptrace(PTRACE_POKETEXT). So, clearly, the proper way to fix this failure
>> of complete mediation is by enforcing appropriate page-table permissions
>> even on the SGX instructions that don't do it themselves. Just make any
>> implicit memory access look like a regular memory access and now
>> everyone is on the same page (pun intended).
>>
>
> I agree that we should do this. But then what?
Then, we have a minimum viable SGX implementation that doesn't make
things worse than they are today from a userspace code loading/LSM
perspective. People without LSMs can use SGX and people with LSMs are
not more vulnerable than before. I agree that we should do something
along the following lines...
> So I think we need a better EADD ioctl that explicitly does work on
> PROT_READ|PROT_EXEC enclave memory but makes up for by validating the
> *source* of the data. The effect will be similar to mapping a
> labeled, appraised, etc file as PROT_EXEC.
... but I don't see why this would need to be in the initial patch set.
We need to take some time to explore the design space here (see
additional comments below), and I don't think it's necessary to wait for it.
> Maybe, in extreme pseudocode:
>
> fd = open(“/dev/sgx/enclave”);
> ioctl(fd, SGX_CREATE_FROM_FILE, file_fd);
> // fd now inherits the LSM label from the file, or is otherwise marked.
> mmap(fd, PROT_READ|PROT_EXEC, ...);
>
> I suppose that an alternative would be to delegate all the EADD calls
> to a privileged daemon, but that’s nasty.
>
What file format should this be in? I have worked with several different
binary enclave formats and I don't really like any of them.
# ELF
Pros:
* People know about ELF.
* Allows storing additional metadata that is only used by userspace, not
the enclave itself.
Cons:
* ELF generally loads all kinds of stuff in memory that is not necessary
for enclaves, such as the ELF header.
* Special tools are needed to calculate ENCLAVEHASH, for signing &
verification.
* All tools need to agree on the exact transformation.
* Unclear how to specify things such as: which 256-byte chunks of memory
should be measured, heap, TCS pages, stacks, SSAs, etc.
Questions:
* If using ELF, should this be the same format that the Intel Linux SDK
uses (not documented, but source code is available) or something newly
standardized?
# PE (Windows Intel SDK format)
Andy suggested this in another email. I'm not sure why exactly?
Pros:
* Used by Windows enclaves?
* Allows storing additional metadata that is only used by userspace, not
the enclave itself.
Cons:
* The format is not documented. I did some RE on this format a long time
ago. See
https://github.com/fortanix/rust-sgx/blob/master/doc/WINTEL-SGX-ABI.md
and
https://github.com/fortanix/rust-sgx/blob/master/sgxs-tools/src/bin/isgx-pe2sgx.rs.
* PE is not really used on Linux.
* All same cons as ELF above.
# CPU-native (hash stream) "SGXS"
The security properties of an enclave are completely defined by the hash
that's calculated by the processor while loading the enclave. The exact
hashed data is what I call the "SGX stream" format (SGXS). This is fully
described by the Intel SDM. I've written down some notes about this at
https://github.com/fortanix/rust-sgx/blob/master/doc/SGXS.md. That
document also defines a notion of canonicality for streams. You can
ignore the section on "Enhanced SGXS", which is a failed experiment.
Pros:
* Computing ENCLAVEHASH is a simple SHA256 of the file.
* No complex transformations needed to load enclave.
Cons:
* No way to specify memory contents of non-measured memory.
* No space for non-enclave metadata (including SIGSTRUCT).
* Not a standard format for transporting binaries.
# CPU-native (instruction stream)
An enclave's memory contents is fully defined by the set of
ECREATE/EADD/EEXTEND/EINIT instructions that the OS needs to execute.
One could envision a format that describes exactly those instructions.
One difference with the SGXS format described above is that the enclave
memory is described as part of EADD, not EEXTEND. This allows including
specific values for non-measured memory.
Pros:
* No complex transformations needed to load enclave.
* Obvious place to store SIGSTRUCT.
Cons:
* Special tools are needed to calculate ENCLAVEHASH, for signing &
verification.
* No obvious space for non-enclave metadata.
* Not a standard format for transporting binaries.
---
We've been using the SGXS format for a couple of years, and also the
"Enhanced SGXS" format. I think SGXS make a lot of sense for SGX
software, Enhanced SGXS not so much. I've recently been pondering
developing a new format that is basically an archive (tar? but
preferably something with an index) of SGXS, SIGSTRUCT, some file
describing non-measured memory contents (format TBD), and additional
non-enclave metadata.
I'd be interested in hearing people's thoughts on file formats.
--
Jethro Beekman | Fortanix
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver
2019-04-22 21:58 ` Sean Christopherson
@ 2019-04-23 23:29 ` Jethro Beekman
2019-04-24 0:26 ` Sean Christopherson
0 siblings, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-04-23 23:29 UTC (permalink / raw)
To: Sean Christopherson, Jarkko Sakkinen
Cc: linux-kernel, x86, linux-sgx, akpm, dave.hansen, nhorman,
npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
rientjes
[-- Attachment #1: Type: text/plain, Size: 2739 bytes --]
On 2019-04-22 14:58, Sean Christopherson wrote:
> +Cc Jethro
>
> On Wed, Apr 17, 2019 at 01:39:25PM +0300, Jarkko Sakkinen wrote:
>> Intel Software Guard eXtensions (SGX) is a set of CPU instructions that
>> can be used by applications to set aside private regions of code and
>> data. The code outside the enclave is disallowed to access the memory
>> inside the enclave by the CPU access control.
>>
>> This commit adds the Linux SGX Enclave Driver that provides an ioctl API
>> to manage enclaves. The address range for an enclave, commonly referred
>> as ELRANGE in the documentation (e.g. Intel SDM), is reserved with
>> mmap() against /dev/sgx/enclave. After that a set ioctls is used to
>> build the enclave to the ELRANGE.
>>
>> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
>> Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
>> Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
>> Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
>> Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
>> Co-developed-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
>> Signed-off-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
>> Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
>> Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
>> ---
>
> ...
>
>> +#ifdef CONFIG_ACPI
>> +static struct acpi_device_id sgx_device_ids[] = {
>> + {"INT0E0C", 0},
>> + {"", 0},
>> +};
>> +MODULE_DEVICE_TABLE(acpi, sgx_device_ids);
>> +#endif
>> +
>> +static struct platform_driver sgx_drv = {
>> + .probe = sgx_drv_probe,
>> + .remove = sgx_drv_remove,
>> + .driver = {
>> + .name = "sgx",
>> + .acpi_match_table = ACPI_PTR(sgx_device_ids),
>> + },
>> +};
>
> Where do we stand on removing the ACPI and platform_driver dependencies?
> Can we get rid of them sooner rather than later?
You know my position on this...
https://www.spinics.net/lists/linux-sgx/msg00624.html . I don't really
have any new arguments.
Considering the amount of planned changes for the driver post-merge, I
think it's crucial that the driver part can be swapped out with
alternative implementations.
> Now that the core SGX code is approaching stability, I'd like to start
> sending RFCs for the EPC virtualization and KVM bits to hash out that side
> of things. The ACPI crud is the last chunk of code that would require
> non-trivial changes to the core SGX code for the proposed virtualization
> implementation. I'd strongly prefer to get it out of the way before
> sending the KVM RFCs.
What kind of changes? Wouldn't KVM just be another consumer of the same
API used by the driver?
--
Jethro Beekman | Fortanix
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver
2019-04-23 23:29 ` Jethro Beekman
@ 2019-04-24 0:26 ` Sean Christopherson
2019-04-24 1:04 ` Jethro Beekman
2019-06-04 20:12 ` Sean Christopherson
0 siblings, 2 replies; 285+ messages in thread
From: Sean Christopherson @ 2019-04-24 0:26 UTC (permalink / raw)
To: Jethro Beekman
Cc: Jarkko Sakkinen, linux-kernel, x86, linux-sgx, akpm, dave.hansen,
nhorman, npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
rientjes
On Tue, Apr 23, 2019 at 11:29:24PM +0000, Jethro Beekman wrote:
> On 2019-04-22 14:58, Sean Christopherson wrote:
> >+Cc Jethro
> >
> >On Wed, Apr 17, 2019 at 01:39:25PM +0300, Jarkko Sakkinen wrote:
> >>Intel Software Guard eXtensions (SGX) is a set of CPU instructions that
> >>can be used by applications to set aside private regions of code and
> >>data. The code outside the enclave is disallowed to access the memory
> >>inside the enclave by the CPU access control.
> >>
> >>This commit adds the Linux SGX Enclave Driver that provides an ioctl API
> >>to manage enclaves. The address range for an enclave, commonly referred
> >>as ELRANGE in the documentation (e.g. Intel SDM), is reserved with
> >>mmap() against /dev/sgx/enclave. After that a set ioctls is used to
> >>build the enclave to the ELRANGE.
> >>
> >>Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> >>Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
> >>Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> >>Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
> >>Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
> >>Co-developed-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
> >>Signed-off-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
> >>Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
> >>Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
> >>---
> >
> >...
> >
> >>+#ifdef CONFIG_ACPI
> >>+static struct acpi_device_id sgx_device_ids[] = {
> >>+ {"INT0E0C", 0},
> >>+ {"", 0},
> >>+};
> >>+MODULE_DEVICE_TABLE(acpi, sgx_device_ids);
> >>+#endif
> >>+
> >>+static struct platform_driver sgx_drv = {
> >>+ .probe = sgx_drv_probe,
> >>+ .remove = sgx_drv_remove,
> >>+ .driver = {
> >>+ .name = "sgx",
> >>+ .acpi_match_table = ACPI_PTR(sgx_device_ids),
> >>+ },
> >>+};
> >
> >Where do we stand on removing the ACPI and platform_driver dependencies?
> >Can we get rid of them sooner rather than later?
>
> You know my position on this...
> https://www.spinics.net/lists/linux-sgx/msg00624.html . I don't really have
> any new arguments.
>
> Considering the amount of planned changes for the driver post-merge, I think
> it's crucial that the driver part can be swapped out with alternative
> implementations.
This gets far outside of my area of expertise as I think this is more of
a policy question as opposed to a technical question, e.g. do we export
function simply to allow out-of-tree alternatives.
> >Now that the core SGX code is approaching stability, I'd like to start
> >sending RFCs for the EPC virtualization and KVM bits to hash out that side
> >of things. The ACPI crud is the last chunk of code that would require
> >non-trivial changes to the core SGX code for the proposed virtualization
> >implementation. I'd strongly prefer to get it out of the way before
> >sending the KVM RFCs.
>
> What kind of changes? Wouldn't KVM just be another consumer of the same API
> used by the driver?
Nope, userspace "only" needs to be able to mmap() arbitrary chunks of EPC.
Except for EPC management, which is already in built into the kernel, the
EPC virtualization code has effectively zero overlap with the driver. Of
course this is all technically speculative since none of this is upstream...
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver
2019-04-24 0:26 ` Sean Christopherson
@ 2019-04-24 1:04 ` Jethro Beekman
2019-04-29 19:08 ` Sean Christopherson
2019-06-04 20:12 ` Sean Christopherson
1 sibling, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-04-24 1:04 UTC (permalink / raw)
To: Sean Christopherson
Cc: Jarkko Sakkinen, linux-kernel, x86, linux-sgx, akpm, dave.hansen,
nhorman, npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
rientjes
[-- Attachment #1: Type: text/plain, Size: 903 bytes --]
On 2019-04-23 17:26, Sean Christopherson wrote:
> On Tue, Apr 23, 2019 at 11:29:24PM +0000, Jethro Beekman wrote:
>> On 2019-04-22 14:58, Sean Christopherson wrote:
>>> Now that the core SGX code is approaching stability, I'd like to start
>>> sending RFCs for the EPC virtualization and KVM bits to hash out that side
>>> of things. The ACPI crud is the last chunk of code that would require
>>> non-trivial changes to the core SGX code for the proposed virtualization
>>> implementation. I'd strongly prefer to get it out of the way before
>>> sending the KVM RFCs.
>>
>> What kind of changes? Wouldn't KVM just be another consumer of the same API
>> used by the driver?
>
> Nope, userspace "only" needs to be able to mmap() arbitrary chunks of EPC.
I don't think this is sufficient. Don't you need enclave tracking in
order to support paging?
--
Jethro Beekman | Fortanix
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 16/28] x86/sgx: Add provisioning
2019-04-17 10:39 ` [PATCH v20 16/28] x86/sgx: Add provisioning Jarkko Sakkinen
2019-04-19 3:06 ` Huang, Kai
@ 2019-04-24 1:34 ` Jethro Beekman
2019-05-02 8:27 ` Jarkko Sakkinen
1 sibling, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-04-24 1:34 UTC (permalink / raw)
To: Jarkko Sakkinen, linux-kernel, x86, linux-sgx
Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
James Morris, Serge E . Hallyn, linux-security-module
[-- Attachment #1: Type: text/plain, Size: 622 bytes --]
On 2019-04-17 03:39, Jarkko Sakkinen wrote:
> diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
> index 7bf627ac4958..3b80acde8671 100644
> --- a/arch/x86/include/uapi/asm/sgx.h
> +++ b/arch/x86/include/uapi/asm/sgx.h
> @@ -16,6 +16,8 @@
> _IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page)
> #define SGX_IOC_ENCLAVE_INIT \
> _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
> +#define SGX_IOC_ENCLAVE_SET_ATTRIBUTE \
> + _IOW(SGX_MAGIC, 0x03, struct sgx_enclave_set_attribute)
Need to update Documentation/ioctl/ioctl-number.txt as well
--
Jethro Beekman | Fortanix
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]
^ permalink raw reply [flat|nested] 285+ messages in thread
* [RFC PATCH v2 0/3] An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack
2019-04-23 0:37 ` [RFC PATCH v1 0/3] An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack Cedric Xing
@ 2019-04-24 6:26 ` Cedric Xing
2019-07-10 11:17 ` Jarkko Sakkinen
2019-04-24 6:26 ` [RFC PATCH v2 1/3] selftests/x86: Fixed Makefile for SGX selftest Cedric Xing
` (2 subsequent siblings)
3 siblings, 1 reply; 285+ messages in thread
From: Cedric Xing @ 2019-04-24 6:26 UTC (permalink / raw)
To: linux-kernel, linux-sgx
Cc: cedric.xing, akpm, dave.hansen, sean.j.christopherson,
serge.ayoun, shay.katz-zamir, haitao.huang, kai.svahn, kai.huang,
jarkko.sakkinen
The current proposed __vdso_sgx_enter_enclave() requires enclaves to preserve
%rsp, which prohibits enclaves from allocating space on the untrusted stack.
However, there are existing enclaves (e.g. those built with current Intel SGX
SDK libraries) relying on the untrusted stack for passing parameters to
untrusted functions (aka. o-calls), which requires allocating space on the
untrusted stack by enclaves. And given its simplicity and convenience, it could
be desired by future SGX applications as well.
This patchset introduces a new ABI for __vdso_sgx_enter_enclave() to anchor its
stack frame on %rbp (instead of %rsp), so as to allow enclaves to "push" onto
the untrusted stack by decrementing the untrusted %rsp. Additionally, this new
__vdso_sgx_enter_enclave() will take one more parameter - a callback function,
to be invoked upon all enclave exits (both AEX and normal exits). The callback
function will be given the value of %rsp left off by the enclave, so that data
"pushed" by the enclave (if any) could be addressed/accessed. Please note that
the callback function is optional, and if not supplied (i.e. null),
__vdso_sgx_enter_enclave() will just return (i.e. behave the same as the
current implementation) after the enclave exits (or AEX due to exceptions).
The SGX selftest is augmented by two new tests. One exercises the new callback
interface, and serves as a simple example to showcase how to use it; while the
other validates the hand-crafted CFI directives in __vdso_sgx_enter_enclave()
by single-stepping through it and unwinding call stack at every instruction.
v2:
- Revised comments in __vdso_sgx_enter_enclave(). See patch 2/3.
- Added stack unwind test. See patch 3/3.
v1: https://lkml.org/lkml/2019/4/22/871
Note: This patchset is based upon SGX1 patch v20
(https://lkml.org/lkml/2019/4/17/344) by Jarkko Sakkinen
Cedric Xing (3):
selftests/x86: Fixed Makefile for SGX selftest
x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing
on untrusted stack
selftests/x86: Augment SGX selftest to test new
__vdso_sgx_enter_enclave() and its callback interface
arch/x86/entry/vdso/vsgx_enter_enclave.S | 175 +++++++----
arch/x86/include/uapi/asm/sgx.h | 14 +-
tools/testing/selftests/x86/Makefile | 12 +-
tools/testing/selftests/x86/sgx/Makefile | 49 ++--
tools/testing/selftests/x86/sgx/main.c | 323 ++++++++++++++++++---
tools/testing/selftests/x86/sgx/sgx_call.S | 40 ++-
6 files changed, 471 insertions(+), 142 deletions(-)
--
2.17.1
^ permalink raw reply [flat|nested] 285+ messages in thread
* [RFC PATCH v2 1/3] selftests/x86: Fixed Makefile for SGX selftest
2019-04-23 0:37 ` [RFC PATCH v1 0/3] An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack Cedric Xing
2019-04-24 6:26 ` [RFC PATCH v2 " Cedric Xing
@ 2019-04-24 6:26 ` Cedric Xing
2019-07-12 3:19 ` Jarkko Sakkinen
2019-04-24 6:26 ` [RFC PATCH v2 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack Cedric Xing
2019-04-24 6:26 ` [RFC PATCH v2 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface Cedric Xing
3 siblings, 1 reply; 285+ messages in thread
From: Cedric Xing @ 2019-04-24 6:26 UTC (permalink / raw)
To: linux-kernel, linux-sgx
Cc: cedric.xing, akpm, dave.hansen, sean.j.christopherson,
serge.ayoun, shay.katz-zamir, haitao.huang, kai.svahn, kai.huang,
jarkko.sakkinen
The original x86/sgx/Makefile doesn't work when 'x86/sgx' is specified as the
test target. This patch fixes that problem, along with minor changes to the
dependencies between 'x86' and 'x86/sgx' in selftests/x86/Makefile.
Signed-off-by: Cedric Xing <cedric.xing@intel.com>
---
tools/testing/selftests/x86/Makefile | 12 +++----
tools/testing/selftests/x86/sgx/Makefile | 45 +++++++++---------------
2 files changed, 22 insertions(+), 35 deletions(-)
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 4fc9a42f56ea..1294c5f5b6ca 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -70,11 +70,11 @@ all_32: $(BINARIES_32)
all_64: $(BINARIES_64)
-all_64: $(SUBDIRS_64)
- @for DIR in $(SUBDIRS_64); do \
- BUILD_TARGET=$(OUTPUT)/$$DIR; \
- mkdir $$BUILD_TARGET -p; \
- make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+all_64: | $(SUBDIRS_64)
+ @for DIR in $|; do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ mkdir $$BUILD_TARGET -p; \
+ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
done
EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64)
@@ -90,7 +90,7 @@ ifeq ($(CAN_BUILD_I386)$(CAN_BUILD_X86_64),01)
all: warn_32bit_failure
warn_32bit_failure:
- @echo "Warning: you seem to have a broken 32-bit build" 2>&1; \
+ @echo "Warning: you seem to have a broken 32-bit build" 2>&1; \
echo "environment. This will reduce test coverage of 64-bit" 2>&1; \
echo "kernels. If you are using a Debian-like distribution," 2>&1; \
echo "try:"; 2>&1; \
diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile
index 1fd6f2708e81..3af15d7c8644 100644
--- a/tools/testing/selftests/x86/sgx/Makefile
+++ b/tools/testing/selftests/x86/sgx/Makefile
@@ -2,47 +2,34 @@ top_srcdir = ../../../../..
include ../../lib.mk
-HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC
-ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \
+ifeq ($(shell $(CC) -dumpmachine | cut --delimiter=- -f1),x86_64)
+all: all_64
+endif
+
+HOST_CFLAGS := -Wall -Werror -g $(INCLUDES)
+ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIE \
-fno-stack-protector -mrdrnd $(INCLUDES)
TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx
all_64: $(TEST_CUSTOM_PROGS)
-$(TEST_CUSTOM_PROGS): $(OUTPUT)/main.o $(OUTPUT)/sgx_call.o \
- $(OUTPUT)/encl_piggy.o
+$(TEST_CUSTOM_PROGS): main.c sgx_call.S $(OUTPUT)/encl_piggy.o
$(CC) $(HOST_CFLAGS) -o $@ $^
-$(OUTPUT)/main.o: main.c
- $(CC) $(HOST_CFLAGS) -c $< -o $@
-
-$(OUTPUT)/sgx_call.o: sgx_call.S
- $(CC) $(HOST_CFLAGS) -c $< -o $@
-
-$(OUTPUT)/encl_piggy.o: $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
- $(CC) $(HOST_CFLAGS) -c encl_piggy.S -o $@
+$(OUTPUT)/encl_piggy.o: encl_piggy.S $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+ $(CC) $(HOST_CFLAGS) -I$(OUTPUT) -c $< -o $@
-$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf $(OUTPUT)/sgxsign
+$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf
objcopy --remove-section=.got.plt -O binary $< $@
-$(OUTPUT)/encl.elf: $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o
- $(CC) $(ENCL_CFLAGS) -T encl.lds -o $@ $^
+$(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S
+ $(CC) $(ENCL_CFLAGS) -T $^ -o $@
-$(OUTPUT)/encl.o: encl.c
- $(CC) $(ENCL_CFLAGS) -c $< -o $@
-
-$(OUTPUT)/encl_bootstrap.o: encl_bootstrap.S
- $(CC) $(ENCL_CFLAGS) -c $< -o $@
-
-$(OUTPUT)/encl.ss: $(OUTPUT)/encl.bin $(OUTPUT)/sgxsign
- $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+$(OUTPUT)/encl.ss: $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin
+ $^ $@
$(OUTPUT)/sgxsign: sgxsign.c
$(CC) -o $@ $< -lcrypto
-EXTRA_CLEAN := $(OUTPUT)/sgx-selftest $(OUTPUT)/sgx-selftest.o \
- $(OUTPUT)/sgx_call.o $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss \
- $(OUTPUT)/encl.elf $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o \
- $(OUTPUT)/sgxsign
-
-.PHONY: clean
+EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(addprefix $(OUTPUT)/, \
+ encl.elf encl.bin encl.ss encl_piggy.o sgxsign)
--
2.17.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [RFC PATCH v2 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-23 0:37 ` [RFC PATCH v1 0/3] An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack Cedric Xing
2019-04-24 6:26 ` [RFC PATCH v2 " Cedric Xing
2019-04-24 6:26 ` [RFC PATCH v2 1/3] selftests/x86: Fixed Makefile for SGX selftest Cedric Xing
@ 2019-04-24 6:26 ` Cedric Xing
2019-04-24 19:04 ` Sean Christopherson
2019-04-24 6:26 ` [RFC PATCH v2 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface Cedric Xing
3 siblings, 1 reply; 285+ messages in thread
From: Cedric Xing @ 2019-04-24 6:26 UTC (permalink / raw)
To: linux-kernel, linux-sgx
Cc: cedric.xing, akpm, dave.hansen, sean.j.christopherson,
serge.ayoun, shay.katz-zamir, haitao.huang, kai.svahn, kai.huang,
jarkko.sakkinen
The previous __vdso_sgx_enter_enclave() requires enclaves to preserve %rsp,
which prohibits enclaves from allocating and passing parameters for
untrusted function calls (aka. o-calls).
This patch addresses the problem above by introducing a new ABI that preserves
%rbp instead of %rsp. Then __vdso_sgx_enter_enclave() can anchor its frame
using %rbp so that enclaves are allowed to allocate space on the untrusted
stack by decrementing %rsp. Please note that the stack space allocated in such
way will be part of __vdso_sgx_enter_enclave()'s frame so will be freed after
__vdso_sgx_enter_enclave() returns. Therefore, __vdso_sgx_enter_enclave() has
been revised to take a callback function as an optional parameter, which if
supplied, will be invoked upon enclave exits (both AEX (Asynchronous Enclave
eXit) and normal exits), with the value of %rsp left off by the enclave as a
parameter to the callback.
Here's the summary of API/ABI changes in this patch. More details could be
found in arch/x86/entry/vdso/vsgx_enter_enclave.S.
* 'struct sgx_enclave_exception' is renamed to 'struct sgx_enclave_exinfo'
because it is filled upon both AEX (i.e. exceptions) and normal enclave
exits.
* __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead of %rsp in
the previous implementation).
* __vdso_sgx_enter_enclave() takes one more parameter - a callback function to
be invoked upon enclave exits. This callback is optional, and if not
supplied, will cause __vdso_sgx_enter_enclave() to return upon enclave exits
(same behavior as previous implementation).
* The callback function is given as a parameter the value of %rsp at enclave
exit to address data "pushed" by the enclave. A positive value returned by
the callback will be treated as an ENCLU leaf for re-entering the enclave,
while a zero or negative value will be passed through as the return
value of __vdso_sgx_enter_enclave() to its caller. It's also safe to
leave callback by longjmp() or by throwing a C++ exception.
Signed-off-by: Cedric Xing <cedric.xing@intel.com>
---
arch/x86/entry/vdso/vsgx_enter_enclave.S | 175 ++++++++++++++++-------
arch/x86/include/uapi/asm/sgx.h | 14 +-
2 files changed, 128 insertions(+), 61 deletions(-)
diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S
index fe0bf6671d6d..debecb0d785c 100644
--- a/arch/x86/entry/vdso/vsgx_enter_enclave.S
+++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
@@ -19,83 +19,150 @@
* __vdso_sgx_enter_enclave() - Enter an SGX enclave
*
* @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME
- * @tcs: **IN \%rbx** - TCS, must be non-NULL
- * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception' pointer
+ * @tcs: **IN 0x08(\%rsp)** - TCS, must be non-NULL
+ * @ex_info: **IN 0x10(\%rsp)** - Optional 'struct sgx_enclave_exinfo'
+ * pointer
+ * @callback: **IN 0x18(\%rsp)** - Optional callback function to be called on
+ * enclave exit or exception
*
* Return:
* **OUT \%eax** -
- * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is
- * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave faults
+ * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is not
+ * allowed, %-EFAULT if ENCLU or the enclave faults, or a non-positive value
+ * returned from ``callback`` (if one is supplied).
*
* **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant with the
- * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
- * input parameters must be passed via ``%eax``, ``%rbx`` and ``%rcx``, with
- * the return value passed via ``%eax``. All registers except ``%rsp`` must
- * be treated as volatile from the caller's perspective, including but not
- * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave
- * being run **must** preserve the untrusted ``%rsp`` and stack.
+ * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
+ * input parameters must be passed via ``%eax``, ``8(%rsp)``, ``0x10(%rsp)`` and
+ * ``0x18(%rsp)``, with the return value passed via ``%eax``. All other
+ * registers will be passed through to the enclave as is. All registers except
+ * ``%rbp`` must be treated as volatile from the caller's perspective, including
+ * but not limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the
+ * enclave being run **must** preserve the untrusted ``%rbp``.
+ *
+ * ``callback`` has the following signature:
+ * int callback(long rdi, long rsi, long rdx,
+ * struct sgx_enclave_exinfo *exinfo, long r8, long r9,
+ * void *tcs, long ursp);
+ * ``callback`` **shall** follow x86_64 ABI. All GPRs **except** ``%rax``,
+ * ``%rbx`` and ``rcx`` are passed through to ``callback``. ``%rdi``, ``%rsi``,
+ * ``%rdx``, ``%r8``, ``%r9``, along with the value of ``%rsp`` when the enclave
+ * exited/excepted, can be accessed directly as input parameters, while other
+ * GPRs can be accessed in assembly if needed. A positive value returned from
+ * ``callback`` will be treated as an ENCLU leaf (e.g. EENTER/ERESUME) to
+ * reenter the enclave (without popping the extra data pushed by the enclave off
+ * the stack), while 0 (zero) or a negative return value will be passed back to
+ * the caller of __vdso_sgx_enter_enclave(). It is also safe to leave
+ * ``callback`` via ``longjmp()`` or by throwing a C++ exception.
*/
-__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
- struct sgx_enclave_exception *ex_info)
+typedef int (*sgx_callback)(long rdi, long rsi, long rdx,
+ struct sgx_enclave_exinfo *exinfo, long r8,
+ long r9, void *tcs, long ursp);
+int __vdso_sgx_enter_enclave(int leaf, void *tcs,
+ struct sgx_enclave_exinfo *exinfo,
+ sgx_callback callback)
{
- if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
- return -EINVAL;
-
- if (!tcs)
- return -EINVAL;
-
- try {
- ENCLU[leaf];
- } catch (exception) {
- if (e)
- *e = exception;
- return -EFAULT;
+ while (leaf == EENTER || leaf == ERESUME) {
+ int rc;
+ try {
+ ENCLU[leaf];
+ rc = 0;
+ if (exinfo)
+ exinfo->leaf = EEXIT;
+ } catch (exception) {
+ rc = -EFAULT;
+ if (exinfo)
+ *exinfo = exception;
+ }
+
+ leaf = callback ? (*callback)(
+ rdi, rsi, rdx, exinfo, r8, r9, tcs, ursp) : rc;
}
- return 0;
+ return leaf > 0 ? -EINVAL : leaf;
}
#endif
ENTRY(__vdso_sgx_enter_enclave)
- /* EENTER <= leaf <= ERESUME */
+ /* Prolog */
+ .cfi_startproc
+ push %rbp
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rbp, 0
+ mov %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+
+1: /* EENTER <= leaf <= ERESUME */
cmp $0x2, %eax
- jb bad_input
-
+ jb 6f
cmp $0x3, %eax
- ja bad_input
+ ja 6f
- /* TCS must be non-NULL */
- test %rbx, %rbx
- je bad_input
+ /* Load TCS and AEP */
+ mov 0x10(%rbp), %rbx
+ lea 2f(%rip), %rcx
- /* Save @exception_info */
- push %rcx
+ /* Single ENCLU serving as both EENTER and AEP (ERESUME) */
+2: enclu
- /* Load AEP for ENCLU */
- lea 1f(%rip), %rcx
-1: enclu
+ /* EEXIT path */
+ xor %ebx, %ebx
+3: mov 0x18(%rbp), %rcx
+ jrcxz 4f
+ mov %eax, EX_LEAF(%rcx)
+ jnc 4f
+ mov %di, EX_TRAPNR(%rcx)
+ mov %si, EX_ERROR_CODE(%rcx)
+ mov %rdx, EX_ADDRESS(%rcx)
- add $0x8, %rsp
- xor %eax, %eax
+4: /* Call *callback if supplied */
+ mov 0x20(%rbp), %rax
+ test %rax, %rax
+ /* At this point, %ebx holds the effective return value, which shall be
+ * returned if no callback is specified */
+ cmovz %rbx, %rax
+ jz 7f
+ /* Align stack per x86_64 ABI. The original %rsp is saved in %rbx to be
+ * restored after *callback returns. */
+ mov %rsp, %rbx
+ and $-0x10, %rsp
+ /* Clear RFLAGS.DF per x86_64 ABI */
+ cld
+ /* Parameters for *callback */
+ push %rbx
+ push 0x10(%rbp)
+ /* Call *%rax via retpoline */
+ call 40f
+ /* Restore %rsp to its original value left off by the enclave from last
+ * exit */
+ mov %rbx, %rsp
+ /* Positive return value from *callback will be interpreted as an ENCLU
+ * leaf, while a non-positive value will be interpreted as the return
+ * value to be passed back to the caller. */
+ jmp 1b
+40: /* retpoline */
+ call 42f
+41: pause
+ lfence
+ jmp 41b
+42: mov %rax, (%rsp)
ret
-bad_input:
- mov $(-EINVAL), %rax
- ret
+5: /* Exception path */
+ mov $-EFAULT, %ebx
+ stc
+ jmp 3b
-.pushsection .fixup, "ax"
- /* Re-load @exception_info and fill it (if it's non-NULL) */
-2: pop %rcx
- test %rcx, %rcx
- je 3f
+6: /* Unsupported ENCLU leaf */
+ cmp $0, %eax
+ jle 7f
+ mov $-EINVAL, %eax
- mov %eax, EX_LEAF(%rcx)
- mov %di, EX_TRAPNR(%rcx)
- mov %si, EX_ERROR_CODE(%rcx)
- mov %rdx, EX_ADDRESS(%rcx)
-3: mov $(-EFAULT), %rax
+7: /* Epilog */
+ leave
+ .cfi_def_cfa %rsp, 8
ret
-.popsection
+ .cfi_endproc
-_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
+_ASM_VDSO_EXTABLE_HANDLE(2b, 5b)
ENDPROC(__vdso_sgx_enter_enclave)
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index 9ed690a38c70..50d2b5143e5e 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -24,7 +24,7 @@
/**
* struct sgx_enclave_create - parameter structure for the
- * %SGX_IOC_ENCLAVE_CREATE ioctl
+ * %SGX_IOC_ENCLAVE_CREATE ioctl
* @src: address for the SECS page data
*/
struct sgx_enclave_create {
@@ -33,7 +33,7 @@ struct sgx_enclave_create {
/**
* struct sgx_enclave_add_page - parameter structure for the
- * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl
+ * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl
* @addr: address within the ELRANGE
* @src: address for the page data
* @secinfo: address for the SECINFO data
@@ -49,7 +49,7 @@ struct sgx_enclave_add_page {
/**
* struct sgx_enclave_init - parameter structure for the
- * %SGX_IOC_ENCLAVE_INIT ioctl
+ * %SGX_IOC_ENCLAVE_INIT ioctl
* @sigstruct: address for the SIGSTRUCT data
*/
struct sgx_enclave_init {
@@ -66,16 +66,16 @@ struct sgx_enclave_set_attribute {
};
/**
- * struct sgx_enclave_exception - structure to report exceptions encountered in
- * __vdso_sgx_enter_enclave()
+ * struct sgx_enclave_exinfo - structure to report exceptions encountered in
+ * __vdso_sgx_enter_enclave()
*
- * @leaf: ENCLU leaf from \%eax at time of exception
+ * @leaf: ENCLU leaf from \%eax at time of exception/exit
* @trapnr: exception trap number, a.k.a. fault vector
* @error_code: exception error code
* @address: exception address, e.g. CR2 on a #PF
* @reserved: reserved for future use
*/
-struct sgx_enclave_exception {
+struct sgx_enclave_exinfo {
__u32 leaf;
__u16 trapnr;
__u16 error_code;
--
2.17.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* [RFC PATCH v2 3/3] selftests/x86: Augment SGX selftest to test new __vdso_sgx_enter_enclave() and its callback interface
2019-04-23 0:37 ` [RFC PATCH v1 0/3] An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack Cedric Xing
` (2 preceding siblings ...)
2019-04-24 6:26 ` [RFC PATCH v2 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack Cedric Xing
@ 2019-04-24 6:26 ` Cedric Xing
2019-07-12 3:25 ` Jarkko Sakkinen
3 siblings, 1 reply; 285+ messages in thread
From: Cedric Xing @ 2019-04-24 6:26 UTC (permalink / raw)
To: linux-kernel, linux-sgx
Cc: cedric.xing, akpm, dave.hansen, sean.j.christopherson,
serge.ayoun, shay.katz-zamir, haitao.huang, kai.svahn, kai.huang,
jarkko.sakkinen
This patch augments SGX selftest with two new tests.
The first test exercises the newly added callback interface, by marking the
whole enclave range as PROT_READ, then calling mprotect() upon #PFs to add
necessary PTE permissions per PFEC (#PF Error Code) until the enclave finishes.
This test also serves as an example to demonstrate the callback interface.
The second test single-steps through __vdso_sgx_enter_enclave() to make sure
the call stack can be unwound at every instruction within that vDSO API. Its
purpose is to validate the hand-crafted CFI directives in the assembly.
Signed-off-by: Cedric Xing <cedric.xing@intel.com>
---
tools/testing/selftests/x86/sgx/Makefile | 6 +-
tools/testing/selftests/x86/sgx/main.c | 323 ++++++++++++++++++---
tools/testing/selftests/x86/sgx/sgx_call.S | 40 ++-
3 files changed, 322 insertions(+), 47 deletions(-)
diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile
index 3af15d7c8644..31f937e220c4 100644
--- a/tools/testing/selftests/x86/sgx/Makefile
+++ b/tools/testing/selftests/x86/sgx/Makefile
@@ -14,16 +14,16 @@ TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx
all_64: $(TEST_CUSTOM_PROGS)
$(TEST_CUSTOM_PROGS): main.c sgx_call.S $(OUTPUT)/encl_piggy.o
- $(CC) $(HOST_CFLAGS) -o $@ $^
+ $(CC) $(HOST_CFLAGS) -o $@ $^ -lunwind -ldl -Wl,--defsym,__image_base=0 -pie
$(OUTPUT)/encl_piggy.o: encl_piggy.S $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
$(CC) $(HOST_CFLAGS) -I$(OUTPUT) -c $< -o $@
$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf
- objcopy --remove-section=.got.plt -O binary $< $@
+ objcopy -O binary $< $@
$(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S
- $(CC) $(ENCL_CFLAGS) -T $^ -o $@
+ $(CC) $(ENCL_CFLAGS) -T $^ -o $@ -Wl,--build-id=none
$(OUTPUT)/encl.ss: $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin
$^ $@
diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c
index e2265f841fb0..d3e53c71306d 100644
--- a/tools/testing/selftests/x86/sgx/main.c
+++ b/tools/testing/selftests/x86/sgx/main.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright(c) 2016-18 Intel Corporation.
+#define _GNU_SOURCE
#include <elf.h>
#include <fcntl.h>
#include <stdbool.h>
@@ -9,16 +10,31 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
-#include <sys/time.h>
+#include <sys/auxv.h>
+#include <signal.h>
+#include <sys/ucontext.h>
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
#include "encl_piggy.h"
#include "defines.h"
#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h"
#include "../../../../../arch/x86/include/uapi/asm/sgx.h"
-static const uint64_t MAGIC = 0x1122334455667788ULL;
+#define _Q(x) __Q(x)
+#define __Q(x) #x
+#define ERRLN "Line " _Q(__LINE__)
+
+#define X86_EFLAGS_TF (1ul << 8)
+
+extern char __image_base[];
+size_t eenter;
+static size_t vdso_base;
struct vdso_symtab {
Elf64_Sym *elf_symtab;
@@ -26,20 +42,11 @@ struct vdso_symtab {
Elf64_Word *elf_hashtab;
};
-static void *vdso_get_base_addr(char *envp[])
+static void vdso_init(void)
{
- Elf64_auxv_t *auxv;
- int i;
-
- for (i = 0; envp[i]; i++);
- auxv = (Elf64_auxv_t *)&envp[i + 1];
-
- for (i = 0; auxv[i].a_type != AT_NULL; i++) {
- if (auxv[i].a_type == AT_SYSINFO_EHDR)
- return (void *)auxv[i].a_un.a_val;
- }
-
- return NULL;
+ vdso_base = getauxval(AT_SYSINFO_EHDR);
+ if (!vdso_base)
+ exit(1);
}
static Elf64_Dyn *vdso_get_dyntab(void *addr)
@@ -66,8 +73,9 @@ static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag)
return NULL;
}
-static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab)
+static bool vdso_get_symtab(struct vdso_symtab *symtab)
{
+ void *addr = (void *)vdso_base;
Elf64_Dyn *dyntab = vdso_get_dyntab(addr);
symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB);
@@ -138,7 +146,7 @@ static bool encl_create(int dev_fd, unsigned long bin_size,
base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED, dev_fd, 0);
if (base == MAP_FAILED) {
- perror("mmap");
+ perror(ERRLN);
return false;
}
@@ -224,35 +232,271 @@ static bool encl_load(struct sgx_secs *secs, unsigned long bin_size)
return false;
}
-void sgx_call(void *rdi, void *rsi, void *tcs,
- struct sgx_enclave_exception *exception,
- void *eenter);
+int sgx_call(void *rdi, void *rsi, long rdx, void *rcx, void *r8, void *r9,
+ void *tcs, struct sgx_enclave_exinfo *ei, void *cb);
+
+static void show_enclave_exinfo(const struct sgx_enclave_exinfo *exinfop,
+ const char *header)
+{
+ static const char * const enclu_leaves[] = {
+ "EREPORT",
+ "EGETKEY",
+ "EENTER",
+ "ERESUME",
+ "EEXIT"
+ };
+ static const char * const exception_names[] = {
+ "#DE",
+ "#DB",
+ "NMI",
+ "#BP",
+ "#OF",
+ "#BR",
+ "#UD",
+ "#NM",
+ "#DF",
+ "CSO",
+ "#TS",
+ "#NP",
+ "#SS",
+ "#GP",
+ "#PF",
+ "Unknown",
+ "#MF",
+ "#AC",
+ "#MC",
+ "#XM",
+ "#VE",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown"
+ };
+
+ printf("%s: leaf:%s(%d)", header,
+ enclu_leaves[exinfop->leaf], exinfop->leaf);
+ if (exinfop->leaf != 4)
+ printf(" trap:%s(%d) ec:%d addr:0x%llx\n",
+ exception_names[exinfop->trapnr], exinfop->trapnr,
+ exinfop->error_code, exinfop->address);
+ else
+ printf("\n");
+}
+
+static const uint64_t MAGIC = 0x1122334455667788ULL;
-int main(int argc, char *argv[], char *envp[])
+static void test1(struct sgx_secs *secs)
+{
+ uint64_t result = 0;
+ struct sgx_enclave_exinfo exinfo;
+
+ printf("[1] Entering the enclave without callback.\n");
+
+ printf("Input: 0x%lx\n Expect: Same as input\n", MAGIC);
+ sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL,
+ (void *)secs->base, &exinfo, NULL);
+ show_enclave_exinfo(&exinfo, " Exit");
+ if (result != MAGIC) {
+ fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
+ exit(1);
+ }
+ printf(" Output: 0x%lx\n", result);
+
+ printf("Input: Null TCS\n Expect: #PF at EENTER\n");
+ sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL,
+ NULL, &exinfo, NULL);
+ show_enclave_exinfo(&exinfo, " Exit");
+ if (exinfo.leaf != 2 /*EENTER*/ || exinfo.trapnr != 14 /*#PF*/)
+ exit(1);
+}
+
+static int test2_callback(long rdi, long rsi, long rdx,
+ struct sgx_enclave_exinfo *ei, long r8, long r9,
+ void *tcs, long ursp)
+{
+ show_enclave_exinfo(ei, " callback");
+
+ switch (ei->leaf) {
+ case 4:
+ return 0;
+ case 3:
+ case 2:
+ switch (ei->trapnr) {
+ case 1: /*#DB*/
+ break;
+ case 14:/*#PF*/
+ if ((ei->error_code & 1) == 0) {
+ fprintf(stderr, ERRLN
+ ": Unexpected #PF error code\n");
+ exit(1);
+ }
+ if (mprotect((void *)(ei->address & -0x1000), 0x1000,
+ ((ei->error_code & 2) ? PROT_WRITE : 0) |
+ ((ei->error_code & 0x10) ? PROT_EXEC : 0) |
+ PROT_READ)) {
+ perror(ERRLN);
+ exit(1);
+ }
+ break;
+ default:
+ fprintf(stderr, ERRLN ": Unexpected exception\n");
+ exit(1);
+ }
+ return ei->leaf == 2 ? -EAGAIN : ei->leaf;
+ }
+ return -EINVAL;
+}
+
+static void test2(struct sgx_secs *secs)
+{
+ uint64_t result = 0;
+ struct sgx_enclave_exinfo exinfo;
+
+ printf("[2] Entering the enclave with callback.\n");
+
+ printf("Input: 0x%lx\n Expect: Same as input\n", MAGIC);
+ sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL,
+ (void *)secs->base, &exinfo, test2_callback);
+ if (result != MAGIC) {
+ fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
+ exit(1);
+ }
+ printf(" Output: 0x%lx\n", result);
+
+ printf("Input: Read-only enclave (0x%lx-0x%lx)\n"
+ " Expect: #PFs to be fixed by callback\n",
+ secs->base, secs->base + (encl_bin_end - encl_bin) - 1);
+ if (mprotect((void *)secs->base, encl_bin_end - encl_bin, PROT_READ)) {
+ perror(ERRLN);
+ exit(1);
+ }
+ while (sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL,
+ (void *)secs->base, &exinfo, test2_callback) == -EAGAIN)
+ ;
+ show_enclave_exinfo(&exinfo, " Exit");
+ if (exinfo.leaf != 4 /*EEXIT*/)
+ exit(1);
+}
+
+static struct test3_proc_context {
+ unw_word_t ip, bx, sp, bp, r12, r13, r14, r15;
+} test3_ctx;
+
+static unw_word_t test3_getcontext(unw_cursor_t *cursor,
+ struct test3_proc_context *ctxp)
+{
+ unw_get_reg(cursor, UNW_REG_IP, &ctxp->ip);
+ unw_get_reg(cursor, UNW_REG_SP, &ctxp->sp);
+ unw_get_reg(cursor, UNW_X86_64_RBX, &ctxp->bx);
+ unw_get_reg(cursor, UNW_X86_64_RBP, &ctxp->bp);
+ unw_get_reg(cursor, UNW_X86_64_R12, &ctxp->r12);
+ unw_get_reg(cursor, UNW_X86_64_R13, &ctxp->r13);
+ unw_get_reg(cursor, UNW_X86_64_R14, &ctxp->r14);
+ unw_get_reg(cursor, UNW_X86_64_R15, &ctxp->r15);
+ return ctxp->ip;
+}
+
+static void test3_sigtrap(int sig, siginfo_t *info, ucontext_t *ctxp)
+{
+ static int in_vdso_eenter;
+ static size_t caller;
+
+ unw_cursor_t cursor;
+ unw_context_t uc;
+ struct test3_proc_context pc;
+
+ if (ctxp->uc_mcontext.gregs[REG_RIP] == eenter) {
+ in_vdso_eenter = 1;
+ caller = *(size_t *)(ctxp->uc_mcontext.gregs[REG_RSP]);
+ printf(" trace started at ip:%llx (vdso:0x%llx)\n",
+ ctxp->uc_mcontext.gregs[REG_RIP],
+ ctxp->uc_mcontext.gregs[REG_RIP] - vdso_base);
+ }
+
+ if (!in_vdso_eenter)
+ return;
+
+ if (ctxp->uc_mcontext.gregs[REG_RIP] == caller) {
+ in_vdso_eenter = 0;
+ ctxp->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
+ printf(" trace ended successfully at ip:%llx (executable:0x%llx)\n",
+ ctxp->uc_mcontext.gregs[REG_RIP],
+ ctxp->uc_mcontext.gregs[REG_RIP] -
+ (size_t)__image_base);
+ return;
+ }
+
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+ while (unw_step(&cursor) > 0 &&
+ test3_getcontext(&cursor, &pc) != test3_ctx.ip)
+ ;
+
+ if (memcmp(&pc, &test3_ctx, sizeof(pc))) {
+ fprintf(stderr, ERRLN ": Error unwinding\n");
+ exit(1);
+ }
+}
+
+static void test3_set_tf(void)
+{
+ __asm__ ("pushfq; orl %0, (%%rsp); popfq" : : "i"(X86_EFLAGS_TF));
+}
+
+static void test3(struct sgx_secs *secs)
+{
+ unw_cursor_t cursor;
+ unw_context_t uc;
+ struct sigaction sa = {
+ .sa_sigaction = (void (*)(int, siginfo_t*, void*))test3_sigtrap,
+ .sa_flags = SA_SIGINFO,
+ };
+
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+ if (unw_step(&cursor) > 0)
+ test3_getcontext(&cursor, &test3_ctx);
+ else {
+ fprintf(stderr, ERRLN ": error initializing unwind context\n");
+ exit(1);
+ }
+
+ if (sigaction(SIGTRAP, &sa, NULL) < 0) {
+ perror(ERRLN);
+ exit(1);
+ }
+
+ test3_set_tf();
+ test1(secs);
+
+ test3_set_tf();
+ test2(secs);
+}
+
+int main(void)
{
unsigned long bin_size = encl_bin_end - encl_bin;
unsigned long ss_size = encl_ss_end - encl_ss;
- struct sgx_enclave_exception exception;
Elf64_Sym *eenter_sym;
struct vdso_symtab symtab;
struct sgx_secs secs;
- uint64_t result = 0;
- void *eenter;
- void *addr;
-
- memset(&exception, 0, sizeof(exception));
- addr = vdso_get_base_addr(envp);
- if (!addr)
- exit(1);
+ vdso_init();
- if (!vdso_get_symtab(addr, &symtab))
+ if (!vdso_get_symtab(&symtab))
exit(1);
eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave");
if (!eenter_sym)
exit(1);
- eenter = addr + eenter_sym->st_value;
+ eenter = vdso_base + eenter_sym->st_value;
printf("Binary size %lu (0x%lx), SIGSTRUCT size %lu\n", bin_size,
bin_size, ss_size);
@@ -266,14 +510,11 @@ int main(int argc, char *argv[], char *envp[])
if (!encl_load(&secs, bin_size))
exit(1);
- printf("Input: 0x%lx\n", MAGIC);
- sgx_call((void *)&MAGIC, &result, (void *)secs.base, &exception,
- eenter);
- if (result != MAGIC) {
- fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
- exit(1);
- }
+ printf("--- Functional Tests ---\n");
+ test1(&secs);
+ test2(&secs);
- printf("Output: 0x%lx\n", result);
- exit(0);
+ printf("--- Unwind Tests ---\n");
+ test3(&secs);
+ return 0;
}
diff --git a/tools/testing/selftests/x86/sgx/sgx_call.S b/tools/testing/selftests/x86/sgx/sgx_call.S
index 14bd0a044199..ca2c0c947758 100644
--- a/tools/testing/selftests/x86/sgx/sgx_call.S
+++ b/tools/testing/selftests/x86/sgx/sgx_call.S
@@ -7,9 +7,43 @@
.global sgx_call
sgx_call:
+ .cfi_startproc
+ push %r15
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r15, 0
+ push %r14
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r14, 0
+ push %r13
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r13, 0
+ push %r12
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r12, 0
push %rbx
- mov $0x02, %rax
- mov %rdx, %rbx
- call *%r8
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rbx, 0
+ push $0
+ .cfi_adjust_cfa_offset 8
+ push 0x48(%rsp)
+ .cfi_adjust_cfa_offset 8
+ push 0x48(%rsp)
+ .cfi_adjust_cfa_offset 8
+ push 0x48(%rsp)
+ .cfi_adjust_cfa_offset 8
+ mov $2, %eax
+ call *eenter(%rip)
+ add $0x20, %rsp
+ .cfi_adjust_cfa_offset -0x20
pop %rbx
+ .cfi_adjust_cfa_offset -8
+ pop %r12
+ .cfi_adjust_cfa_offset -8
+ pop %r13
+ .cfi_adjust_cfa_offset -8
+ pop %r14
+ .cfi_adjust_cfa_offset -8
+ pop %r15
+ .cfi_adjust_cfa_offset -8
ret
+ .cfi_endproc
--
2.17.1
^ permalink raw reply related [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-23 16:52 ` Andy Lutomirski
@ 2019-04-24 12:17 ` Jarkko Sakkinen
2019-05-08 13:45 ` Jarkko Sakkinen
0 siblings, 1 reply; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-04-24 12:17 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jarkko Sakkinen, LKML, X86 ML, linux-sgx, Andrew Morton,
Dave Hansen, Christopherson, Sean J, nhorman, npmccallum, Ayoun,
Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
Huang, Kai, David Rientjes, linux-sgx-owner
On 2019-04-23 19:52, Andy Lutomirski wrote:
> On Tue, Apr 23, 2019 at 4:56 AM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
>>
>> On Wed, Apr 17, 2019 at 01:39:10PM +0300, Jarkko Sakkinen wrote:
>> > Intel(R) SGX is a set of CPU instructions that can be used by applications
>> > to set aside private regions of code and data. The code outside the enclave
>> > is disallowed to access the memory inside the enclave by the CPU access
>> > control. In a way you can think that SGX provides inverted sandbox. It
>> > protects the application from a malicious host.
>> >
>> > There is a new hardware unit in the processor called Memory Encryption
>> > Engine (MEE) starting from the Skylake microacrhitecture. BIOS can define
>> > one or many MEE regions that can hold enclave data by configuring them with
>> > PRMRR registers.
>> >
>> > The MEE automatically encrypts the data leaving the processor package to
>> > the MEE regions. The data is encrypted using a random key whose life-time
>> > is exactly one power cycle.
>> >
>> > The current implementation requires that the firmware sets
>> > IA32_SGXLEPUBKEYHASH* MSRs as writable so that ultimately the kernel can
>> > decide what enclaves it wants run. The implementation does not create
>> > any bottlenecks to support read-only MSRs later on.
>> >
>> > You can tell if your CPU supports SGX by looking into /proc/cpuinfo:
>> >
>> > cat /proc/cpuinfo | grep sgx
>> >
>> > v20:
>> > * Fine-tune Kconfig messages and spacing and remove MMU_NOTIFIER
>> > dependency as MMU notifiers are no longer used in the driver.
>> > * Use mm_users instead of mm_count as refcount for mm_struct as mm_count
>> > only protects from deleting mm_struct, not removing its contents.
>> > * Sanitize EPC when the reclaimer thread starts by doing EREMOVE for all
>> > of them. They could be in initialized state when the kernel starts
>> > because it might be spawned by kexec().
>> > * Documentation overhaul.
>> > * Use a device /dev/sgx/provision for delivering the provision token
>> > instead of securityfs.
>> > * Create a reference to the enclave when already when opening
>> > /dev/sgx/enclave. The file is then associated with this enclave only.
>> > mmap() can be done at free at any point and always get a reference to
>> > the enclave. To summarize the file now represents the enclave.
>> >
>> > v19:
>> > * Took 3-4 months but in some sense this was more like a rewrite of most
>> > of the corners of the source code. If I've forgotten to deal with some
>> > feedback, please don't shout me. Make a remark and I will fix it for
>> > the next version. Hopefully there won't be this big turnovers anymore.
>> > * Validate SECS attributes properly against CPUID given attributes and
>> > against allowed attributes. SECS attributes are the ones that are
>> > enforced whereas SIGSTRUCT attributes tell what is required to run
>> > the enclave.
>> > * Add KSS (Key Sharing Support) to the enclave attributes.
>> > * Deny MAP_PRIVATE as an enclave is always a shared memory entity.
>> > * Revert back to shmem backing storage so that it can be easily shared
>> > by multiple processes.
>> > * Split the recognization of an ENCLS leaf failure by using three
>> > functions to detect it: encsl_faulted(), encls_returned_code() and
>> > sgx_failed(). encls_failed() is only caused by a spurious expections that
>> > should never happen. Thus, it is not defined as an inline function in
>> > order to easily insert a kprobe to it.
>> > * Move low-level enclave management routines, page fault handler and page
>> > reclaiming routines from driver to the core. These cannot be separated
>> > from each other as they are heavily interdependent. The rationale is that
>> > the core does not call any code from the driver.
>> > * Allow the driver to be compiled as a module now that it no code is using
>> > its routines and it only uses exported symbols. Now the driver is
>> > essentially just a thin ioctl layer.
>> > * Reworked the driver to maintain a list of mm_struct's. The VMA callbacks
>> > add new entries to this list as the process is forked. Each entry has
>> > its own refcount because they have a different life-cycle as the enclave
>> > does. In effect @tgid and @mm have been removed from struct sgx_encl
>> > and we allow forking by removing VM_DONTCOPY from vm flags.
>> > * Generate a cpu mask in the reclaimer from the cpu mask's of all
>> > mm_struct's. This will kick out the hardware threads out of the enclave
>> > from multiple processes. It is not a local variable because it would
>> > eat too much of the stack space but instead a field in struct
>> > sgx_encl.
>> > * Allow forking i.e. remove VM_DONTCOPY. I did not change the API
>> > because the old API scaled to the workload that Andy described. The
>> > codebase is now mostly API independent i.e. changing the API is a
>> > small task. For me the proper trigger to chanage it is a as concrete
>> > as possible workload that cannot be fulfilled. I hope you understand
>> > my thinking here. I don't want to change anything w/o proper basis
>> > but I'm ready to change anything if there is a proper basis. I do
>> > not have any kind of attachment to any particular type of API.
>> > * Add Sean's vDSO ENCLS(EENTER) patches and update selftest to use the
>> > new vDSO.
>> >
>> > v18:
>> > * Update the ioctl-number.txt.
>> > * Move the driver under arch/x86.
>> > * Add SGX features (SGX, SGX1, SGX2) to the disabled-features.h.
>> > * Rename the selftest as test_sgx (previously sgx-selftest).
>> > * In order to enable process accounting, swap EPC pages and PCMD's to a VMA
>> > instead of shmem.
>> > * Allow only to initialize and run enclaves with a subset of
>> > {DEBUG, MODE64BIT} set.
>> > * Add SGX_IOC_ENCLAVE_SET_ATTRIBUTE to allow an enclave to have privileged
>> > attributes e.g. PROVISIONKEY.
>> >
>> > v17:
>> > * Add a simple selftest.
>> > * Fix a null pointer dereference to section->pages when its
>> > allocation fails.
>> > * Add Sean's description of the exception handling to the documentation.
>> >
>> > v16:
>> > * Fixed SOB's in the commits that were a bit corrupted in v15.
>> > * Implemented exceptio handling properly to detect_sgx().
>> > * Use GENMASK() to define SGX_CPUID_SUB_LEAF_TYPE_MASK.
>> > * Updated the documentation to use rst definition lists.
>> > * Added the missing Documentation/x86/index.rst, which has a link to
>> > intel_sgx.rst. Now the SGX and uapi documentation is properly generated
>> > with 'make htmldocs'.
>> > * While enumerating EPC sections, if an undefined section is found, fail
>> > the driver initialization instead of continuing the initialization.
>> > * Issue a warning if there are more than %SGX_MAX_EPC_SECTIONS.
>> > * Remove copyright notice from arch/x86/include/asm/sgx.h.
>> > * Migrated from ioremap_cache() to memremap().
>> >
>> > v15:
>> > * Split into more digestable size patches.
>> > * Lots of small fixes and clean ups.
>> > * Signal a "plain" SIGSEGV on an EPCM violation.
>> >
>> > v14:
>> > * Change the comment about X86_FEATURE_SGX_LC from “SGX launch
>> > configuration” to “SGX launch control”.
>> > * Move the SGX-related CPU feature flags as part of the Linux defined
>> > virtual leaf 8.
>> > * Add SGX_ prefix to the constants defining the ENCLS leaf functions.
>> > * Use GENMASK*() and BIT*() in sgx_arch.h instead of raw hex numbers.
>> > * Refine the long description for CONFIG_INTEL_SGX_CORE.
>> > * Do not use pr_*_ratelimited() in the driver. The use of the rate limited
>> > versions is legacy cruft from the prototyping phase.
>> > * Detect sleep with SGX_INVALID_EINIT_TOKEN instead of counting power
>> > cycles.
>> > * Manually prefix with “sgx:” in the core SGX code instead of redefining
>> > pr_fmt.
>> > * Report if IA32_SGXLEPUBKEYHASHx MSRs are not writable in the driver
>> > instead of core because it is a driver requirement.
>> > * Change prompt to bool in the entry for CONFIG_INTEL_SGX_CORE because the
>> > default is ‘n’.
>> > * Rename struct sgx_epc_bank as struct sgx_epc_section in order to match
>> > the SDM.
>> > * Allocate struct sgx_epc_page instances one at a time.
>> > * Use “__iomem void *” pointers for the mapped EPC memory consistently.
>> > * Retry once on SGX_INVALID_TOKEN in sgx_einit() instead of counting power
>> > cycles.
>> > * Call enclave swapping operations directly from the driver instead of
>> > calling them .indirectly through struct sgx_epc_page_ops because indirect
>> > calls are not required yet as the patch set does not contain the KVM
>> > support.
>> > * Added special signal SEGV_SGXERR to notify about SGX EPCM violation
>> > errors.
>> >
>> > v13:
>> > * Always use SGX_CPUID constant instead of a hardcoded value.
>> > * Simplified and documented the macros and functions for ENCLS leaves.
>> > * Enable sgx_free_page() to free active enclave pages on demand
>> > in order to allow sgx_invalidate() to delete enclave pages.
>> > It no longer performs EREMOVE if a page is in the process of
>> > being reclaimed.
>> > * Use PM notifier per enclave so that we don't have to traverse
>> > the global list of active EPC pages to find enclaves.
>> > * Removed unused SGX_LE_ROLLBACK constant from uapi/asm/sgx.h
>> > * Always use ioremap() to map EPC banks as we only support 64-bit kernel.
>> > * Invalidate IA32_SGXLEPUBKEYHASH cache used by sgx_einit() when going
>> > to sleep.
>> >
>> > v12:
>> > * Split to more narrow scoped commits in order to ease the review process and
>> > use co-developed-by tag for co-authors of commits instead of listing them in
>> > the source files.
>> > * Removed cruft EXPORT_SYMBOL() declarations and converted to static variables.
>> > * Removed in-kernel LE i.e. this version of the SGX software stack only
>> > supports unlocked IA32_SGXLEPUBKEYHASHx MSRs.
>> > * Refined documentation on launching enclaves, swapping and enclave
>> > construction.
>> > * Refined sgx_arch.h to include alignment information for every struct that
>> > requires it and removed structs that are not needed without an LE.
>> > * Got rid of SGX_CPUID.
>> > * SGX detection now prints log messages about firmware configuration issues.
>> >
>> > v11:
>> > * Polished ENCLS wrappers with refined exception handling.
>> > * ksgxswapd was not stopped (regression in v5) in
>> > sgx_page_cache_teardown(), which causes a leaked kthread after driver
>> > deinitialization.
>> > * Shutdown sgx_le_proxy when going to suspend because its EPC pages will be
>> > invalidated when resuming, which will cause it not function properly
>> > anymore.
>> > * Set EINITTOKEN.VALID to zero for a token that is passed when
>> > SGXLEPUBKEYHASH matches MRSIGNER as alloc_page() does not give a zero
>> > page.
>> > * Fixed the check in sgx_edbgrd() for a TCS page. Allowed to read offsets
>> > around the flags field, which causes a #GP. Only flags read is readable.
>> > * On read access memcpy() call inside sgx_vma_access() had src and dest
>> > parameters in wrong order.
>> > * The build issue with CONFIG_KASAN is now fixed. Added undefined symbols
>> > to LE even if “KASAN_SANITIZE := false” was set in the makefile.
>> > * Fixed a regression in the #PF handler. If a page has
>> > SGX_ENCL_PAGE_RESERVED flag the #PF handler should unconditionally fail.
>> > It did not, which caused weird races when trying to change other parts of
>> > swapping code.
>> > * EPC management has been refactored to a flat LRU cache and moved to
>> > arch/x86. The swapper thread reads a cluster of EPC pages and swaps all
>> > of them. It can now swap from multiple enclaves in the same round.
>> > * For the sake of consistency with SGX_IOC_ENCLAVE_ADD_PAGE, return -EINVAL
>> > when an enclave is already initialized or dead instead of zero.
>> >
>> > v10:
>> > * Cleaned up anon inode based IPC between the ring-0 and ring-3 parts
>> > of the driver.
>> > * Unset the reserved flag from an enclave page if EDBGRD/WR fails
>> > (regression in v6).
>> > * Close the anon inode when LE is stopped (regression in v9).
>> > * Update the documentation with a more detailed description of SGX.
>> >
>> > v9:
>> > * Replaced kernel-LE IPC based on pipes with an anonymous inode.
>> > The driver does not require anymore new exports.
>> >
>> > v8:
>> > * Check that public key MSRs match the LE public key hash in the
>> > driver initialization when the MSRs are read-only.
>> > * Fix the race in VA slot allocation by checking the fullness
>> > immediately after succeesful allocation.
>> > * Fix the race in hash mrsigner calculation between the launch
>> > enclave and user enclaves by having a separate lock for hash
>> > calculation.
>> >
>> > v7:
>> > * Fixed offset calculation in sgx_edbgr/wr(). Address was masked with PAGE_MASK
>> > when it should have been masked with ~PAGE_MASK.
>> > * Fixed a memory leak in sgx_ioc_enclave_create().
>> > * Simplified swapping code by using a pointer array for a cluster
>> > instead of a linked list.
>> > * Squeezed struct sgx_encl_page to 32 bytes.
>> > * Fixed deferencing of an RSA key on OpenSSL 1.1.0.
>> > * Modified TC's CMAC to use kernel AES-NI. Restructured the code
>> > a bit in order to better align with kernel conventions.
>> >
>> > v6:
>> > * Fixed semaphore underrun when accessing /dev/sgx from the launch enclave.
>> > * In sgx_encl_create() s/IS_ERR(secs)/IS_ERR(encl)/.
>> > * Removed virtualization chapter from the documentation.
>> > * Changed the default filename for the signing key as signing_key.pem.
>> > * Reworked EPC management in a way that instead of a linked list of
>> > struct sgx_epc_page instances there is an array of integers that
>> > encodes address and bank of an EPC page (the same data as 'pa' field
>> > earlier). The locking has been moved to the EPC bank level instead
>> > of a global lock.
>> > * Relaxed locking requirements for EPC management. EPC pages can be
>> > released back to the EPC bank concurrently.
>> > * Cleaned up ptrace() code.
>> > * Refined commit messages for new architectural constants.
>> > * Sorted includes in every source file.
>> > * Sorted local variable declarations according to the line length in
>> > every function.
>> > * Style fixes based on Darren's comments to sgx_le.c.
>> >
>> > v5:
>> > * Described IPC between the Launch Enclave and kernel in the commit messages.
>> > * Fixed all relevant checkpatch.pl issues that I have forgot fix in earlier
>> > versions except those that exist in the imported TinyCrypt code.
>> > * Fixed spelling mistakes in the documentation.
>> > * Forgot to check the return value of sgx_drv_subsys_init().
>> > * Encapsulated properly page cache init and teardown.
>> > * Collect epc pages to a temp list in sgx_add_epc_bank
>> > * Removed SGX_ENCLAVE_INIT_ARCH constant.
>> >
>> > v4:
>> > * Tied life-cycle of the sgx_le_proxy process to /dev/sgx.
>> > * Removed __exit annotation from sgx_drv_subsys_exit().
>> > * Fixed a leak of a backing page in sgx_process_add_page_req() in the
>> > case when vm_insert_pfn() fails.
>> > * Removed unused symbol exports for sgx_page_cache.c.
>> > * Updated sgx_alloc_page() to require encl parameter and documented the
>> > behavior (Sean Christopherson).
>> > * Refactored a more lean API for sgx_encl_find() and documented the behavior.
>> > * Moved #PF handler to sgx_fault.c.
>> > * Replaced subsys_system_register() with plain bus_register().
>> > * Retry EINIT 2nd time only if MSRs are not locked.
>> >
>> > v3:
>> > * Check that FEATURE_CONTROL_LOCKED and FEATURE_CONTROL_SGX_ENABLE are set.
>> > * Return -ERESTARTSYS in __sgx_encl_add_page() when sgx_alloc_page() fails.
>> > * Use unused bits in epc_page->pa to store the bank number.
>> > * Removed #ifdef for WQ_NONREENTRANT.
>> > * If mmu_notifier_register() fails with -EINTR, return -ERESTARTSYS.
>> > * Added --remove-section=.got.plt to objcopy flags in order to prevent a
>> > dummy .got.plt, which will cause an inconsistent size for the LE.
>> > * Documented sgx_encl_* functions.
>> > * Added remark about AES implementation used inside the LE.
>> > * Removed redundant sgx_sys_exit() from le/main.c.
>> > * Fixed struct sgx_secinfo alignment from 128 to 64 bytes.
>> > * Validate miscselect in sgx_encl_create().
>> > * Fixed SSA frame size calculation to take the misc region into account.
>> > * Implemented consistent exception handling to __encls() and __encls_ret().
>> > * Implemented a proper device model in order to allow sysfs attributes
>> > and in-kernel API.
>> > * Cleaned up various "find enclave" implementations to the unified
>> > sgx_encl_find().
>> > * Validate that vm_pgoff is zero.
>> > * Discard backing pages with shmem_truncate_range() after EADD.
>> > * Added missing EEXTEND operations to LE signing and launch.
>> > * Fixed SSA size for GPRS region from 168 to 184 bytes.
>> > * Fixed the checks for TCS flags. Now DBGOPTIN is allowed.
>> > * Check that TCS addresses are in ELRANGE and not just page aligned.
>> > * Require kernel to be compiled with X64_64 and CPU_SUP_INTEL.
>> > * Fixed an incorrect value for SGX_ATTR_DEBUG from 0x01 to 0x02.
>> >
>> > v2:
>> > * get_rand_uint32() changed the value of the pointer instead of value
>> > where it is pointing at.
>> > * Launch enclave incorrectly used sigstruct attributes-field instead of
>> > enclave attributes-field.
>> > * Removed unused struct sgx_add_page_req from sgx_ioctl.c
>> > * Removed unused sgx_has_sgx2.
>> > * Updated arch/x86/include/asm/sgx.h so that it provides stub
>> > implementations when sgx in not enabled.
>> > * Removed cruft rdmsr-calls from sgx_set_pubkeyhash_msrs().
>> > * return -ENOMEM in sgx_alloc_page() when VA pages consume too much space
>> > * removed unused global sgx_nr_pids
>> > * moved sgx_encl_release to sgx_encl.c
>> > * return -ERESTARTSYS instead of -EINTR in sgx_encl_init()
>> >
>> > Jarkko Sakkinen (11):
>> > x86/sgx: Add ENCLS architectural error codes
>> > x86/sgx: Add SGX1 and SGX2 architectural data structures
>> > x86/sgx: Add wrappers for ENCLS leaf functions
>> > x86/sgx: Add functions to allocate and free EPC pages
>> > x86/sgx: Add the Linux SGX Enclave Driver
>> > x86/sgx: Add provisioning
>> > x86/sgx: Add swapping code to the core and SGX driver
>> > x86/sgx: ptrace() support for the SGX driver
>> > selftests/x86: Add a selftest for SGX
>> > x86/sgx: Update MAINTAINERS
>> > docs: x86/sgx: Document the enclave API
>> >
>> > Kai Huang (2):
>> > x86/cpufeatures: Add Intel-defined SGX feature bit
>> > x86/cpufeatures: Add Intel-defined SGX_LC feature bit
>> >
>> > Sean Christopherson (15):
>> > x86/cpufeatures: Add SGX sub-features (as Linux-defined bits)
>> > x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition
>> > x86/msr: Add SGX Launch Control MSR definitions
>> > x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit
>> > x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
>> > x86/cpu/intel: Detect SGX support and update caps appropriately
>> > x86/sgx: Enumerate and track EPC sections
>> > x86/sgx: Add sgx_einit() for initializing enclaves
>> > x86/vdso: Add support for exception fixup in vDSO functions
>> > x86/fault: Add helper function to sanitize error code
>> > x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling
>> > x86/traps: Attempt to fixup exceptions in vDSO before signaling
>> > x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave
>> > transitions
>> > docs: x86/sgx: Add Architecture documentation
>> > docs: x86/sgx: Document kernel internals
>> >
>> > Documentation/index.rst | 1 +
>> > Documentation/ioctl/ioctl-number.txt | 1 +
>> > Documentation/x86/index.rst | 10 +
>> > Documentation/x86/sgx/1.Architecture.rst | 431 +++++++++
>> > Documentation/x86/sgx/2.Kernel-internals.rst | 56 ++
>> > Documentation/x86/sgx/3.API.rst | 27 +
>> > Documentation/x86/sgx/index.rst | 18 +
>> > MAINTAINERS | 12 +
>> > arch/x86/Kconfig | 27 +
>> > arch/x86/entry/vdso/Makefile | 6 +-
>> > arch/x86/entry/vdso/extable.c | 37 +
>> > arch/x86/entry/vdso/extable.h | 29 +
>> > arch/x86/entry/vdso/vdso-layout.lds.S | 9 +-
>> > arch/x86/entry/vdso/vdso.lds.S | 1 +
>> > arch/x86/entry/vdso/vdso2c.h | 58 +-
>> > arch/x86/entry/vdso/vsgx_enter_enclave.S | 101 +++
>> > arch/x86/include/asm/cpufeatures.h | 24 +-
>> > arch/x86/include/asm/disabled-features.h | 14 +-
>> > arch/x86/include/asm/msr-index.h | 8 +
>> > arch/x86/include/asm/traps.h | 1 +
>> > arch/x86/include/asm/vdso.h | 5 +
>> > arch/x86/include/uapi/asm/sgx.h | 86 ++
>> > arch/x86/include/uapi/asm/sgx_errno.h | 91 ++
>> > arch/x86/kernel/cpu/Makefile | 1 +
>> > arch/x86/kernel/cpu/intel.c | 39 +
>> > arch/x86/kernel/cpu/scattered.c | 2 +
>> > arch/x86/kernel/cpu/sgx/Makefile | 2 +
>> > arch/x86/kernel/cpu/sgx/arch.h | 424 +++++++++
>> > arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
>> > arch/x86/kernel/cpu/sgx/driver/driver.h | 38 +
>> > arch/x86/kernel/cpu/sgx/driver/ioctl.c | 850 ++++++++++++++++++
>> > arch/x86/kernel/cpu/sgx/driver/main.c | 368 ++++++++
>> > arch/x86/kernel/cpu/sgx/encl.c | 709 +++++++++++++++
>> > arch/x86/kernel/cpu/sgx/encl.h | 136 +++
>> > arch/x86/kernel/cpu/sgx/encls.c | 22 +
>> > arch/x86/kernel/cpu/sgx/encls.h | 244 +++++
>> > arch/x86/kernel/cpu/sgx/main.c | 360 ++++++++
>> > arch/x86/kernel/cpu/sgx/reclaim.c | 482 ++++++++++
>> > arch/x86/kernel/cpu/sgx/sgx.h | 90 ++
>> > arch/x86/kernel/traps.c | 14 +
>> > arch/x86/mm/fault.c | 44 +-
>> > tools/arch/x86/include/asm/cpufeatures.h | 21 +-
>> > tools/testing/selftests/x86/Makefile | 10 +
>> > tools/testing/selftests/x86/sgx/Makefile | 48 +
>> > tools/testing/selftests/x86/sgx/defines.h | 39 +
>> > tools/testing/selftests/x86/sgx/encl.c | 20 +
>> > tools/testing/selftests/x86/sgx/encl.lds | 33 +
>> > .../selftests/x86/sgx/encl_bootstrap.S | 94 ++
>> > tools/testing/selftests/x86/sgx/encl_piggy.S | 18 +
>> > tools/testing/selftests/x86/sgx/encl_piggy.h | 14 +
>> > tools/testing/selftests/x86/sgx/main.c | 279 ++++++
>> > tools/testing/selftests/x86/sgx/sgx_call.S | 15 +
>> > tools/testing/selftests/x86/sgx/sgxsign.c | 508 +++++++++++
>> > .../testing/selftests/x86/sgx/signing_key.pem | 39 +
>> > 54 files changed, 5987 insertions(+), 32 deletions(-)
>> > create mode 100644 Documentation/x86/index.rst
>> > create mode 100644 Documentation/x86/sgx/1.Architecture.rst
>> > create mode 100644 Documentation/x86/sgx/2.Kernel-internals.rst
>> > create mode 100644 Documentation/x86/sgx/3.API.rst
>> > create mode 100644 Documentation/x86/sgx/index.rst
>> > create mode 100644 arch/x86/entry/vdso/extable.c
>> > create mode 100644 arch/x86/entry/vdso/extable.h
>> > create mode 100644 arch/x86/entry/vdso/vsgx_enter_enclave.S
>> > create mode 100644 arch/x86/include/uapi/asm/sgx.h
>> > create mode 100644 arch/x86/include/uapi/asm/sgx_errno.h
>> > create mode 100644 arch/x86/kernel/cpu/sgx/Makefile
>> > create mode 100644 arch/x86/kernel/cpu/sgx/arch.h
>> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
>> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
>> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
>> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
>> > create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
>> > create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
>> > create mode 100644 arch/x86/kernel/cpu/sgx/encls.c
>> > create mode 100644 arch/x86/kernel/cpu/sgx/encls.h
>> > create mode 100644 arch/x86/kernel/cpu/sgx/main.c
>> > create mode 100644 arch/x86/kernel/cpu/sgx/reclaim.c
>> > create mode 100644 arch/x86/kernel/cpu/sgx/sgx.h
>> > create mode 100644 tools/testing/selftests/x86/sgx/Makefile
>> > create mode 100644 tools/testing/selftests/x86/sgx/defines.h
>> > create mode 100644 tools/testing/selftests/x86/sgx/encl.c
>> > create mode 100644 tools/testing/selftests/x86/sgx/encl.lds
>> > create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S
>> > create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.S
>> > create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h
>> > create mode 100644 tools/testing/selftests/x86/sgx/main.c
>> > create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
>> > create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c
>> > create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem
>> >
>> > --
>> > 2.19.1
>> >
>>
>> I'm on leave for this week and next week's Monday if you wonder why
>> I'm
>> so passive in the discussion. Looking at the things next week's Tue.
>>
>> Just a quick comment about Andy's proposal. Probably pretty DSO like
>> ELF blob could work with an addition of a section called ".tcs" for
>> entry points. They need to be recognized so that the loader can add
>> them as TCS pages.
>
> Hmm.
>
> There's a decent argument for specifically supporting whatever format
> Windows uses. There's also an argument for allowing one or more
> enclaves to be bundled in a regular ELF DSO. FWIW, there's no
> fundamental reason we can't support more than one type of enclave
> format.
It would also be better to contain sigstruct so that EINIT can
be performed. I wonder if we could use dlopen() as call path
for this? That would also provide all the DAC/MAC/whatever
security checks.
I can start to PoC this next week once I get back to work (back
on next Tue). I haven't studied Windows format much but I'd
*guess* it is a COFF DLL?
For me easier path to get something done would to do ELF DSO
first. As you said they both could be done, which means that
Windows COFF could be upstreamed later on.
If this approach works, it'd mean that no ioctl's would be
required except SGX_ENCLAVE_SET_ATTRIBUTE.
PS. This quote from LWN got a bit into my feelings:
"After 20 revisions of the patch set over three years, the
authors of this work (which was posted by Jarkko Sakkinen)
might well be forgiven for thinking that it must be about
ready for merging."
I seriously do not make any pre-conclusions ever for any patch
that I post when it should be merged, no matter how big or
small :-) For me this is just work...
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* RE: [RFC PATCH v1 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-23 1:25 ` Andy Lutomirski
@ 2019-04-24 17:56 ` Xing, Cedric
0 siblings, 0 replies; 285+ messages in thread
From: Xing, Cedric @ 2019-04-24 17:56 UTC (permalink / raw)
To: Andy Lutomirski
Cc: LKML, X86 ML, linux-sgx, Andrew Morton, Hansen, Dave,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
Huang, Kai, David Rientjes, Jarkko Sakkinen
Hi Andy,
I sent out my RFC patch v2 last night, that has your suggestions incorporated, plus a new unwind test to single step through the vDSO API to test out the CFI directives. Hopefully it is able to address all of your concerns. It's worth noting that, given the current patch fixes up #DB and #BP at ENCLU, the unwind test cannot run to completion. I assume Sean will revise the fixup code soon.
Thanks!
-Cedric
> -----Original Message-----
> From: Andy Lutomirski [mailto:luto@kernel.org]
> Sent: Monday, April 22, 2019 6:26 PM
> To: Xing, Cedric <cedric.xing@intel.com>
> Cc: LKML <linux-kernel@vger.kernel.org>; X86 ML <x86@kernel.org>; linux-
> sgx@vger.kernel.org; Andrew Morton <akpm@linux-foundation.org>; Hansen,
> Dave <dave.hansen@intel.com>; Christopherson, Sean J
> <sean.j.christopherson@intel.com>; nhorman@redhat.com;
> npmccallum@redhat.com; Ayoun, Serge <serge.ayoun@intel.com>; Katz-zamir,
> Shay <shay.katz-zamir@intel.com>; Huang, Haitao <haitao.huang@intel.com>;
> Andy Shevchenko <andriy.shevchenko@linux.intel.com>; Thomas Gleixner
> <tglx@linutronix.de>; Svahn, Kai <kai.svahn@intel.com>; Borislav Petkov
> <bp@alien8.de>; Josh Triplett <josh@joshtriplett.org>; Andrew Lutomirski
> <luto@kernel.org>; Huang, Kai <kai.huang@intel.com>; David Rientjes
> <rientjes@google.com>; Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Subject: Re: [RFC PATCH v1 2/3] x86/vdso: Modify
> __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
>
> On Mon, Apr 22, 2019 at 5:37 PM Cedric Xing <cedric.xing@intel.com>
> wrote:
> >
> > The previous __vdso_sgx_enter_enclave() requires enclaves to preserve
> > %rsp, which prohibits enclaves from allocating and passing parameters
> > for untrusted function calls (aka. o-calls).
> >
> > This patch addresses the problem above by introducing a new ABI that
> > preserves %rbp instead of %rsp. Then __vdso_sgx_enter_enclave() can
> > anchor its frame using %rbp so that enclaves are allowed to allocate
> > space on the untrusted stack by decrementing %rsp. Please note that
> > the stack space allocated in such way will be part of
> > __vdso_sgx_enter_enclave()'s frame so will be freed after
> > __vdso_sgx_enter_enclave() returns. Therefore,
> > __vdso_sgx_enter_enclave() has been changed to take a callback
> > function as an optional parameter, which if supplied, will be invoked
> > upon enclave exits (both AEX (Asynchronous Enclave
> > eXit) and normal exits), with the value of %rsp left off by the
> > enclave as a parameter to the callback.
> >
> > Here's the summary of API/ABI changes in this patch. More details
> > could be found in arch/x86/entry/vdso/vsgx_enter_enclave.S.
> > * 'struct sgx_enclave_exception' is renamed to 'struct
> sgx_enclave_exinfo'
> > because it is filled upon both AEX (i.e. exceptions) and normal
> enclave
> > exits.
> > * __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead
> of %rsp in
> > the previous implementation).
> > * __vdso_sgx_enter_enclave() takes one more parameter - a callback
> function to
> > be invoked upon enclave exits. This callback is optional, and if not
> > supplied, will cause __vdso_sgx_enter_enclave() to return upon
> enclave exits
> > (same behavior as previous implementation).
> > * The callback function is given as a parameter the value of %rsp at
> enclave
> > exit to address data "pushed" by the enclave. A positive value
> returned by
> > the callback will be treated as an ENCLU leaf for re-entering the
> enclave,
> > while a zero or negative value will be passed through as the return
> > value of __vdso_sgx_enter_enclave() to its caller. It's also safe to
> > leave callback by longjmp() or by throwing a C++ exception.
> >
> > Signed-off-by: Cedric Xing <cedric.xing@intel.com>
> > ---
> > arch/x86/entry/vdso/vsgx_enter_enclave.S | 156 ++++++++++++++--------
> -
> > arch/x86/include/uapi/asm/sgx.h | 14 +-
> > 2 files changed, 100 insertions(+), 70 deletions(-)
> >
> > diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S
> > b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> > index fe0bf6671d6d..210f4366374a 100644
> > --- a/arch/x86/entry/vdso/vsgx_enter_enclave.S
> > +++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> > @@ -14,88 +14,118 @@
> > .code64
> > .section .text, "ax"
> >
> > -#ifdef SGX_KERNEL_DOC
> > /**
> > * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> > *
> > * @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME
> > - * @tcs: **IN \%rbx** - TCS, must be non-NULL
> > - * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception'
> pointer
> > + * @tcs: **IN 0x08(\%rsp)** - TCS, must be non-NULL
> > + * @ex_info: **IN 0x10(\%rsp)** - Optional 'struct
> sgx_enclave_exinfo'
> > + * pointer
> > + * @callback: **IN 0x18(\%rsp)** - Optional callback function to be
> called on
> > + * enclave exit or exception
> > *
> > * Return:
> > * **OUT \%eax** -
> > - * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU
> > leaf is
> > - * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave
> > faults
> > + * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU
> > + leaf is not
> > + * allowed, %-EFAULT if ENCLU or the enclave faults, or a
> > + non-positive value
> > + * returned from ``callback`` (if one is supplied).
> > *
> > * **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant
> with the
> > - * x86-64 ABI, i.e. cannot be called from standard C code. As noted
> above,
> > - * input parameters must be passed via ``%eax``, ``%rbx`` and
> > ``%rcx``, with
> > - * the return value passed via ``%eax``. All registers except
> > ``%rsp`` must
> > - * be treated as volatile from the caller's perspective, including
> > but not
> > - * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the
> > enclave
> > - * being run **must** preserve the untrusted ``%rsp`` and stack.
> > + * x86-64 ABI, i.e. cannot be called from standard C code. As noted
> > + above,
> > + * input parameters must be passed via ``%eax``, ``8(%rsp)``,
> > + ``0x10(%rsp)`` and
> > + * ``0x18(%rsp)``, with the return value passed via ``%eax``. All
> > + other registers
> > + * will be passed through to the enclave as is. All registers except
> > + ``%rbp``
> > + * must be treated as volatile from the caller's perspective,
> > + including but not
> > + * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the
> > + enclave being
> > + * run **must** preserve the untrusted ``%rbp``.
> > + *
> > + * ``callback`` has the following signature:
> > + * int callback(long rdi, long rsi, long rdx,
> > + * struct sgx_enclave_exinfo *ex_info, long r8, long r9,
> > + * void *tcs, long ursp);
> > + * ``callback`` **shall** follow x86_64 ABI. All GPRs **except**
> > + ``%rax``, ``%rbx``
> > + * and ``rcx`` are passed through to ``callback``. ``%rdi``,
> > + ``%rsi``, ``%rdx``,
> > + * ``%r8``, ``%r9``, along with the value of ``%rsp`` when the
> > + enclave exited/excepted,
> > + * can be accessed directly as input parameters, while other GPRs can
> > + be
> > + * accessed in assembly if needed.
> > + * A positive value returned from ``callback`` will be treated as an
> > + ENCLU leaf
> > + * (e.g. EENTER/ERESUME) to reenter the enclave, while 0 or a
> > + negative return
>
> "to reenter the enclave without popping the extra data pushed by the
> enclave off the stack" or similar. We really don't want a situation
> where someone puts all there "keep on going" logic in the callback and
> the stack usage grows without bound.
>
> > + * value will be passed back to the caller of
> __vdso_sgx_enter_enclave().
> > + * It is also **safe** to ``longjmp()`` out of ``callback``.
>
> I'm not sure that "safe" needs emphasis.
>
> > */
> > -__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
> > - struct sgx_enclave_exception *ex_info)
> > -{
> > - if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
> > - return -EINVAL;
> > -
> > - if (!tcs)
> > - return -EINVAL;
> > -
> > - try {
> > - ENCLU[leaf];
> > - } catch (exception) {
> > - if (e)
> > - *e = exception;
> > - return -EFAULT;
> > - }
> > -
> > - return 0;
> > -}
> > -#endif
> > ENTRY(__vdso_sgx_enter_enclave)
> > - /* EENTER <= leaf <= ERESUME */
> > + /* Prolog */
> > + .cfi_startproc
> > + push %rbp
> > + .cfi_adjust_cfa_offset 8
> > + .cfi_rel_offset %rbp, 0
> > + mov %rsp, %rbp
> > + .cfi_def_cfa_register %rbp
> > +
> > +1: /* EENTER <= leaf <= ERESUME */
> > cmp $0x2, %eax
> > - jb bad_input
> > -
> > + jb 6f
> > cmp $0x3, %eax
> > - ja bad_input
> > + ja 6f
> >
> > - /* TCS must be non-NULL */
> > - test %rbx, %rbx
> > - je bad_input
> > + /* Load TCS and AEP */
> > + mov 0x10(%rbp), %rbx
> > + lea 2f(%rip), %rcx
> >
> > - /* Save @exception_info */
> > - push %rcx
> > -
> > - /* Load AEP for ENCLU */
> > - lea 1f(%rip), %rcx
> > -1: enclu
> > -
> > - add $0x8, %rsp
> > - xor %eax, %eax
> > - ret
> > -
> > -bad_input:
> > - mov $(-EINVAL), %rax
> > - ret
> > -
> > -.pushsection .fixup, "ax"
> > - /* Re-load @exception_info and fill it (if it's non-NULL) */
> > -2: pop %rcx
> > - test %rcx, %rcx
> > - je 3f
> > + /* Single ENCLU serving as both EENTER and AEP (ERESUME) */
> > +2: enclu
> >
> > + /* EEXIT path */
> > + xor %ebx, %ebx
> > +3: mov 0x18(%rbp), %rcx
> > + jrcxz 4f
> > mov %eax, EX_LEAF(%rcx)
> > - mov %di, EX_TRAPNR(%rcx)
> > - mov %si, EX_ERROR_CODE(%rcx)
> > + jnc 4f
> > + mov %di, EX_TRAPNR(%rcx)
> > + mov %si, EX_ERROR_CODE(%rcx)
> > mov %rdx, EX_ADDRESS(%rcx)
> > -3: mov $(-EFAULT), %rax
> > +
> > +4: /* Call *callback if supplied */
> > + mov 0x20(%rbp), %rax
> > + test %rax, %rax
>
> Maybe have a comment like "At this point, the effective return value is
> in RBX. If there is no callback, then return it."
>
> > + cmovz %rbx, %rax
> > + jz 7f
> > + /* Align stack and clear RFLAGS.DF per x86_64 ABI */
> > + mov %rsp, %rbx
>
> Whoa, this is too subtle here. Can you update the comment to clarify
> that the uRSP value set by the enclave needs to be saved so that the
> enclave can be resumed if needed?
>
> > + and $-0x10, %rsp
> > + cld
> > + /* Parameters for *callback */
> > + push %rbx
> > + push 0x10(%rbp)
> > + /* Call via retpoline */
> > + call 40f
> > + /* Cleanup stack */
> > + mov %rbx, %rsp
>
> To me, "Cleanup stack" makes me think that you're restoring the original
> RSP, but you're actually just undoing in the stack alignment.
> How about "Undo stack alignment"?
>
> But I'm not seeing the code that causes a return value RAX <= 0 to just
> return.
>
> > + jmp 1b
> > +40: /* retpoline */
> > + call 42f
> > +41: pause
> > + lfence
> > + jmp 41b
> > +42: mov %rax, (%rsp)
> > + ret
> > +
> > +5: /* Exception path */
> > + mov $-EFAULT, %ebx
> > + stc
> > + jmp 3b
> > +
> > +6: /* Unsupported ENCLU leaf */
> > + cmp $0, %eax
> > + jle 7f
> > + mov $-EINVAL, %eax
> > +
> > +7: /* Epilog */
> > + leave
> > + .cfi_def_cfa %rsp, 8
> > ret
> > -.popsection
> > + .cfi_endproc
> >
> > -_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
> > +_ASM_VDSO_EXTABLE_HANDLE(2b, 5b)
>
> --Andy
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v2 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-24 6:26 ` [RFC PATCH v2 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack Cedric Xing
@ 2019-04-24 19:04 ` Sean Christopherson
2019-04-25 23:31 ` Xing, Cedric
0 siblings, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-04-24 19:04 UTC (permalink / raw)
To: Cedric Xing
Cc: linux-kernel, linux-sgx, akpm, dave.hansen, serge.ayoun,
shay.katz-zamir, haitao.huang, kai.svahn, kai.huang,
jarkko.sakkinen
On Tue, Apr 23, 2019 at 11:26:22PM -0700, Cedric Xing wrote:
> The previous __vdso_sgx_enter_enclave() requires enclaves to preserve %rsp,
> which prohibits enclaves from allocating and passing parameters for
> untrusted function calls (aka. o-calls).
>
> This patch addresses the problem above by introducing a new ABI that preserves
> %rbp instead of %rsp. Then __vdso_sgx_enter_enclave() can anchor its frame
> using %rbp so that enclaves are allowed to allocate space on the untrusted
> stack by decrementing %rsp. Please note that the stack space allocated in such
> way will be part of __vdso_sgx_enter_enclave()'s frame so will be freed after
> __vdso_sgx_enter_enclave() returns. Therefore, __vdso_sgx_enter_enclave() has
> been revised to take a callback function as an optional parameter, which if
> supplied, will be invoked upon enclave exits (both AEX (Asynchronous Enclave
> eXit) and normal exits), with the value of %rsp left off by the enclave as a
> parameter to the callback.
AIUI, you and Andy had an off-list discussion regarding rewriting
__vdso_sgx_enter_enclave() vs. providing a second vDSO function. Can you
please summarize the discussion so that it's clear why you're pursuing a
single function?
In the end I probably agree that's it's desirable to have a single ABI and
vDSO function since the cost is basically that the non-callback case needs
to pass params via the stack instead of registers, but I do miss the
simplicity of separate implementations.
> Here's the summary of API/ABI changes in this patch. More details could be
> found in arch/x86/entry/vdso/vsgx_enter_enclave.S.
> * 'struct sgx_enclave_exception' is renamed to 'struct sgx_enclave_exinfo'
> because it is filled upon both AEX (i.e. exceptions) and normal enclave
> exits.
> * __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead of %rsp in
> the previous implementation).
> * __vdso_sgx_enter_enclave() takes one more parameter - a callback function to
> be invoked upon enclave exits. This callback is optional, and if not
> supplied, will cause __vdso_sgx_enter_enclave() to return upon enclave exits
> (same behavior as previous implementation).
> * The callback function is given as a parameter the value of %rsp at enclave
> exit to address data "pushed" by the enclave. A positive value returned by
> the callback will be treated as an ENCLU leaf for re-entering the enclave,
> while a zero or negative value will be passed through as the return
> value of __vdso_sgx_enter_enclave() to its caller. It's also safe to
> leave callback by longjmp() or by throwing a C++ exception.
>
> Signed-off-by: Cedric Xing <cedric.xing@intel.com>
> ---
> arch/x86/entry/vdso/vsgx_enter_enclave.S | 175 ++++++++++++++++-------
> arch/x86/include/uapi/asm/sgx.h | 14 +-
> 2 files changed, 128 insertions(+), 61 deletions(-)
>
> diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> index fe0bf6671d6d..debecb0d785c 100644
> --- a/arch/x86/entry/vdso/vsgx_enter_enclave.S
> +++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> @@ -19,83 +19,150 @@
> * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> *
> * @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME
> - * @tcs: **IN \%rbx** - TCS, must be non-NULL
> - * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception' pointer
> + * @tcs: **IN 0x08(\%rsp)** - TCS, must be non-NULL
> + * @ex_info: **IN 0x10(\%rsp)** - Optional 'struct sgx_enclave_exinfo'
> + * pointer
> + * @callback: **IN 0x18(\%rsp)** - Optional callback function to be called on
> + * enclave exit or exception
> *
> * Return:
> * **OUT \%eax** -
> - * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is
> - * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave faults
> + * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is not
> + * allowed, %-EFAULT if ENCLU or the enclave faults, or a non-positive value
> + * returned from ``callback`` (if one is supplied).
> *
> * **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant with the
> - * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
> - * input parameters must be passed via ``%eax``, ``%rbx`` and ``%rcx``, with
> - * the return value passed via ``%eax``. All registers except ``%rsp`` must
> - * be treated as volatile from the caller's perspective, including but not
> - * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave
> - * being run **must** preserve the untrusted ``%rsp`` and stack.
> + * x86-64 ABI, i.e. cannot be called from standard C code. As noted above,
> + * input parameters must be passed via ``%eax``, ``8(%rsp)``, ``0x10(%rsp)`` and
> + * ``0x18(%rsp)``, with the return value passed via ``%eax``. All other
> + * registers will be passed through to the enclave as is. All registers except
> + * ``%rbp`` must be treated as volatile from the caller's perspective, including
Hmm, this is my fault since I wrote the original comment, but stating
"All registers except %rbp must be treated as volatile" is confusing, e.g.
at first I thought it meant that the assembly blob was mucking with the
registers and that they couldn't be used to pass information out of the
enclave.
Maybe something like:
* Register ABI:
* - As noted above, input parameters are passed via %eax and the stack.
* - The return value is passed via %eax.
* - %rbx and %rcx must be treated as volatile as they are modified as part of
* enclaves transitions and are used as scratch regs.
* - %rbp **must** be preserved by the enclave in all cases, as it is used to
* reference paramaters when handling enclave exits.
* - %rdx, %rdi, %rsi and %r8-%r15 may be freely modified by the enclave
* and will not be passed back to the caller untouched.
* - %rsp is saved/restored across __vdso_sgx_enter_enclave(), but is passed
* as-is to the callback if one is provided, i.e. the enclave may use u_rsp
* to pass information to its associated callback.
*
* Callback ABI:
* <more information about callback ABI>
*
> + * but not limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the
> + * enclave being run **must** preserve the untrusted ``%rbp``.
> + *
> + * ``callback`` has the following signature:
> + * int callback(long rdi, long rsi, long rdx,
> + * struct sgx_enclave_exinfo *exinfo, long r8, long r9,
> + * void *tcs, long ursp);
> + * ``callback`` **shall** follow x86_64 ABI. All GPRs **except** ``%rax``,
> + * ``%rbx`` and ``rcx`` are passed through to ``callback``. ``%rdi``, ``%rsi``,
> + * ``%rdx``, ``%r8``, ``%r9``, along with the value of ``%rsp`` when the enclave
> + * exited/excepted, can be accessed directly as input parameters, while other
> + * GPRs can be accessed in assembly if needed. A positive value returned from
> + * ``callback`` will be treated as an ENCLU leaf (e.g. EENTER/ERESUME) to
> + * reenter the enclave (without popping the extra data pushed by the enclave off
> + * the stack), while 0 (zero) or a negative return value will be passed back to
> + * the caller of __vdso_sgx_enter_enclave(). It is also safe to leave
> + * ``callback`` via ``longjmp()`` or by throwing a C++ exception.
> */
While this may format nicely in .rst (I haven't checked), it's damn near
unreadable in its raw form. Escaping '%' in kernel-doc is unresolved at
this point, but this definitely is an argument for allowing the %CONST
interpretation to be disabled entirely.
longjmp() is probably the only thing that needs to be explicitly marked,
I think it would be better to simply say "the callback" instead of using
``callback``, i.e. use regular English instead of referencing the param.
> -__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
> - struct sgx_enclave_exception *ex_info)
> +typedef int (*sgx_callback)(long rdi, long rsi, long rdx,
> + struct sgx_enclave_exinfo *exinfo, long r8,
> + long r9, void *tcs, long ursp);
> +int __vdso_sgx_enter_enclave(int leaf, void *tcs,
> + struct sgx_enclave_exinfo *exinfo,
> + sgx_callback callback)
> {
> - if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
> - return -EINVAL;
> -
> - if (!tcs)
> - return -EINVAL;
> -
> - try {
> - ENCLU[leaf];
> - } catch (exception) {
> - if (e)
> - *e = exception;
> - return -EFAULT;
> + while (leaf == EENTER || leaf == ERESUME) {
This gives the impression that looping is the common case. I'd prefer
using 'goto' to show that the common case is a simple fall through whereas
the callback case can effectively loop on ENCLU.
> + int rc;
> + try {
> + ENCLU[leaf];
> + rc = 0;
> + if (exinfo)
> + exinfo->leaf = EEXIT;
> + } catch (exception) {
> + rc = -EFAULT;
> + if (exinfo)
> + *exinfo = exception;
> + }
> +
> + leaf = callback ? (*callback)(
> + rdi, rsi, rdx, exinfo, r8, r9, tcs, ursp) : rc;
> }
>
> - return 0;
> + return leaf > 0 ? -EINVAL : leaf;
> }
> #endif
> ENTRY(__vdso_sgx_enter_enclave)
> - /* EENTER <= leaf <= ERESUME */
> + /* Prolog */
> + .cfi_startproc
> + push %rbp
> + .cfi_adjust_cfa_offset 8
> + .cfi_rel_offset %rbp, 0
> + mov %rsp, %rbp
> + .cfi_def_cfa_register %rbp
> +
> +1: /* EENTER <= leaf <= ERESUME */
> cmp $0x2, %eax
> - jb bad_input
> -
> + jb 6f
> cmp $0x3, %eax
> - ja bad_input
> + ja 6f
>
> - /* TCS must be non-NULL */
> - test %rbx, %rbx
> - je bad_input
> + /* Load TCS and AEP */
> + mov 0x10(%rbp), %rbx
> + lea 2f(%rip), %rcx
>
> - /* Save @exception_info */
> - push %rcx
> + /* Single ENCLU serving as both EENTER and AEP (ERESUME) */
> +2: enclu
>
> - /* Load AEP for ENCLU */
> - lea 1f(%rip), %rcx
> -1: enclu
> + /* EEXIT path */
> + xor %ebx, %ebx
> +3: mov 0x18(%rbp), %rcx
@exinfo is optional, i.e. %ecx needs to be tested before its used.
> + jrcxz 4f
I dislike the number of flags and values that are stashed away only to
be consumed later, it makes the code hard to follow. AFAICT, a lot of
the shenanigans are done to reuse code because exinfo was overloaded,
but that's actually pointless, i.e. it's unnecessarily complex.
Overlading the struct is pointless becase if you make it mandatory when
using a callback then it can be nullified (when passed to the callback)
to indicate EEXIT. If it's still optional, then the callback needs an
extra param to explicitly indicate EEXIT vs. -EFAULT, otherwise the
callback has no indication whatsover of why it was invoked.
My preference is for the latter since it's more explicit and obvious.
Tangetially related, I'm not opposed to renaming it
'struct sgx_enclave_exception_info' for clarity.
> + mov %eax, EX_LEAF(%rcx)
> + jnc 4f
> + mov %di, EX_TRAPNR(%rcx)
> + mov %si, EX_ERROR_CODE(%rcx)
> + mov %rdx, EX_ADDRESS(%rcx)
My strong preference would be to organize the code to separate the various
flows, i.e. happy common case, invalid input, exception handler and
callback invocation. And make the common case a straight shot so that
it's more obvious what happens in the happy non-callback case.
>
> - add $0x8, %rsp
> - xor %eax, %eax
> +4: /* Call *callback if supplied */
> + mov 0x20(%rbp), %rax
> + test %rax, %rax
> + /* At this point, %ebx holds the effective return value, which shall be
> + * returned if no callback is specified */
Use kernel-doc format for multi-line comments. Missing punctionation at
the end. Blank lines between logical blocks would also help readability.
E.g.:
4: /* Call *callback if supplied */
mov 0x20(%rbp), %rax
test %rax, %rax
/*
* At this point, %ebx holds the effective return value, which shall be
* returned if no callback is specified.
*/
cmovz %rbx, %rax
jz 7f
> + cmovz %rbx, %rax
> + jz 7f
> + /* Align stack per x86_64 ABI. The original %rsp is saved in %rbx to be
> + * restored after *callback returns. */
> + mov %rsp, %rbx
> + and $-0x10, %rsp
> + /* Clear RFLAGS.DF per x86_64 ABI */
> + cld
> + /* Parameters for *callback */
> + push %rbx
> + push 0x10(%rbp)
> + /* Call *%rax via retpoline */
> + call 40f
Another case of stashing a value it consuming it later. This one is
especially brutal since %rax was loaded with CMOVcc, which means the
reader needs to backtrack even further to understand what %rax contains
at this point.
> + /* Restore %rsp to its original value left off by the enclave from last
> + * exit */
> + mov %rbx, %rsp
> + /* Positive return value from *callback will be interpreted as an ENCLU
> + * leaf, while a non-positive value will be interpreted as the return
> + * value to be passed back to the caller. */
Please use imperative mood in the comments, i.e. simply state what the
code is doing. E.g. "will be interpreted" makes it sound like something
else is processing the callback's return value.
> + jmp 1b
> +40: /* retpoline */
> + call 42f
> +41: pause
> + lfence
> + jmp 41b
> +42: mov %rax, (%rsp)
> ret
>
> -bad_input:
> - mov $(-EINVAL), %rax
> - ret
> +5: /* Exception path */
> + mov $-EFAULT, %ebx
> + stc
> + jmp 3b
>
> -.pushsection .fixup, "ax"
> - /* Re-load @exception_info and fill it (if it's non-NULL) */
> -2: pop %rcx
> - test %rcx, %rcx
> - je 3f
> +6: /* Unsupported ENCLU leaf */
> + cmp $0, %eax
> + jle 7f
> + mov $-EINVAL, %eax
>
> - mov %eax, EX_LEAF(%rcx)
> - mov %di, EX_TRAPNR(%rcx)
> - mov %si, EX_ERROR_CODE(%rcx)
> - mov %rdx, EX_ADDRESS(%rcx)
> -3: mov $(-EFAULT), %rax
> +7: /* Epilog */
> + leave
> + .cfi_def_cfa %rsp, 8
> ret
> -.popsection
> + .cfi_endproc
>
> -_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
> +_ASM_VDSO_EXTABLE_HANDLE(2b, 5b)
>
> ENDPROC(__vdso_sgx_enter_enclave)
Putting everything together, sans comments, I'd prefer something like the
following. Pasted in raw code as that'll hopefully be easier to review
and discuss than a diff.
Note, swapping 'long rc' and '... *e' would allow the callback flow
to save one memory access, but IMO the exception info belongs at the end
since it's optional.
#ifdef SGX_KERNEL_DOC
typedef int (*sgx_callback)(long rdi, long rsi, long rdx, long ret, long r8,
long r9, void *tcs, long ursp,
struct sgx_enclave_exception_info *e);
int __vdso_sgx_enter_enclave(int leaf, void *tcs,
struct sgx_enclave_exception_info *e,
sgx_callback callback)
{
int ret;
enter_enclave:
if (leaf != EENTER && leaf != ERESUME) {
ret = -EINVAL;
goto out;
}
try {
ENCLU[leaf];
ret = 0;
} catch (exception) {
ret = -EFAULT;
if (e)
*e = exception;
}
if (callback)
goto do_callback;
out:
return ret;
do_callback:
leaf = (*callback)(rdi, rsi, rdx, ret, r8, r9, e, tcs, ursp);
if (leaf <= 0)
goto out;
goto enter_enclave;
}
#endif
ENTRY(__vdso_sgx_enter_enclave)
/* Prolog */
.cfi_startproc
push %rbp
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rbp, 0
mov %rsp, %rbp
.cfi_def_cfa_register %rbp
1: /* EENTER <= leaf <= ERESUME */
cmp $0x2, %eax
jb 5f
cmp $0x3, %eax
ja 5f
/* Load TCS and AEP */
mov 0x10(%rbp), %rbx
lea 2f(%rip), %rcx
/* Single ENCLU serving as both EENTER and AEP (ERESUME) */
2: enclu
/* EEXIT branches here unless the enclave is doing something fancy. */
xor %eax, %eax
3: cmp $0, 0x20(%rbp)
jne 8f
4: leave
.cfi_def_cfa %rsp, 8
ret
5: mov $(-EINVAL), %rax
jmp 4b
6: mov 0x18(%rbp), %rcx
test %rcx, %rcx
je 7f
/* Fill optional exception info. */
mov %eax, EX_LEAF(%rcx)
mov %di, EX_TRAPNR(%rcx)
mov %si, EX_ERROR_CODE(%rcx)
mov %rdx, EX_ADDRESS(%rcx)
7: mov $(-EFAULT), %rax
jmp 3b
8: /*
* Align stack per x86_64 ABI. Save the original %rsp in %rbx to be
* restored after the callback returns.
*/
mov %rsp, %rbx
and $-0x10, %rsp
/* Push @e, u_rsp and @tcs as parameters to the callback. */
push 0x18(%rbp)
push %rbx
push 0x10(%rbp)
/* Pass the "return value" to the callback via %rcx. */
mov %rax, %rcx
/* Clear RFLAGS.DF per x86_64 ABI */
cld
/* Load the callback pointer to %rax and invoke it via retpoline. */
mov 0x20(%rbp), %rax
call 40f
/* Restore %rsp to its post-exit value. */
mov %rbx, %rsp
/*
* If the return from callback is zero or negative, return immediately,
* otherwise attempt to re-execute ENCLU with the return interpreted as
* the requested ENCLU leaf.
*/
cmp $0, %eax
jle 4b
jmp 1b
40: /* retpoline */
call 42f
41: pause
lfence
jmp 41b
42: mov %rax, (%rsp)
ret
.cfi_endproc
_ASM_VDSO_EXTABLE_HANDLE(2b, 6b)
ENDPROC(__vdso_sgx_enter_enclave)
^ permalink raw reply [flat|nested] 285+ messages in thread
* RE: [RFC PATCH v2 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-24 19:04 ` Sean Christopherson
@ 2019-04-25 23:31 ` Xing, Cedric
2019-04-26 21:00 ` Sean Christopherson
0 siblings, 1 reply; 285+ messages in thread
From: Xing, Cedric @ 2019-04-25 23:31 UTC (permalink / raw)
To: Christopherson, Sean J
Cc: linux-kernel, linux-sgx, akpm, Hansen, Dave, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Svahn, Kai, Huang, Kai,
jarkko.sakkinen
Hi Sean,
> AIUI, you and Andy had an off-list discussion regarding rewriting
> __vdso_sgx_enter_enclave() vs. providing a second vDSO function. Can
> you
> please summarize the discussion so that it's clear why you're pursuing a
> single function?
>
> In the end I probably agree that's it's desirable to have a single ABI
> and
> vDSO function since the cost is basically that the non-callback case
> needs
> to pass params via the stack instead of registers, but I do miss the
> simplicity of separate implementations.
>
The major reason is we don't want to maintain 2 different ABIs (preserving %rsp vs. %rbp). Given the new one covers all known use cases, we prefer not to keep the other one.
> > Here's the summary of API/ABI changes in this patch. More details
> could be
> > found in arch/x86/entry/vdso/vsgx_enter_enclave.S.
> > * 'struct sgx_enclave_exception' is renamed to 'struct
> sgx_enclave_exinfo'
> > because it is filled upon both AEX (i.e. exceptions) and normal
> enclave
> > exits.
> > * __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead
> of %rsp in
> > the previous implementation).
> > * __vdso_sgx_enter_enclave() takes one more parameter - a callback
> function to
> > be invoked upon enclave exits. This callback is optional, and if not
> > supplied, will cause __vdso_sgx_enter_enclave() to return upon
> enclave exits
> > (same behavior as previous implementation).
> > * The callback function is given as a parameter the value of %rsp at
> enclave
> > exit to address data "pushed" by the enclave. A positive value
> returned by
> > the callback will be treated as an ENCLU leaf for re-entering the
> enclave,
> > while a zero or negative value will be passed through as the return
> > value of __vdso_sgx_enter_enclave() to its caller. It's also safe to
> > leave callback by longjmp() or by throwing a C++ exception.
> >
> > Signed-off-by: Cedric Xing <cedric.xing@intel.com>
> > ---
> > arch/x86/entry/vdso/vsgx_enter_enclave.S | 175 ++++++++++++++++------
> -
> > arch/x86/include/uapi/asm/sgx.h | 14 +-
> > 2 files changed, 128 insertions(+), 61 deletions(-)
> >
> > diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S
> b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> > index fe0bf6671d6d..debecb0d785c 100644
> > --- a/arch/x86/entry/vdso/vsgx_enter_enclave.S
> > +++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
> > @@ -19,83 +19,150 @@
> > * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> > *
> > * @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME
> > - * @tcs: **IN \%rbx** - TCS, must be non-NULL
> > - * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception'
> pointer
> > + * @tcs: **IN 0x08(\%rsp)** - TCS, must be non-NULL
> > + * @ex_info: **IN 0x10(\%rsp)** - Optional 'struct
> sgx_enclave_exinfo'
> > + * pointer
> > + * @callback: **IN 0x18(\%rsp)** - Optional callback function to
> be called on
> > + * enclave exit or exception
> > *
> > * Return:
> > * **OUT \%eax** -
> > - * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU
> leaf is
> > - * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave
> faults
> > + * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU
> leaf is not
> > + * allowed, %-EFAULT if ENCLU or the enclave faults, or a non-
> positive value
> > + * returned from ``callback`` (if one is supplied).
> > *
> > * **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant
> with the
> > - * x86-64 ABI, i.e. cannot be called from standard C code. As noted
> above,
> > - * input parameters must be passed via ``%eax``, ``%rbx`` and
> ``%rcx``, with
> > - * the return value passed via ``%eax``. All registers except
> ``%rsp`` must
> > - * be treated as volatile from the caller's perspective, including
> but not
> > - * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the
> enclave
> > - * being run **must** preserve the untrusted ``%rsp`` and stack.
> > + * x86-64 ABI, i.e. cannot be called from standard C code. As noted
> above,
> > + * input parameters must be passed via ``%eax``, ``8(%rsp)``,
> ``0x10(%rsp)`` and
> > + * ``0x18(%rsp)``, with the return value passed via ``%eax``. All
> other
> > + * registers will be passed through to the enclave as is. All
> registers except
> > + * ``%rbp`` must be treated as volatile from the caller's perspective,
> including
>
> Hmm, this is my fault since I wrote the original comment, but stating
> "All registers except %rbp must be treated as volatile" is confusing,
> e.g.
> at first I thought it meant that the assembly blob was mucking with the
> registers and that they couldn't be used to pass information out of the
> enclave.
>
> Maybe something like:
>
> * Register ABI:
> * - As noted above, input parameters are passed via %eax and the stack.
> * - The return value is passed via %eax.
> * - %rbx and %rcx must be treated as volatile as they are modified as
> part of
> * enclaves transitions and are used as scratch regs.
> * - %rbp **must** be preserved by the enclave in all cases, as it is
> used to
> * reference paramaters when handling enclave exits.
> * - %rdx, %rdi, %rsi and %r8-%r15 may be freely modified by the
> enclave
> * and will not be passed back to the caller untouched.
> * - %rsp is saved/restored across __vdso_sgx_enter_enclave(), but is
> passed
> * as-is to the callback if one is provided, i.e. the enclave may use
> u_rsp
> * to pass information to its associated callback.
> *
> * Callback ABI:
> * <more information about callback ABI>
> *
Really appreciated! I'll update the comments in the next revision.
>
> > + * but not limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely,
> the
> > + * enclave being run **must** preserve the untrusted ``%rbp``.
> > + *
> > + * ``callback`` has the following signature:
> > + * int callback(long rdi, long rsi, long rdx,
> > + * struct sgx_enclave_exinfo *exinfo, long r8, long r9,
> > + * void *tcs, long ursp);
> > + * ``callback`` **shall** follow x86_64 ABI. All GPRs **except**
> ``%rax``,
> > + * ``%rbx`` and ``rcx`` are passed through to ``callback``. ``%rdi``,
> ``%rsi``,
> > + * ``%rdx``, ``%r8``, ``%r9``, along with the value of ``%rsp`` when
> the enclave
> > + * exited/excepted, can be accessed directly as input parameters,
> while other
> > + * GPRs can be accessed in assembly if needed. A positive value
> returned from
> > + * ``callback`` will be treated as an ENCLU leaf (e.g. EENTER/ERESUME)
> to
> > + * reenter the enclave (without popping the extra data pushed by the
> enclave off
> > + * the stack), while 0 (zero) or a negative return value will be
> passed back to
> > + * the caller of __vdso_sgx_enter_enclave(). It is also safe to leave
> > + * ``callback`` via ``longjmp()`` or by throwing a C++ exception.
> > */
>
> While this may format nicely in .rst (I haven't checked), it's damn near
> unreadable in its raw form. Escaping '%' in kernel-doc is unresolved at
> this point, but this definitely is an argument for allowing the %CONST
> interpretation to be disabled entirely.
>
I know very little about kernel-doc. Not quite sure which markup means what. Or if you don't mind, could you just write up the whole thing (you have written half of it in your email already) so I can include it into my next revision?
> longjmp() is probably the only thing that needs to be explicitly marked,
> I think it would be better to simply say "the callback" instead of using
> ``callback``, i.e. use regular English instead of referencing the param.
>
Are you suggesting not to mention C++ exception here?
> > -__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
> > - struct sgx_enclave_exception *ex_info)
> > +typedef int (*sgx_callback)(long rdi, long rsi, long rdx,
> > + struct sgx_enclave_exinfo *exinfo, long r8,
> > + long r9, void *tcs, long ursp);
> > +int __vdso_sgx_enter_enclave(int leaf, void *tcs,
> > + struct sgx_enclave_exinfo *exinfo,
> > + sgx_callback callback)
> > {
> > - if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
> > - return -EINVAL;
> > -
> > - if (!tcs)
> > - return -EINVAL;
> > -
> > - try {
> > - ENCLU[leaf];
> > - } catch (exception) {
> > - if (e)
> > - *e = exception;
> > - return -EFAULT;
> > + while (leaf == EENTER || leaf == ERESUME) {
>
> This gives the impression that looping is the common case. I'd prefer
> using 'goto' to show that the common case is a simple fall through
> whereas
> the callback case can effectively loop on ENCLU.
Looping IMO is indeed the common case. Just think of the case of an enclave making a sequence of o-calls.
>
> > + int rc;
> > + try {
> > + ENCLU[leaf];
> > + rc = 0;
> > + if (exinfo)
> > + exinfo->leaf = EEXIT;
> > + } catch (exception) {
> > + rc = -EFAULT;
> > + if (exinfo)
> > + *exinfo = exception;
> > + }
> > +
> > + leaf = callback ? (*callback)(
> > + rdi, rsi, rdx, exinfo, r8, r9, tcs, ursp) : rc;
> > }
> >
> > - return 0;
> > + return leaf > 0 ? -EINVAL : leaf;
> > }
> > #endif
> > ENTRY(__vdso_sgx_enter_enclave)
> > - /* EENTER <= leaf <= ERESUME */
> > + /* Prolog */
> > + .cfi_startproc
> > + push %rbp
> > + .cfi_adjust_cfa_offset 8
> > + .cfi_rel_offset %rbp, 0
> > + mov %rsp, %rbp
> > + .cfi_def_cfa_register %rbp
> > +
> > +1: /* EENTER <= leaf <= ERESUME */
> > cmp $0x2, %eax
> > - jb bad_input
> > -
> > + jb 6f
> > cmp $0x3, %eax
> > - ja bad_input
> > + ja 6f
> >
> > - /* TCS must be non-NULL */
> > - test %rbx, %rbx
> > - je bad_input
> > + /* Load TCS and AEP */
> > + mov 0x10(%rbp), %rbx
> > + lea 2f(%rip), %rcx
> >
> > - /* Save @exception_info */
> > - push %rcx
> > + /* Single ENCLU serving as both EENTER and AEP (ERESUME) */
> > +2: enclu
> >
> > - /* Load AEP for ENCLU */
> > - lea 1f(%rip), %rcx
> > -1: enclu
> > + /* EEXIT path */
> > + xor %ebx, %ebx
> > +3: mov 0x18(%rbp), %rcx
>
> @exinfo is optional, i.e. %ecx needs to be tested before its used.
>
> > + jrcxz 4f
The above instruction (i.e. jrcxz) does test %rcx.
>
> I dislike the number of flags and values that are stashed away only to
> be consumed later, it makes the code hard to follow. AFAICT, a lot of
> the shenanigans are done to reuse code because exinfo was overloaded,
> but that's actually pointless, i.e. it's unnecessarily complex.
>
> Overlading the struct is pointless becase if you make it mandatory when
> using a callback then it can be nullified (when passed to the callback)
> to indicate EEXIT. If it's still optional, then the callback needs an
> extra param to explicitly indicate EEXIT vs. -EFAULT, otherwise the
> callback has no indication whatsover of why it was invoked.
>
> My preference is for the latter since it's more explicit and obvious.
I wouldn't nullify @exinfo at EEXIT because that could be used as a "context" pointer to the callback.
Making it optionally because the callback can still use other registers (e.g. %rdi) determine the reason without it. For example, an enclave may set %rdi to non-zero to signify o-call/e-ret while zero (as set by AEX) would indicate an exception. My intention is not to impose unnecessary restrictions on applications.
>
> Tangetially related, I'm not opposed to renaming it
> 'struct sgx_enclave_exception_info' for clarity.
I'd still prefer sgx_enclave_exinfo to imply its use in both _ex_it and _ex_ception cases, for the reason stated above.
>
> > + mov %eax, EX_LEAF(%rcx)
> > + jnc 4f
> > + mov %di, EX_TRAPNR(%rcx)
> > + mov %si, EX_ERROR_CODE(%rcx)
> > + mov %rdx, EX_ADDRESS(%rcx)
>
> My strong preference would be to organize the code to separate the
> various
> flows, i.e. happy common case, invalid input, exception handler and
> callback invocation. And make the common case a straight shot so that
> it's more obvious what happens in the happy non-callback case.
I'd prefer more concise code. After all, this is assembly without optimization done by the compiler. If it were for C, I'd agree with you because either case would end up in roughly the same code (in terms of code size or efficiency) due to the optimizer.
> >
> > - add $0x8, %rsp
> > - xor %eax, %eax
> > +4: /* Call *callback if supplied */
> > + mov 0x20(%rbp), %rax
> > + test %rax, %rax
> > + /* At this point, %ebx holds the effective return value, which
> shall be
> > + * returned if no callback is specified */
>
> Use kernel-doc format for multi-line comments. Missing punctionation at
> the end. Blank lines between logical blocks would also help readability.
>
> E.g.:
>
> 4: /* Call *callback if supplied */
> mov 0x20(%rbp), %rax
> test %rax, %rax
>
> /*
> * At this point, %ebx holds the effective return value, which
> shall be
> * returned if no callback is specified.
> */
> cmovz %rbx, %rax
> jz 7f
I know very little about kernel-doc. Would you please point me to documents describing it? What you suggested here will of course be incorporated into my next revision.
> > + cmovz %rbx, %rax
> > + jz 7f
> > + /* Align stack per x86_64 ABI. The original %rsp is saved in %rbx
> to be
> > + * restored after *callback returns. */
> > + mov %rsp, %rbx
> > + and $-0x10, %rsp
> > + /* Clear RFLAGS.DF per x86_64 ABI */
> > + cld
> > + /* Parameters for *callback */
> > + push %rbx
> > + push 0x10(%rbp)
> > + /* Call *%rax via retpoline */
> > + call 40f
>
> Another case of stashing a value it consuming it later. This one is
> especially brutal since %rax was loaded with CMOVcc, which means the
> reader needs to backtrack even further to understand what %rax contains
> at this point.
%rax was loaded only 2 instructions ago, while what %rbx is containing is described in the comment immediately above it. I think I have done my best to make it readable. After all, this is assembly so can't expect something like giving proper names to variables in C because that just can't be done in assembly.
>
> > + /* Restore %rsp to its original value left off by the enclave from
> last
> > + * exit */
> > + mov %rbx, %rsp
> > + /* Positive return value from *callback will be interpreted as an
> ENCLU
> > + * leaf, while a non-positive value will be interpreted as the
> return
> > + * value to be passed back to the caller. */
>
> Please use imperative mood in the comments, i.e. simply state what the
> code is doing. E.g. "will be interpreted" makes it sound like something
> else is processing the callback's return value.
Will do in the next revision.
> > + jmp 1b
> > +40: /* retpoline */
> > + call 42f
> > +41: pause
> > + lfence
> > + jmp 41b
> > +42: mov %rax, (%rsp)
> > ret
> >
> > -bad_input:
> > - mov $(-EINVAL), %rax
> > - ret
> > +5: /* Exception path */
> > + mov $-EFAULT, %ebx
> > + stc
> > + jmp 3b
> >
> > -.pushsection .fixup, "ax"
> > - /* Re-load @exception_info and fill it (if it's non-NULL) */
> > -2: pop %rcx
> > - test %rcx, %rcx
> > - je 3f
> > +6: /* Unsupported ENCLU leaf */
> > + cmp $0, %eax
> > + jle 7f
> > + mov $-EINVAL, %eax
> >
> > - mov %eax, EX_LEAF(%rcx)
> > - mov %di, EX_TRAPNR(%rcx)
> > - mov %si, EX_ERROR_CODE(%rcx)
> > - mov %rdx, EX_ADDRESS(%rcx)
> > -3: mov $(-EFAULT), %rax
> > +7: /* Epilog */
> > + leave
> > + .cfi_def_cfa %rsp, 8
> > ret
> > -.popsection
> > + .cfi_endproc
> >
> > -_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
> > +_ASM_VDSO_EXTABLE_HANDLE(2b, 5b)
> >
> > ENDPROC(__vdso_sgx_enter_enclave)
>
> Putting everything together, sans comments, I'd prefer something like
> the
> following. Pasted in raw code as that'll hopefully be easier to review
> and discuss than a diff.
>
> Note, swapping 'long rc' and '... *e' would allow the callback flow
> to save one memory access, but IMO the exception info belongs at the end
> since it's optional.
You have probably misunderstood my code. 'ret' is _not_ passed while @exinfo is passed in %rcx. exinfo->leaf implies the reason of the callback.
Or are you suggesting to add 'ret' as one more parameter to the callback? I don't think it necessary because exinfo contains everything the callback would ever need.
> #ifdef SGX_KERNEL_DOC
> typedef int (*sgx_callback)(long rdi, long rsi, long rdx, long ret, long
> r8,
> long r9, void *tcs, long ursp,
> struct sgx_enclave_exception_info *e);
> int __vdso_sgx_enter_enclave(int leaf, void *tcs,
> struct sgx_enclave_exception_info *e,
> sgx_callback callback)
> {
> int ret;
>
> enter_enclave:
> if (leaf != EENTER && leaf != ERESUME) {
> ret = -EINVAL;
> goto out;
> }
>
> try {
> ENCLU[leaf];
> ret = 0;
> } catch (exception) {
> ret = -EFAULT;
> if (e)
> *e = exception;
> }
> if (callback)
> goto do_callback;
> out:
> return ret;
>
> do_callback:
> leaf = (*callback)(rdi, rsi, rdx, ret, r8, r9, e, tcs, ursp);
> if (leaf <= 0)
> goto out;
> goto enter_enclave;
> }
> #endif
> ENTRY(__vdso_sgx_enter_enclave)
> /* Prolog */
> .cfi_startproc
> push %rbp
> .cfi_adjust_cfa_offset 8
> .cfi_rel_offset %rbp, 0
> mov %rsp, %rbp
> .cfi_def_cfa_register %rbp
>
> 1: /* EENTER <= leaf <= ERESUME */
> cmp $0x2, %eax
> jb 5f
> cmp $0x3, %eax
> ja 5f
>
> /* Load TCS and AEP */
> mov 0x10(%rbp), %rbx
> lea 2f(%rip), %rcx
>
> /* Single ENCLU serving as both EENTER and AEP (ERESUME) */
> 2: enclu
>
> /* EEXIT branches here unless the enclave is doing something fancy.
> */
> xor %eax, %eax
>
> 3: cmp $0, 0x20(%rbp)
> jne 8f
>
> 4: leave
> .cfi_def_cfa %rsp, 8
> ret
>
> 5: mov $(-EINVAL), %rax
> jmp 4b
>
> 6: mov 0x18(%rbp), %rcx
> test %rcx, %rcx
> je 7f
>
> /* Fill optional exception info. */
> mov %eax, EX_LEAF(%rcx)
> mov %di, EX_TRAPNR(%rcx)
> mov %si, EX_ERROR_CODE(%rcx)
> mov %rdx, EX_ADDRESS(%rcx)
> 7: mov $(-EFAULT), %rax
> jmp 3b
>
> 8: /*
> * Align stack per x86_64 ABI. Save the original %rsp in %rbx to
> be
> * restored after the callback returns.
> */
> mov %rsp, %rbx
> and $-0x10, %rsp
>
> /* Push @e, u_rsp and @tcs as parameters to the callback. */
> push 0x18(%rbp)
> push %rbx
> push 0x10(%rbp)
>
> /* Pass the "return value" to the callback via %rcx. */
> mov %rax, %rcx
>
> /* Clear RFLAGS.DF per x86_64 ABI */
> cld
>
> /* Load the callback pointer to %rax and invoke it via retpoline.
> */
> mov 0x20(%rbp), %rax
> call 40f
>
> /* Restore %rsp to its post-exit value. */
> mov %rbx, %rsp
>
> /*
> * If the return from callback is zero or negative, return
> immediately,
> * otherwise attempt to re-execute ENCLU with the return
> interpreted as
> * the requested ENCLU leaf.
> */
> cmp $0, %eax
> jle 4b
> jmp 1b
>
> 40: /* retpoline */
> call 42f
> 41: pause
> lfence
> jmp 41b
> 42: mov %rax, (%rsp)
> ret
> .cfi_endproc
>
> _ASM_VDSO_EXTABLE_HANDLE(2b, 6b)
>
> ENDPROC(__vdso_sgx_enter_enclave)
Your code will work. The only missing thing is a ".cfi_def_cfa %rbp, 16" after 'ret' instruction in block 4. I decided to keep "leave; ret" at the end to avoid confusion around the frame pointer. As you may also notice, GCC generated code usually does the same thing.
-Cedric
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v2 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-25 23:31 ` Xing, Cedric
@ 2019-04-26 21:00 ` Sean Christopherson
2019-05-02 8:28 ` Jarkko Sakkinen
0 siblings, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-04-26 21:00 UTC (permalink / raw)
To: Xing, Cedric
Cc: linux-kernel, linux-sgx, akpm, Hansen, Dave, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Svahn, Kai, Huang, Kai,
jarkko.sakkinen
On Thu, Apr 25, 2019 at 04:31:40PM -0700, Xing, Cedric wrote:
> > While this may format nicely in .rst (I haven't checked), it's damn near
> > unreadable in its raw form. Escaping '%' in kernel-doc is unresolved at
> > this point, but this definitely is an argument for allowing the %CONST
> > interpretation to be disabled entirely.
> >
>
> I know very little about kernel-doc. Not quite sure which markup means what.
> Or if you don't mind, could you just write up the whole thing (you have
> written half of it in your email already) so I can include it into my next
> revision?
Hmm, at this point I'd say just ignore the comment entirely. It's
something we need to sort out in the "official" series anyways.
>
> > longjmp() is probably the only thing that needs to be explicitly marked,
> > I think it would be better to simply say "the callback" instead of using
> > ``callback``, i.e. use regular English instead of referencing the param.
> >
>
> Are you suggesting not to mention C++ exception here?
No, I was saying that rather than do the backtick markup on callback,
just say "the callback". The copious amount of markup makes it difficult
to read the raw text.
>
> > > -__vdso_sgx_enter_enclave(u32 leaf, void *tcs,
> > > - struct sgx_enclave_exception *ex_info)
> > > +typedef int (*sgx_callback)(long rdi, long rsi, long rdx,
> > > + struct sgx_enclave_exinfo *exinfo, long r8,
> > > + long r9, void *tcs, long ursp);
> > > +int __vdso_sgx_enter_enclave(int leaf, void *tcs,
> > > + struct sgx_enclave_exinfo *exinfo,
> > > + sgx_callback callback)
> > > {
> > > - if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
> > > - return -EINVAL;
> > > -
> > > - if (!tcs)
> > > - return -EINVAL;
> > > -
> > > - try {
> > > - ENCLU[leaf];
> > > - } catch (exception) {
> > > - if (e)
> > > - *e = exception;
> > > - return -EFAULT;
> > > + while (leaf == EENTER || leaf == ERESUME) {
> >
> > This gives the impression that looping is the common case. I'd prefer
> > using 'goto' to show that the common case is a simple fall through
> > whereas
> > the callback case can effectively loop on ENCLU.
>
> Looping IMO is indeed the common case. Just think of the case of an enclave
> making a sequence of o-calls.
Except that looping is only done when using a callback, and that most
definitely is not the common case.
> > > + int rc;
> > > + try {
> > > + ENCLU[leaf];
> > > + rc = 0;
> > > + if (exinfo)
> > > + exinfo->leaf = EEXIT;
> > > + } catch (exception) {
> > > + rc = -EFAULT;
> > > + if (exinfo)
> > > + *exinfo = exception;
> > > + }
> > > +
> > > + leaf = callback ? (*callback)(
> > > + rdi, rsi, rdx, exinfo, r8, r9, tcs, ursp) : rc;
> > > }
> > >
> > > - return 0;
> > > + return leaf > 0 ? -EINVAL : leaf;
> > > }
> > > #endif
> > > ENTRY(__vdso_sgx_enter_enclave)
> > > - /* EENTER <= leaf <= ERESUME */
> > > + /* Prolog */
> > > + .cfi_startproc
> > > + push %rbp
> > > + .cfi_adjust_cfa_offset 8
> > > + .cfi_rel_offset %rbp, 0
> > > + mov %rsp, %rbp
> > > + .cfi_def_cfa_register %rbp
> > > +
> > > +1: /* EENTER <= leaf <= ERESUME */
> > > cmp $0x2, %eax
> > > - jb bad_input
> > > -
> > > + jb 6f
> > > cmp $0x3, %eax
> > > - ja bad_input
> > > + ja 6f
> > >
> > > - /* TCS must be non-NULL */
> > > - test %rbx, %rbx
> > > - je bad_input
> > > + /* Load TCS and AEP */
> > > + mov 0x10(%rbp), %rbx
> > > + lea 2f(%rip), %rcx
> > >
> > > - /* Save @exception_info */
> > > - push %rcx
> > > + /* Single ENCLU serving as both EENTER and AEP (ERESUME) */
> > > +2: enclu
> > >
> > > - /* Load AEP for ENCLU */
> > > - lea 1f(%rip), %rcx
> > > -1: enclu
> > > + /* EEXIT path */
> > > + xor %ebx, %ebx
> > > +3: mov 0x18(%rbp), %rcx
> >
> > @exinfo is optional, i.e. %ecx needs to be tested before its used.
> >
> > > + jrcxz 4f
>
> The above instruction (i.e. jrcxz) does test %rcx.
I love x86 ISA.
> > I dislike the number of flags and values that are stashed away only to
> > be consumed later, it makes the code hard to follow. AFAICT, a lot of
> > the shenanigans are done to reuse code because exinfo was overloaded,
> > but that's actually pointless, i.e. it's unnecessarily complex.
> >
> > Overlading the struct is pointless becase if you make it mandatory when
> > using a callback then it can be nullified (when passed to the callback)
> > to indicate EEXIT. If it's still optional, then the callback needs an
> > extra param to explicitly indicate EEXIT vs. -EFAULT, otherwise the
> > callback has no indication whatsover of why it was invoked.
> >
> > My preference is for the latter since it's more explicit and obvious.
>
> I wouldn't nullify @exinfo at EEXIT because that could be used as a "context"
> pointer to the callback.
>
> Making it optionally because the callback can still use other registers (e.g.
> %rdi) determine the reason without it. For example, an enclave may set %rdi
> to non-zero to signify o-call/e-ret while zero (as set by AEX) would indicate
> an exception. My intention is not to impose unnecessary restrictions on
> applications.
> > Tangetially related, I'm not opposed to renaming it
> > 'struct sgx_enclave_exception_info' for clarity.
>
> I'd still prefer sgx_enclave_exinfo to imply its use in both _ex_it and
> _ex_ception cases, for the reason stated above.
Because an enclave *could* choose a different route? The cost is
essentially one memory access, which is likely offset by the fact that
the callback can directly check %rcx for the exit reason. You are not
the sole consumer of this code.
> > > + mov %eax, EX_LEAF(%rcx)
> > > + jnc 4f
> > > + mov %di, EX_TRAPNR(%rcx)
> > > + mov %si, EX_ERROR_CODE(%rcx)
> > > + mov %rdx, EX_ADDRESS(%rcx)
> >
> > My strong preference would be to organize the code to separate the
> > various
> > flows, i.e. happy common case, invalid input, exception handler and
> > callback invocation. And make the common case a straight shot so that
> > it's more obvious what happens in the happy non-callback case.
>
> I'd prefer more concise code. After all, this is assembly without
> optimization done by the compiler. If it were for C, I'd agree with you
> because either case would end up in roughly the same code (in terms of code
> size or efficiency) due to the optimizer.
a) You've argued multiple times this isn't performance critical code.
b) A straight shot is optimal for the common case where userspace is not
using a callback.
c) The performance difference is something like one or two memory accesses.
That's peanuts compared to how readable the code is by people without
prior knowledge of exactly what this code is doing.
> > > - add $0x8, %rsp
> > > - xor %eax, %eax
> > > +4: /* Call *callback if supplied */
> > > + mov 0x20(%rbp), %rax
> > > + test %rax, %rax
> > > + /* At this point, %ebx holds the effective return value, which
> > shall be
> > > + * returned if no callback is specified */
> >
> > Use kernel-doc format for multi-line comments. Missing punctionation at
> > the end. Blank lines between logical blocks would also help readability.
> >
> > E.g.:
> >
> > 4: /* Call *callback if supplied */
> > mov 0x20(%rbp), %rax
> > test %rax, %rax
> >
> > /*
> > * At this point, %ebx holds the effective return value, which
> > shall be
> > * returned if no callback is specified.
> > */
> > cmovz %rbx, %rax
> > jz 7f
>
> I know very little about kernel-doc. Would you please point me to documents
> describing it? What you suggested here will of course be incorporated into my
> next revision.
Documentation/doc-guide/kernel-doc.rst
[...]
> > Note, swapping 'long rc' and '... *e' would allow the callback flow
> > to save one memory access, but IMO the exception info belongs at the end
> > since it's optional.
>
> You have probably misunderstood my code. 'ret' is _not_ passed while @exinfo
> is passed in %rcx. exinfo->leaf implies the reason of the callback.
>
> Or are you suggesting to add 'ret' as one more parameter to the callback? I
> don't think it necessary because exinfo contains everything the callback
> would ever need.
The latter, because exinfo is optional, as stated above.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver
2019-04-24 1:04 ` Jethro Beekman
@ 2019-04-29 19:08 ` Sean Christopherson
0 siblings, 0 replies; 285+ messages in thread
From: Sean Christopherson @ 2019-04-29 19:08 UTC (permalink / raw)
To: Jethro Beekman
Cc: Jarkko Sakkinen, linux-kernel, x86, linux-sgx, akpm, dave.hansen,
nhorman, npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
rientjes
On Wed, Apr 24, 2019 at 01:04:21AM +0000, Jethro Beekman wrote:
> On 2019-04-23 17:26, Sean Christopherson wrote:
> >On Tue, Apr 23, 2019 at 11:29:24PM +0000, Jethro Beekman wrote:
> >>On 2019-04-22 14:58, Sean Christopherson wrote:
> >>>Now that the core SGX code is approaching stability, I'd like to start
> >>>sending RFCs for the EPC virtualization and KVM bits to hash out that side
> >>>of things. The ACPI crud is the last chunk of code that would require
> >>>non-trivial changes to the core SGX code for the proposed virtualization
> >>>implementation. I'd strongly prefer to get it out of the way before
> >>>sending the KVM RFCs.
> >>
> >>What kind of changes? Wouldn't KVM just be another consumer of the same API
> >>used by the driver?
> >
> >Nope, userspace "only" needs to be able to mmap() arbitrary chunks of EPC.
>
> I don't think this is sufficient. Don't you need enclave tracking in order
> to support paging?
The plan is to not support graceful EPC reclaim in the host on platforms
without VMM oversubscription extensions, e.g. ENCLV, ERDINFO, etc..., due
to the complexity and performance overhead. Mostly the complexity.
And if reclaim were to be supported without the extensions, it would be
done without exiting to userspace on every ENCLS instruction.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 16/28] x86/sgx: Add provisioning
2019-04-24 1:34 ` Jethro Beekman
@ 2019-05-02 8:27 ` Jarkko Sakkinen
0 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-02 8:27 UTC (permalink / raw)
To: Jethro Beekman
Cc: linux-kernel, x86, linux-sgx, akpm, dave.hansen,
sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes, James Morris,
Serge E . Hallyn, linux-security-module
On Wed, Apr 24, 2019 at 01:34:03AM +0000, Jethro Beekman wrote:
> On 2019-04-17 03:39, Jarkko Sakkinen wrote:
> > diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
> > index 7bf627ac4958..3b80acde8671 100644
> > --- a/arch/x86/include/uapi/asm/sgx.h
> > +++ b/arch/x86/include/uapi/asm/sgx.h
> > @@ -16,6 +16,8 @@
> > _IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page)
> > #define SGX_IOC_ENCLAVE_INIT \
> > _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
> > +#define SGX_IOC_ENCLAVE_SET_ATTRIBUTE \
> > + _IOW(SGX_MAGIC, 0x03, struct sgx_enclave_set_attribute)
>
> Need to update Documentation/ioctl/ioctl-number.txt as well
Tha patch contains ioctl update. Can you be more specific?
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [RFC PATCH v2 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack
2019-04-26 21:00 ` Sean Christopherson
@ 2019-05-02 8:28 ` Jarkko Sakkinen
0 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-02 8:28 UTC (permalink / raw)
To: Sean Christopherson
Cc: Xing, Cedric, linux-kernel, linux-sgx, akpm, Hansen, Dave, Ayoun,
Serge, Katz-zamir, Shay, Huang, Haitao, Svahn, Kai, Huang, Kai
On Fri, Apr 26, 2019 at 02:00:18PM -0700, Sean Christopherson wrote:
> On Thu, Apr 25, 2019 at 04:31:40PM -0700, Xing, Cedric wrote:
> > > While this may format nicely in .rst (I haven't checked), it's damn near
> > > unreadable in its raw form. Escaping '%' in kernel-doc is unresolved at
> > > this point, but this definitely is an argument for allowing the %CONST
> > > interpretation to be disabled entirely.
> > >
> >
> > I know very little about kernel-doc. Not quite sure which markup means what.
> > Or if you don't mind, could you just write up the whole thing (you have
> > written half of it in your email already) so I can include it into my next
> > revision?
>
> Hmm, at this point I'd say just ignore the comment entirely. It's
> something we need to sort out in the "official" series anyways.
To get started: https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-04-24 12:17 ` Jarkko Sakkinen
@ 2019-05-08 13:45 ` Jarkko Sakkinen
0 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-08 13:45 UTC (permalink / raw)
To: Andy Lutomirski
Cc: LKML, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
Huang, Kai, David Rientjes, linux-sgx-owner
On Wed, Apr 24, 2019 at 03:17:47PM +0300, Jarkko Sakkinen wrote:
> For me easier path to get something done would to do ELF DSO
> first. As you said they both could be done, which means that
> Windows COFF could be upstreamed later on.
>
> If this approach works, it'd mean that no ioctl's would be
> required except SGX_ENCLAVE_SET_ATTRIBUTE.
>
> PS. This quote from LWN got a bit into my feelings:
>
> "After 20 revisions of the patch set over three years, the
> authors of this work (which was posted by Jarkko Sakkinen)
> might well be forgiven for thinking that it must be about
> ready for merging."
>
> I seriously do not make any pre-conclusions ever for any patch
> that I post when it should be merged, no matter how big or
> small :-) For me this is just work...
Just throwing this out of my head so that all options are considered but
wouldn't one alternative to get things right be to replace ioctl with a
syscall? Not endorsing this option in particular but I think you could
get security right by doing this.
Even with dlopen() you need ioctl's for setting attributes (e.g.
provisioning) and EINIT. A syscall would be in some ways more sound.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* RE: [PATCH v20 00/28] Intel SGX1 support
2019-04-22 16:26 ` Andy Lutomirski
2019-04-23 21:15 ` Jethro Beekman
@ 2019-05-10 17:23 ` Xing, Cedric
2019-05-10 17:37 ` Jethro Beekman
1 sibling, 1 reply; 285+ messages in thread
From: Xing, Cedric @ 2019-05-10 17:23 UTC (permalink / raw)
To: Andy Lutomirski, Jethro Beekman
Cc: Thomas Gleixner, Dr. Greg, Hansen, Dave, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
Hi Andy,
> So I think we need a better EADD ioctl that explicitly does work on
> PROT_READ|PROT_EXEC enclave memory but makes up for by validating the
> *source* of the data. The effect will be similar to mapping a
> labeled, appraised, etc file as PROT_EXEC. Maybe, in extreme
> pseudocode:
>
> fd = open(“/dev/sgx/enclave”);
> ioctl(fd, SGX_CREATE_FROM_FILE, file_fd); // fd now inherits the LSM
> label from the file, or is otherwise marked.
> mmap(fd, PROT_READ|PROT_EXEC, ...);
>
> I suppose that an alternative would be to delegate all the EADD calls
> to a privileged daemon, but that’s nasty.
I agree with you that *source* of EPC pages shall be validated. But instead of doing it in the driver, I think an alternative could be to treat an enclave file as a regular shared object so all IMA/LSM checks could be triggered/performed as part of the loading process, then let the driver "copy" those pages to EPC. By "copy", I mean both the page content and _permissions_ are copied, which differs from the current driver's behavior of copying page content only (while permissions are taken from ioctl parameters). That said, if an adversary cannot inject any code into a process in regular pages, then it cannot inject any code to any EPC pages in that process either, simply because the latter depends on the former.
Security wise, it is also assumed that duplicating (both content and permissions of) an existing page within a process will not threaten the security of that process. That assumption may not always be true but in practice, the current LSM architecture doesn't seem to have that in scope, so this proposal I think still aligns with LSM in that sense.
If compared to the idea of "enclave loader inside kernel", I think this alternative is much simpler and more flexible. In particular,
* It requires minimal change to the driver - just take EPCM permissions from source pages' VMA instead of from ioctl parameter.
* It requires little change to user mode enclave loader - just mmap() enclave file in the same way as dlopen() would do, then all IMA/LSM checks applicable to shared objects will be triggered/performed transparently.
* It doesn't assume/tie to specific enclave formats.
* It is extensible - Today every regular page within a process is allowed implicitly to be the source for an EPC page. In future, if at all desirable/necessary, IMA/LSM could be extended to leave a flag somewhere (e.g. in VMA) to indicate explicitly whether a regular page (or range) could be a source for EPC. Then SGX specific hooks/policies could be supported easily.
How do you think?
-Cedric
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 17:23 ` Xing, Cedric
@ 2019-05-10 17:37 ` Jethro Beekman
2019-05-10 17:54 ` Dave Hansen
0 siblings, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-05-10 17:37 UTC (permalink / raw)
To: Xing, Cedric, Andy Lutomirski
Cc: Thomas Gleixner, Dr. Greg, Hansen, Dave, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
[-- Attachment #1: Type: text/plain, Size: 1926 bytes --]
On 2019-05-10 10:23, Xing, Cedric wrote:
> ... I think an alternative could be to treat an enclave file as a regular shared object so all IMA/LSM checks could be triggered/performed as part of the loading process, then let the driver "copy" those pages to EPC. ...
>
> If compared to the idea of "enclave loader inside kernel", I think this alternative is much simpler and more flexible. In particular,
> * It requires minimal change to the driver - just take EPCM permissions from source pages' VMA instead of from ioctl parameter.
> * It requires little change to user mode enclave loader - just mmap() enclave file in the same way as dlopen() would do, then all IMA/LSM checks applicable to shared objects will be triggered/performed transparently.
> * It doesn't assume/tie to specific enclave formats.
It does assume a specific format, namely, that the memory layout
(including page types/permissions) of the enclave can be represented in
a "flat file" on disk, or at least that the enclave memory contents
consist of 4096-byte chunks in that file.
Of the formats I have described in my other email, the only format that
satisfies this property is the "CPU-native (instruction stream)" format
which is not in use today. The ELF/PE formats in use today don't satisfy
this property as the files don't contain the TCS contents. The SGXS
format doesn't satisfy this property because the enclave memory is
represented with 256-byte chunks.
> * It is extensible - Today every regular page within a process is allowed implicitly to be the source for an EPC page. In future, if at all desirable/necessary, IMA/LSM could be extended to leave a flag somewhere (e.g. in VMA) to indicate explicitly whether a regular page (or range) could be a source for EPC. Then SGX specific hooks/policies could be supported easily.
>
> How do you think?
>
> -Cedric
>
--
Jethro Beekman | Fortanix
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 17:37 ` Jethro Beekman
@ 2019-05-10 17:54 ` Dave Hansen
2019-05-10 18:04 ` Jethro Beekman
2019-05-10 18:44 ` Xing, Cedric
0 siblings, 2 replies; 285+ messages in thread
From: Dave Hansen @ 2019-05-10 17:54 UTC (permalink / raw)
To: Jethro Beekman, Xing, Cedric, Andy Lutomirski
Cc: Thomas Gleixner, Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML,
X86 ML, linux-sgx, Andrew Morton, Christopherson, Sean J,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, Andy Shevchenko, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
On 5/10/19 10:37 AM, Jethro Beekman wrote:
> It does assume a specific format, namely, that the memory layout
> (including page types/permissions) of the enclave can be represented in
> a "flat file" on disk, or at least that the enclave memory contents
> consist of 4096-byte chunks in that file.
I _think_ Cedric's point is that, to the kernel,
/lib/x86_64-linux-gnu/libc.so.6 is a "flat file" because the kernel
doesn't have any part in parsing the executable format of a shared library.
I actually don't know how it works, though. Do we just just trust that
the userspace parsing of the .so format is correct? Do we just assume
that any part of a file passing IMA checks can be PROT_EXEC?
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 17:54 ` Dave Hansen
@ 2019-05-10 18:04 ` Jethro Beekman
2019-05-10 18:56 ` Xing, Cedric
2019-05-10 18:44 ` Xing, Cedric
1 sibling, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-05-10 18:04 UTC (permalink / raw)
To: Dave Hansen, Xing, Cedric, Andy Lutomirski
Cc: Thomas Gleixner, Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML,
X86 ML, linux-sgx, Andrew Morton, Christopherson, Sean J,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, Andy Shevchenko, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
[-- Attachment #1: Type: text/plain, Size: 1224 bytes --]
On 2019-05-10 10:54, Dave Hansen wrote:
> On 5/10/19 10:37 AM, Jethro Beekman wrote:
>> It does assume a specific format, namely, that the memory layout
>> (including page types/permissions) of the enclave can be represented in
>> a "flat file" on disk, or at least that the enclave memory contents
>> consist of 4096-byte chunks in that file.
>
> I _think_ Cedric's point is that, to the kernel,
> /lib/x86_64-linux-gnu/libc.so.6 is a "flat file" because the kernel
> doesn't have any part in parsing the executable format of a shared library.
>
> I actually don't know how it works, though. Do we just just trust that
> the userspace parsing of the .so format is correct? Do we just assume
> that any part of a file passing IMA checks can be PROT_EXEC?
>
ELF files are explicitly designed such that you can map them (with mmap)
in 4096-byte chunks. However, sometimes there's overlap and you will
sometimes see that a particular offset is mapped twice because the first
half of the page in the file belongs to an RX range and the second half
to an R-only range. Also, ELF files don't (normally) describe stack,
heap, etc. which you do need for enclaves.
--
Jethro Beekman | Fortanix
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]
^ permalink raw reply [flat|nested] 285+ messages in thread
* RE: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 17:54 ` Dave Hansen
2019-05-10 18:04 ` Jethro Beekman
@ 2019-05-10 18:44 ` Xing, Cedric
1 sibling, 0 replies; 285+ messages in thread
From: Xing, Cedric @ 2019-05-10 18:44 UTC (permalink / raw)
To: Hansen, Dave, Jethro Beekman, Andy Lutomirski
Cc: Thomas Gleixner, Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML,
X86 ML, linux-sgx, Andrew Morton, Christopherson, Sean J,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, Andy Shevchenko, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
Hi Dave,
> On 5/10/19 10:37 AM, Jethro Beekman wrote:
> > It does assume a specific format, namely, that the memory layout
> > (including page types/permissions) of the enclave can be represented
> in
> > a "flat file" on disk, or at least that the enclave memory contents
> > consist of 4096-byte chunks in that file.
>
> I _think_ Cedric's point is that, to the kernel,
> /lib/x86_64-linux-gnu/libc.so.6 is a "flat file" because the kernel
> doesn't have any part in parsing the executable format of a shared
> library.
>
> I actually don't know how it works, though. Do we just just trust that
> the userspace parsing of the .so format is correct? Do we just assume
> that any part of a file passing IMA checks can be PROT_EXEC?
The key idea here is for enclave files to "inherit" the checks applicable to regular shared objects. And how we are going to do it is for user process to map every loadable segment of the enclave file into its address space using *multiple* mmap() calls, just in the same way as dlopen() would do for loading regular shared objects. Then those open()/mmap() syscalls will trigger all applicable checks (by means of security_file_open(), security_mmap_file() and ima_file_mmap() hooks) transparently. That said, IMA/LSM implementations/policies will dictate the success/failure of those open()/mmap() syscalls. And by depending EPCM attributes on permissions of source VMAs, no EXEC page could be ever created unless the source page (which has to be EXEC, btw) has passed IMA/LSM checks (as if it were loaded from a regular shared object file).
-Cedric
^ permalink raw reply [flat|nested] 285+ messages in thread
* RE: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 18:04 ` Jethro Beekman
@ 2019-05-10 18:56 ` Xing, Cedric
2019-05-10 19:04 ` Jethro Beekman
0 siblings, 1 reply; 285+ messages in thread
From: Xing, Cedric @ 2019-05-10 18:56 UTC (permalink / raw)
To: Jethro Beekman, Hansen, Dave, Andy Lutomirski
Cc: Thomas Gleixner, Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML,
X86 ML, linux-sgx, Andrew Morton, Christopherson, Sean J,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, Andy Shevchenko, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
Hi Jethro,
> ELF files are explicitly designed such that you can map them (with mmap)
> in 4096-byte chunks. However, sometimes there's overlap and you will
> sometimes see that a particular offset is mapped twice because the first
> half of the page in the file belongs to an RX range and the second half
> to an R-only range. Also, ELF files don't (normally) describe stack,
> heap, etc. which you do need for enclaves.
You have probably misread my email. By mmap(), I meant the enclave file would be mapped via *multiple* mmap() calls, in the same way as what dlopen() would do in loading regular shared object. The intention here is to make the enclave file subject to the same checks as regular shared objects.
The other enclave components (e.g. TCS, heap, stack, etc.) will be created from writable pages within the calling process's address space as before. Similar to heaps/stacks in a regular process, those components don't have to be backed by disk files.
The core idea is for SGX driver to "copy" not only the content but also permissions from a source page to the target EPC page. Given the existence of the source page, it can be concluded that the combination of content and permissions of that page has passed all IMA/LSM checks applicable to that page, hence "copying" that page to EPC with the same (or less) permission is *not* a circumvention of IMA/LSM policies.
-Cedric
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 18:56 ` Xing, Cedric
@ 2019-05-10 19:04 ` Jethro Beekman
2019-05-10 19:22 ` Andy Lutomirski
0 siblings, 1 reply; 285+ messages in thread
From: Jethro Beekman @ 2019-05-10 19:04 UTC (permalink / raw)
To: Xing, Cedric, Hansen, Dave, Andy Lutomirski
Cc: Thomas Gleixner, Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML,
X86 ML, linux-sgx, Andrew Morton, Christopherson, Sean J,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, Andy Shevchenko, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
[-- Attachment #1: Type: text/plain, Size: 1050 bytes --]
On 2019-05-10 11:56, Xing, Cedric wrote:
> Hi Jethro,
>
>> ELF files are explicitly designed such that you can map them (with mmap)
>> in 4096-byte chunks. However, sometimes there's overlap and you will
>> sometimes see that a particular offset is mapped twice because the first
>> half of the page in the file belongs to an RX range and the second half
>> to an R-only range. Also, ELF files don't (normally) describe stack,
>> heap, etc. which you do need for enclaves.
>
> You have probably misread my email. By mmap(), I meant the enclave file would be mapped via *multiple* mmap() calls, in the same way as what dlopen() would do in loading regular shared object. The intention here is to make the enclave file subject to the same checks as regular shared objects.
No, I didn't misread your email. My original point still stands:
requiring that an enclave's memory is created from one or more mmap
calls of a file puts significant restrictions on the enclave's on-disk
representation.
--
Jethro Beekman | Fortanix
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 19:04 ` Jethro Beekman
@ 2019-05-10 19:22 ` Andy Lutomirski
2019-05-11 1:06 ` Xing, Cedric
` (2 more replies)
0 siblings, 3 replies; 285+ messages in thread
From: Andy Lutomirski @ 2019-05-10 19:22 UTC (permalink / raw)
To: Jethro Beekman
Cc: Xing, Cedric, Hansen, Dave, Andy Lutomirski, Thomas Gleixner,
Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, Christopherson, Sean J, nhorman,
npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao,
Andy Shevchenko, Svahn, Kai, Borislav Petkov, Josh Triplett,
Huang, Kai, David Rientjes
On Fri, May 10, 2019 at 12:04 PM Jethro Beekman <jethro@fortanix.com> wrote:
>
> On 2019-05-10 11:56, Xing, Cedric wrote:
> > Hi Jethro,
> >
> >> ELF files are explicitly designed such that you can map them (with mmap)
> >> in 4096-byte chunks. However, sometimes there's overlap and you will
> >> sometimes see that a particular offset is mapped twice because the first
> >> half of the page in the file belongs to an RX range and the second half
> >> to an R-only range. Also, ELF files don't (normally) describe stack,
> >> heap, etc. which you do need for enclaves.
> >
> > You have probably misread my email. By mmap(), I meant the enclave file would be mapped via *multiple* mmap() calls, in the same way as what dlopen() would do in loading regular shared object. The intention here is to make the enclave file subject to the same checks as regular shared objects.
>
> No, I didn't misread your email. My original point still stands:
> requiring that an enclave's memory is created from one or more mmap
> calls of a file puts significant restrictions on the enclave's on-disk
> representation.
>
For a tiny bit of background, Linux (AFAIK*) makes no effort to ensure
the complete integrity of DSOs. What Linux *does* do (if so
configured) is to make sure that only approved data is mapped
executable. So, if you want to have some bytes be executable, those
bytes have to come from a file that passes the relevant LSM and IMA
checks. So we have two valid approaches, I think.
Approach 1: we treat SGX exactly the same way and make it so that only
bytes that pass the relevant checks can be mapped as code within an
enclave. This imposes no particular restrictions on the file format
-- we just need some API that takes an fd, an offset, and a length,
and adds those bytes as code to an enclave. (It could also take a
pointer and a length and make sure that the pointer points to
executable memory -- same effect.)
Approach 2: we decide that we want a stronger guarantee and that we
*will* ensure the integrity of the enclave. This means:
2a) that we either need to load the entire thing from some approved
file, and we commit to supporting one or more file formats.
2b) we need to check that the eventual enclave hash is approved. Or
we could have a much shorter file that is just the hash and we check
that. At its simplest, the file could be *only* the hash, and there
could be an LSM callback to check it. In the future, if someone wants
to allow enclaves to be embedded in DSOs, we could have a special ELF
note or similar that contains an enclave hash or similar.
2c) same as 2b except that we expose the whole SIGSTRUCT, not just the hash.
Here are some pros and cons of various bits:
1 and 2a allow anti-virus software to scan the enclave code, and 2a
allows it to scan the whole enclave. I don't know if this is actually
imporant.
2a is by far the most complicated kernel implementation.
2b and 2c are almost file-format agnostic. 1 is completely file
format agnostic but, in exchange, it's much weaker.
2b and 2c should solve most (but not all) of the launch control
complaints that Dr. Greg cares about, in the sense that the LSM policy
quite literally validates that the enclave is approved.
As a straw man design, I propose the following, which is mostly 2c.
The whole loading process works almost as in Jarkko's current driver,
but the actual ioctl that triggers EINIT changes. When you issue the
ioctl, you pass in an fd and the SIGSTRUCT is loaded and checked from
the fd. The idea is that software that ships an enclave will ship a
.sgxsig file that is literally a SIGSTRUCT for the enclave. With
SELinux, that file gets labeled something like
sgx_enclave_sigstruct_t. And we have the following extra twist: if
you're calling the EADD ioctl to add *code* to the enclave, the driver
checks that the code being loaded is mapped executable. This way
existing code-signing policies don't get subverted, and policies that
want to impose full verification on the enclave can do so by verifying
the .sigstruct file.
What do you all think?
* It's certainly the case that Linux does not *succeed* at preserving
the overall integrity of shared objects. If nothing else, you can
freely mremap() them however you like. And you can jump into them
wherever you like.
^ permalink raw reply [flat|nested] 285+ messages in thread
* RE: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 19:22 ` Andy Lutomirski
@ 2019-05-11 1:06 ` Xing, Cedric
2019-05-14 15:08 ` Andy Lutomirski
[not found] ` <20190513102926.GD8743@linux.intel.com>
2019-05-14 14:33 ` Haitao Huang
2 siblings, 1 reply; 285+ messages in thread
From: Xing, Cedric @ 2019-05-11 1:06 UTC (permalink / raw)
To: Andy Lutomirski, Jethro Beekman
Cc: Hansen, Dave, Thomas Gleixner, Dr. Greg, Jarkko Sakkinen,
Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
Hi Andy and Jethro,
> > > You have probably misread my email. By mmap(), I meant the enclave
> file would be mapped via *multiple* mmap() calls, in the same way as
> what dlopen() would do in loading regular shared object. The intention
> here is to make the enclave file subject to the same checks as regular
> shared objects.
> >
> > No, I didn't misread your email. My original point still stands:
> > requiring that an enclave's memory is created from one or more mmap
> > calls of a file puts significant restrictions on the enclave's on-disk
> > representation.
> >
I was talking in the context of ELF, with the assumption that changing RW pages to RX is disallowed by LSM. And in that case mmap()would be the only way to load a page from disk without having to "write" to it. But that's just an example but not the focus of the discussion.
The point I was trying to make was, that the driver is going to copy both content and permissions from the source so the security properties established (by IMA/LSM) around that source page would be carried onto the EPC page being EADD'ed. The driver doesn't care how that source page came into existence. It could be mapped from an ELF file as in the example, or it could be a result from JIT as long as LSM allows it. The driver will be file format agnostic.
>
> For a tiny bit of background, Linux (AFAIK*) makes no effort to ensure
> the complete integrity of DSOs. What Linux *does* do (if so
> configured) is to make sure that only approved data is mapped
> executable. So, if you want to have some bytes be executable, those
> bytes have to come from a file that passes the relevant LSM and IMA
> checks. So we have two valid approaches, I think.
>
> Approach 1: we treat SGX exactly the same way and make it so that only
> bytes that pass the relevant checks can be mapped as code within an
> enclave. This imposes no particular restrictions on the file format
> -- we just need some API that takes an fd, an offset, and a length,
> and adds those bytes as code to an enclave. (It could also take a
> pointer and a length and make sure that the pointer points to
> executable memory -- same effect.)
I assume "some API" is some user mode API so this approach is the same as what I suggested in my last email. Am I correct?
>
> Approach 2: we decide that we want a stronger guarantee and that we
> *will* ensure the integrity of the enclave. This means:
>
> 2a) that we either need to load the entire thing from some approved
> file, and we commit to supporting one or more file formats.
>
> 2b) we need to check that the eventual enclave hash is approved. Or
> we could have a much shorter file that is just the hash and we check
> that. At its simplest, the file could be *only* the hash, and there
> could be an LSM callback to check it. In the future, if someone wants
> to allow enclaves to be embedded in DSOs, we could have a special ELF
> note or similar that contains an enclave hash or similar.
>
> 2c) same as 2b except that we expose the whole SIGSTRUCT, not just the
> hash.
>
> Here are some pros and cons of various bits:
>
> 1 and 2a allow anti-virus software to scan the enclave code, and 2a
> allows it to scan the whole enclave. I don't know if this is actually
> imporant.
I guess anti-virus software can scan any enclave file in *all* cases as long as it understands the format of that enclave. It doesn't necessary mean the kernel has to understand that enclave format (as enclave file could be parsed in user mode) or the anti-virus software has to understand all formats (if any) supported natively by the kernel.
>
> 2a is by far the most complicated kernel implementation.
>
Agreed. I don't see any reason 2a would be necessary.
> 2b and 2c are almost file-format agnostic. 1 is completely file
> format agnostic but, in exchange, it's much weaker.
I'd say 1 and (variants of) 2 are orthogonal. SGX always enforces integrities so not doing integrity checks at EADD doesn't mean the enclave integrity is not being enforced. 1 and 2 are basically 2 different checkpoints where LSM hooks could be placed. And a given LSM implementation/policy may enforce either 1 or 2, or both, or neither.
>
> 2b and 2c should solve most (but not all) of the launch control
> complaints that Dr. Greg cares about, in the sense that the LSM policy
> quite literally validates that the enclave is approved.
>
> As a straw man design, I propose the following, which is mostly 2c.
> The whole loading process works almost as in Jarkko's current driver,
> but the actual ioctl that triggers EINIT changes. When you issue the
> ioctl, you pass in an fd and the SIGSTRUCT is loaded and checked from
> the fd. The idea is that software that ships an enclave will ship a
> .sgxsig file that is literally a SIGSTRUCT for the enclave. With
> SELinux, that file gets labeled something like
> sgx_enclave_sigstruct_t. And we have the following extra twist: if
> you're calling the EADD ioctl to add *code* to the enclave, the driver
> checks that the code being loaded is mapped executable. This way
> existing code-signing policies don't get subverted, and policies that
> want to impose full verification on the enclave can do so by verifying
> the .sigstruct file.
I'm with you that it's desirable/necessary to add an LSM hook at EINIT, but don't see the need for .sigstruct file or its fd as input to EINIT ioctl.
Generally speaking, LSM needs to decide whether or not to launch the enclave in question. And that decision could be based upon either the enclave itself (i.e. bytes comprising the enclave, or its MRENCLAVE, or its signature, all equivalent from cryptographic standpoint), or some external attributes associated with the enclave (e.g. DAC/MAC context associated with the enclave file), or both. In the former case, what matters is the content of the SIGSTRUCT but not where it came from; while in the latter case it could be gated at open() syscall so that no fd to SIGSTRUCT (or the enclave image file) could ever be obtained by the calling process if it was not allowed to launch that enclave at all. In either case, no fd is necessary to be passed to EINIT ioctl. That said, by providing a SIGSTRUCT to EINIT ioctl, the calling process has implicitly proven its access to needed file(s) at the file system level, so only the content (i.e. MRENCLAVE or signing key) of the SIGSTRUCT needs to be checked by LSM, while the integrity of the enclave will be enforced by SGX hardware.
>
> What do you all think?
>
I think approach 1 and (variants of) 2 are orthogonal so I wouldn't skip either to make the other mandatory from architecture perspective; while an LSM policy may opt to enforce either one, or both.
Putting everything together, I'd suggest to:
- Change EADD ioctl to take source page's VMA permission as ("upper bound" of) EPCM permission. This make sure no one can circumvent LSM to generate executable code on the fly using SGX driver.
- Change EINIT ioctl to invoke (new?) LSM hook to validate SIGSTRUCT before issuing EINIT.
> * It's certainly the case that Linux does not *succeed* at preserving
> the overall integrity of shared objects. If nothing else, you can
> freely mremap() them however you like. And you can jump into them
> wherever you like.
-Cedric
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
[not found] ` <20190513102926.GD8743@linux.intel.com>
@ 2019-05-14 10:43 ` Jarkko Sakkinen
2019-05-14 15:13 ` Andy Lutomirski
0 siblings, 1 reply; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-14 10:43 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jethro Beekman, Xing, Cedric, Hansen, Dave, Thomas Gleixner,
Dr. Greg, Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Mon, May 13, 2019 at 01:29:26PM +0300, Jarkko Sakkinen wrote:
> I did study through SDK's file format and realized that it does not
> does make sense after all to embed one.
>
> To implement it properly you would probably need a new syscall (lets say
> sgx_load_enclave) and also that enclaves are not just executables
> binaries. It is hard to find a generic format for them as applications
> range from simply protecting part of an application to running a
> containter inside enclave.
When I looked at SDK's data structures embedded to the ELF they contain
data that is also used by the run-time. Thus, run-time would anyway need
to also parse the file containing the enclave.
There is not same way a single rigid structure of what executable means
as for normal application and in many cases enclave could be just used
to seal a portion of the code. In extreme case you might construct
enclave even on run-time.
I think SIGSTRUCT in a file is the right choice. It is the lowest common
denominator for enclaves that locks in its contents and is same for any
enclave. Practicing access control to that file should be enough to
define whatever security policy required.
I'm still puzzling what kind of changes you were discussing considering
SGX_IOC_ENCLAVE_ADD_PAGE.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-10 19:22 ` Andy Lutomirski
2019-05-11 1:06 ` Xing, Cedric
[not found] ` <20190513102926.GD8743@linux.intel.com>
@ 2019-05-14 14:33 ` Haitao Huang
2019-05-14 15:17 ` Andy Lutomirski
2 siblings, 1 reply; 285+ messages in thread
From: Haitao Huang @ 2019-05-14 14:33 UTC (permalink / raw)
To: Jethro Beekman, Andy Lutomirski
Cc: Xing, Cedric, Hansen, Dave, Thomas Gleixner, Dr. Greg,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On Fri, 10 May 2019 14:22:34 -0500, Andy Lutomirski <luto@kernel.org>
wrote:
> On Fri, May 10, 2019 at 12:04 PM Jethro Beekman <jethro@fortanix.com>
> wrote:
>>
>> On 2019-05-10 11:56, Xing, Cedric wrote:
>> > Hi Jethro,
>> >
>> >> ELF files are explicitly designed such that you can map them (with
>> mmap)
>> >> in 4096-byte chunks. However, sometimes there's overlap and you will
>> >> sometimes see that a particular offset is mapped twice because the
>> first
>> >> half of the page in the file belongs to an RX range and the second
>> half
>> >> to an R-only range. Also, ELF files don't (normally) describe stack,
>> >> heap, etc. which you do need for enclaves.
>> >
>> > You have probably misread my email. By mmap(), I meant the enclave
>> file would be mapped via *multiple* mmap() calls, in the same way as
>> what dlopen() would do in loading regular shared object. The intention
>> here is to make the enclave file subject to the same checks as regular
>> shared objects.
>>
>> No, I didn't misread your email. My original point still stands:
>> requiring that an enclave's memory is created from one or more mmap
>> calls of a file puts significant restrictions on the enclave's on-disk
>> representation.
>>
>
> For a tiny bit of background, Linux (AFAIK*) makes no effort to ensure
> the complete integrity of DSOs. What Linux *does* do (if so
> configured) is to make sure that only approved data is mapped
> executable. So, if you want to have some bytes be executable, those
> bytes have to come from a file that passes the relevant LSM and IMA
> checks.
Given this, I just want to step back a little to understand the exact
issue that SGX is causing here for LSM/IMA. Sorry if I missed points
discussed earlier.
By the time of EADD, enclave file is opened and should have passed IMA and
SELinux policy enforcement gates if any. We really don't need extra mmaps
on the enclave files to be IMA and SELinux compliant. We are loading
enclave files as RO and copying those into EPC. An IMA policy can enforce
RO files (or any file). And SELinux policy can say which processes can
open the file for what permissions. No extra needed here.
And sgx enclaves are always signed and integrity protected and verified at
the time of EINIT. So if EINIT passes, we know the content loaded
(including permission flags) is matching the sigstruct. But
sigstruct/signature is part of the file, should be accounted for in IMA
measurement of the whole file, so it is also verified by IMA during file
open, right?
The only potential gap/difference comparing to regular ELF executable or
DSOs:for enclaves, we need mmap portions of enclave linear range with RW
to do EADD IOC, then mprotect those pages to RX after EINIT. But this is
operated on enclave fd provided by driver. So we can have an SELinux
policy say: only this type of processes is allowed to open enclave fd, and
allowed to do mmap/mprotect with read, write, execute on it. Wouldn't that
be enough?
Thanks
Haitao
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-11 1:06 ` Xing, Cedric
@ 2019-05-14 15:08 ` Andy Lutomirski
2019-05-15 8:31 ` Jarkko Sakkinen
0 siblings, 1 reply; 285+ messages in thread
From: Andy Lutomirski @ 2019-05-14 15:08 UTC (permalink / raw)
To: Xing, Cedric
Cc: Andy Lutomirski, Jethro Beekman, Hansen, Dave, Thomas Gleixner,
Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, Christopherson, Sean J, nhorman,
npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao,
Andy Shevchenko, Svahn, Kai, Borislav Petkov, Josh Triplett,
Huang, Kai, David Rientjes
On Fri, May 10, 2019 at 6:06 PM Xing, Cedric <cedric.xing@intel.com> wrote:
>
> Hi Andy and Jethro,
>
> > > > You have probably misread my email. By mmap(), I meant the enclave
> > file would be mapped via *multiple* mmap() calls, in the same way as
> > what dlopen() would do in loading regular shared object. The intention
> > here is to make the enclave file subject to the same checks as regular
> > shared objects.
> > >
> > > No, I didn't misread your email. My original point still stands:
> > > requiring that an enclave's memory is created from one or more mmap
> > > calls of a file puts significant restrictions on the enclave's on-disk
> > > representation.
> > >
>
> I was talking in the context of ELF, with the assumption that changing RW pages to RX is disallowed by LSM. And in that case mmap()would be the only way to load a page from disk without having to "write" to it. But that's just an example but not the focus of the discussion.
>
> The point I was trying to make was, that the driver is going to copy both content and permissions from the source so the security properties established (by IMA/LSM) around that source page would be carried onto the EPC page being EADD'ed. The driver doesn't care how that source page came into existence. It could be mapped from an ELF file as in the example, or it could be a result from JIT as long as LSM allows it. The driver will be file format agnostic.
>
> >
> > For a tiny bit of background, Linux (AFAIK*) makes no effort to ensure
> > the complete integrity of DSOs. What Linux *does* do (if so
> > configured) is to make sure that only approved data is mapped
> > executable. So, if you want to have some bytes be executable, those
> > bytes have to come from a file that passes the relevant LSM and IMA
> > checks. So we have two valid approaches, I think.
> >
> > Approach 1: we treat SGX exactly the same way and make it so that only
> > bytes that pass the relevant checks can be mapped as code within an
> > enclave. This imposes no particular restrictions on the file format
> > -- we just need some API that takes an fd, an offset, and a length,
> > and adds those bytes as code to an enclave. (It could also take a
> > pointer and a length and make sure that the pointer points to
> > executable memory -- same effect.)
>
> I assume "some API" is some user mode API so this approach is the same as what I suggested in my last email. Am I correct?
I meant kernel, but SGX_IOC_ADD_ENCLAVE_PAGE could be that API.
The only benefit I can see to making the kernel API take an fd instead
of a pointer is that it allows loading an enclave without mapping it
first, but that seems unlikely to be very important.
>
> >
> > Approach 2: we decide that we want a stronger guarantee and that we
> > *will* ensure the integrity of the enclave. This means:
> >
> > 2a) that we either need to load the entire thing from some approved
> > file, and we commit to supporting one or more file formats.
> >
> > 2b) we need to check that the eventual enclave hash is approved. Or
> > we could have a much shorter file that is just the hash and we check
> > that. At its simplest, the file could be *only* the hash, and there
> > could be an LSM callback to check it. In the future, if someone wants
> > to allow enclaves to be embedded in DSOs, we could have a special ELF
> > note or similar that contains an enclave hash or similar.
> >
> > 2c) same as 2b except that we expose the whole SIGSTRUCT, not just the
> > hash.
> >
> > Here are some pros and cons of various bits:
> >
> > 1 and 2a allow anti-virus software to scan the enclave code, and 2a
> > allows it to scan the whole enclave. I don't know if this is actually
> > imporant.
>
> I guess anti-virus software can scan any enclave file in *all* cases as long as it understands the format of that enclave. It doesn't necessary mean the kernel has to understand that enclave format (as enclave file could be parsed in user mode) or the anti-virus software has to understand all formats (if any) supported natively by the kernel.
True, but in 2a it can be scanned even without understanding the
format. That's probably not terribly important.
>
> >
> > 2a is by far the most complicated kernel implementation.
> >
>
> Agreed. I don't see any reason 2a would be necessary.
>
> > 2b and 2c are almost file-format agnostic. 1 is completely file
> > format agnostic but, in exchange, it's much weaker.
>
> I'd say 1 and (variants of) 2 are orthogonal. SGX always enforces integrities so not doing integrity checks at EADD doesn't mean the enclave integrity is not being enforced. 1 and 2 are basically 2 different checkpoints where LSM hooks could be placed. And a given LSM implementation/policy may enforce either 1 or 2, or both, or neither.
They're not orthogonal in the sense that verifying the SIGSTRUCT
implicitly verifies the executable contents.
>
> >
> > 2b and 2c should solve most (but not all) of the launch control
> > complaints that Dr. Greg cares about, in the sense that the LSM policy
> > quite literally validates that the enclave is approved.
> >
> > As a straw man design, I propose the following, which is mostly 2c.
> > The whole loading process works almost as in Jarkko's current driver,
> > but the actual ioctl that triggers EINIT changes. When you issue the
> > ioctl, you pass in an fd and the SIGSTRUCT is loaded and checked from
> > the fd. The idea is that software that ships an enclave will ship a
> > .sgxsig file that is literally a SIGSTRUCT for the enclave. With
> > SELinux, that file gets labeled something like
> > sgx_enclave_sigstruct_t. And we have the following extra twist: if
> > you're calling the EADD ioctl to add *code* to the enclave, the driver
> > checks that the code being loaded is mapped executable. This way
> > existing code-signing policies don't get subverted, and policies that
> > want to impose full verification on the enclave can do so by verifying
> > the .sigstruct file.
>
> I'm with you that it's desirable/necessary to add an LSM hook at EINIT, but don't see the need for .sigstruct file or its fd as input to EINIT ioctl.
>
> Generally speaking, LSM needs to decide whether or not to launch the enclave in question. And that decision could be based upon either the enclave itself (i.e. bytes comprising the enclave, or its MRENCLAVE, or its signature, all equivalent from cryptographic standpoint), or some external attributes associated with the enclave (e.g. DAC/MAC context associated with the enclave file), or both. In the former case, what matters is the content of the SIGSTRUCT but not where it came from; while in the latter case it could be gated at open() syscall so that no fd to SIGSTRUCT (or the enclave image file) could ever be obtained by the calling process if it was not allowed to launch that enclave at all. In either case, no fd is necessary to be passed to EINIT ioctl. That said, by providing a SIGSTRUCT to EINIT ioctl, the calling process has implicitly proven its access to needed file(s) at the file system level, so only the content (i.e. MRENCLAVE or signing key) of the SIGSTRUCT needs to be checked by LSM, while the integrity of the enclave will be enforced by SGX hardware.
>
I agree with you except that there's a difference in how the
configuration works. If the whole .sigstruct is in a file, then
teaching your policy to accept an enclave works just like teaching
your policy to accept a new ELF program -- you just put it in a file
and mark the file trusted (by labeling it, for example). If the
.sigstruct comes from unverifiable application memory, then an
out-of-band mechanism will likely be needed.
> Putting everything together, I'd suggest to:
> - Change EADD ioctl to take source page's VMA permission as ("upper bound" of) EPCM permission. This make sure no one can circumvent LSM to generate executable code on the fly using SGX driver.
> - Change EINIT ioctl to invoke (new?) LSM hook to validate SIGSTRUCT before issuing EINIT.
I'm okay with this if the consensus is that having a .sigstruct file
is too annoying.
(Note that a .sigstruct file does not, strictly speaking, require that
it comes from a real file. It could come from memfd, and it will then
fail to load if the LSM doesn't like it. So you can still easily
download and run an enclave if you think that's a good idea.)
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 10:43 ` Jarkko Sakkinen
@ 2019-05-14 15:13 ` Andy Lutomirski
2019-05-14 20:45 ` Sean Christopherson
2019-05-15 8:49 ` Jarkko Sakkinen
0 siblings, 2 replies; 285+ messages in thread
From: Andy Lutomirski @ 2019-05-14 15:13 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Andy Lutomirski, Jethro Beekman, Xing, Cedric, Hansen, Dave,
Thomas Gleixner, Dr. Greg, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, Christopherson, Sean J, nhorman,
npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao,
Andy Shevchenko, Svahn, Kai, Borislav Petkov, Josh Triplett,
Huang, Kai, David Rientjes
On Tue, May 14, 2019 at 3:43 AM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
>
> On Mon, May 13, 2019 at 01:29:26PM +0300, Jarkko Sakkinen wrote:
> > I did study through SDK's file format and realized that it does not
> > does make sense after all to embed one.
> >
> > To implement it properly you would probably need a new syscall (lets say
> > sgx_load_enclave) and also that enclaves are not just executables
> > binaries. It is hard to find a generic format for them as applications
> > range from simply protecting part of an application to running a
> > containter inside enclave.
>
> I'm still puzzling what kind of changes you were discussing considering
> SGX_IOC_ENCLAVE_ADD_PAGE.
I think it's as simple as requiring that, if SECINFO.X is set, then
the src pointer points to the appropriate number of bytes of
executable memory. (Unless there's some way for an enclave to change
SECINFO after the fact -- is there?) Sadly, we don't really have the
a nice in-kernel API for that right now. You could do
down_read(mmap_sem) and find_vma(). Arguably there is no value to
checking that PKRU allows execute to the data.
Hey, Dave, if you're still paying attention to this thread, should we
have copy_from_user_exec() that does the right thing wrt the page
permissions and PKRU.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 14:33 ` Haitao Huang
@ 2019-05-14 15:17 ` Andy Lutomirski
2019-05-14 15:30 ` Haitao Huang
0 siblings, 1 reply; 285+ messages in thread
From: Andy Lutomirski @ 2019-05-14 15:17 UTC (permalink / raw)
To: Haitao Huang
Cc: Jethro Beekman, Andy Lutomirski, Xing, Cedric, Hansen, Dave,
Thomas Gleixner, Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML,
X86 ML, linux-sgx, Andrew Morton, Christopherson, Sean J,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, Andy Shevchenko, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
On Tue, May 14, 2019 at 7:33 AM Haitao Huang
<haitao.huang@linux.intel.com> wrote:
>
> On Fri, 10 May 2019 14:22:34 -0500, Andy Lutomirski <luto@kernel.org>
> wrote:
>
> > On Fri, May 10, 2019 at 12:04 PM Jethro Beekman <jethro@fortanix.com>
> > wrote:
> >>
> >> On 2019-05-10 11:56, Xing, Cedric wrote:
> >> > Hi Jethro,
> >> >
> >> >> ELF files are explicitly designed such that you can map them (with
> >> mmap)
> >> >> in 4096-byte chunks. However, sometimes there's overlap and you will
> >> >> sometimes see that a particular offset is mapped twice because the
> >> first
> >> >> half of the page in the file belongs to an RX range and the second
> >> half
> >> >> to an R-only range. Also, ELF files don't (normally) describe stack,
> >> >> heap, etc. which you do need for enclaves.
> >> >
> >> > You have probably misread my email. By mmap(), I meant the enclave
> >> file would be mapped via *multiple* mmap() calls, in the same way as
> >> what dlopen() would do in loading regular shared object. The intention
> >> here is to make the enclave file subject to the same checks as regular
> >> shared objects.
> >>
> >> No, I didn't misread your email. My original point still stands:
> >> requiring that an enclave's memory is created from one or more mmap
> >> calls of a file puts significant restrictions on the enclave's on-disk
> >> representation.
> >>
> >
> > For a tiny bit of background, Linux (AFAIK*) makes no effort to ensure
> > the complete integrity of DSOs. What Linux *does* do (if so
> > configured) is to make sure that only approved data is mapped
> > executable. So, if you want to have some bytes be executable, those
> > bytes have to come from a file that passes the relevant LSM and IMA
> > checks.
>
> Given this, I just want to step back a little to understand the exact
> issue that SGX is causing here for LSM/IMA. Sorry if I missed points
> discussed earlier.
>
> By the time of EADD, enclave file is opened and should have passed IMA and
> SELinux policy enforcement gates if any. We really don't need extra mmaps
> on the enclave files to be IMA and SELinux compliant.
The problem, as i see it, is that they passed the *wrong* checks,
because, as you noticed:
> We are loading
> enclave files as RO and copying those into EPC.
Which is, semantically, a lot like loading a normal file as RO and
then mprotecting() it to RX, which is disallowed under quite a few LSM
policies.
> An IMA policy can enforce
> RO files (or any file). And SELinux policy can say which processes can
> open the file for what permissions. No extra needed here.
If SELinux says a process may open a file as RO, that does *not* mean
that it can be opened as RX.
>
> And sgx enclaves are always signed and integrity protected and verified at
> the time of EINIT. So if EINIT passes, we know the content loaded
> (including permission flags) is matching the sigstruct. But
> sigstruct/signature is part of the file, should be accounted for in IMA
> measurement of the whole file, so it is also verified by IMA during file
> open, right?
This does work, but only if the kernel parses that file so that the
kernel can trust that the enclave data actually came from the file as
intended. And moving the parsing to the kernel seems like a mess that
no one really wants to do.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 15:17 ` Andy Lutomirski
@ 2019-05-14 15:30 ` Haitao Huang
2019-05-14 20:45 ` Andy Lutomirski
0 siblings, 1 reply; 285+ messages in thread
From: Haitao Huang @ 2019-05-14 15:30 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jethro Beekman, Xing, Cedric, Hansen, Dave, Thomas Gleixner,
Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, Christopherson, Sean J, nhorman,
npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao,
Andy Shevchenko, Svahn, Kai, Borislav Petkov, Josh Triplett,
Huang, Kai, David Rientjes
On Tue, 14 May 2019 10:17:29 -0500, Andy Lutomirski <luto@kernel.org>
wrote:
> On Tue, May 14, 2019 at 7:33 AM Haitao Huang
> <haitao.huang@linux.intel.com> wrote:
>>
>> On Fri, 10 May 2019 14:22:34 -0500, Andy Lutomirski <luto@kernel.org>
>> wrote:
>>
>> > On Fri, May 10, 2019 at 12:04 PM Jethro Beekman <jethro@fortanix.com>
>> > wrote:
>> >>
>> >> On 2019-05-10 11:56, Xing, Cedric wrote:
>> >> > Hi Jethro,
>> >> >
>> >> >> ELF files are explicitly designed such that you can map them (with
>> >> mmap)
>> >> >> in 4096-byte chunks. However, sometimes there's overlap and you
>> will
>> >> >> sometimes see that a particular offset is mapped twice because the
>> >> first
>> >> >> half of the page in the file belongs to an RX range and the second
>> >> half
>> >> >> to an R-only range. Also, ELF files don't (normally) describe
>> stack,
>> >> >> heap, etc. which you do need for enclaves.
>> >> >
>> >> > You have probably misread my email. By mmap(), I meant the enclave
>> >> file would be mapped via *multiple* mmap() calls, in the same way as
>> >> what dlopen() would do in loading regular shared object. The
>> intention
>> >> here is to make the enclave file subject to the same checks as
>> regular
>> >> shared objects.
>> >>
>> >> No, I didn't misread your email. My original point still stands:
>> >> requiring that an enclave's memory is created from one or more mmap
>> >> calls of a file puts significant restrictions on the enclave's
>> on-disk
>> >> representation.
>> >>
>> >
>> > For a tiny bit of background, Linux (AFAIK*) makes no effort to ensure
>> > the complete integrity of DSOs. What Linux *does* do (if so
>> > configured) is to make sure that only approved data is mapped
>> > executable. So, if you want to have some bytes be executable, those
>> > bytes have to come from a file that passes the relevant LSM and IMA
>> > checks.
>>
>> Given this, I just want to step back a little to understand the exact
>> issue that SGX is causing here for LSM/IMA. Sorry if I missed points
>> discussed earlier.
>>
>> By the time of EADD, enclave file is opened and should have passed IMA
>> and
>> SELinux policy enforcement gates if any. We really don't need extra
>> mmaps
>> on the enclave files to be IMA and SELinux compliant.
>
> The problem, as i see it, is that they passed the *wrong* checks,
> because, as you noticed:
>
>> We are loading
>> enclave files as RO and copying those into EPC.
>
> Which is, semantically, a lot like loading a normal file as RO and
> then mprotecting() it to RX, which is disallowed under quite a few LSM
> policies.
>
>> An IMA policy can enforce
>> RO files (or any file). And SELinux policy can say which processes can
>> open the file for what permissions. No extra needed here.
>
> If SELinux says a process may open a file as RO, that does *not* mean
> that it can be opened as RX.
>
But in this case, file itself is mapped as RO treated like data and it is
not for execution. SGX enclave pages have EPCM enforced permissions. So
from SELinux point of view I would think it can treat it as RO and that's
fine.
>>
>> And sgx enclaves are always signed and integrity protected and verified
>> at
>> the time of EINIT. So if EINIT passes, we know the content loaded
>> (including permission flags) is matching the sigstruct. But
>> sigstruct/signature is part of the file, should be accounted for in IMA
>> measurement of the whole file, so it is also verified by IMA during file
>> open, right?
>
> This does work, but only if the kernel parses that file so that the
> kernel can trust that the enclave data actually came from the file as
> intended. And moving the parsing to the kernel seems like a mess that
> no one really wants to do.
If kernel only needs to know the source bytes are from a file that passed
IMA integrity and SELinux RO enforcement, then it can just check if the
source pointer belongs to a VMA with valid fd and no parsing or checking
permissions needed.
Understood if you want to make enclave file code segment stick to the RX
semantics mentioned above, then this doesn't qualify.
Thanks
Haitao
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 15:13 ` Andy Lutomirski
@ 2019-05-14 20:45 ` Sean Christopherson
2019-05-14 21:27 ` Andy Lutomirski
2019-05-15 10:35 ` [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
2019-05-15 8:49 ` Jarkko Sakkinen
1 sibling, 2 replies; 285+ messages in thread
From: Sean Christopherson @ 2019-05-14 20:45 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jarkko Sakkinen, Jethro Beekman, Xing, Cedric, Hansen, Dave,
Thomas Gleixner, Dr. Greg, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Tue, May 14, 2019 at 08:13:36AM -0700, Andy Lutomirski wrote:
> On Tue, May 14, 2019 at 3:43 AM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> >
> > On Mon, May 13, 2019 at 01:29:26PM +0300, Jarkko Sakkinen wrote:
> > > I did study through SDK's file format and realized that it does not
> > > does make sense after all to embed one.
> > >
> > > To implement it properly you would probably need a new syscall (lets say
> > > sgx_load_enclave) and also that enclaves are not just executables
> > > binaries. It is hard to find a generic format for them as applications
> > > range from simply protecting part of an application to running a
> > > containter inside enclave.
> >
> > I'm still puzzling what kind of changes you were discussing considering
> > SGX_IOC_ENCLAVE_ADD_PAGE.
>
> I think it's as simple as requiring that, if SECINFO.X is set, then
> the src pointer points to the appropriate number of bytes of
> executable memory. (Unless there's some way for an enclave to change
> SECINFO after the fact -- is there?)
Nit: SECINFO is just the struct passed to EADD, I think what you're really
asking is "can the EPCM permissions be changed after the fact".
And the answer is, yes.
On SGX2 hardware, the enclave can extend the EPCM permissions at runtime
via ENCLU[EMODPE], e.g. to make a page writable.
Hardware also doesn't prevent doing EADD to the same virtual address
multiple times, e.g. an enclave could EADD a RX page, and then EADD a
RW page at the same virtual address with different data. The second EADD
will affect MRENCLAVE, but so long as it's accounted for by the enclave's
signer, it's "legal". SGX_IOC_ENCLAVE_ADD_PAGE *does* prevent adding the
"same" page to an enclave multiple times, so effectively this scenario is
blocked by the current implementation, but it's more of a side effect (of
a sane implementation) as opposed to deliberately preventing shenanigans.
Regarding EMODPE, the kernel doesn't rely on EPCM permissions in any way
shape or form (the EPCM permissions are purely to protect the enclave
from the kernel), e.g. adding +X to a page in the EPCM doesn't magically
change the kernel's page tables and attempting to execute from the page
will still generate a (non-SGX) #PF.
So rather than check SECINFO.X, I think we'd want to have EADD check that
the permissions in SECINFO are a subset of the VMA's perms (IIUC, this is
essentially what Cedric proposed). That would prevent using EMODPE to
gain executable permissions, and would explicitly deny the scenario of a
double EADD to load non-executable data into an executable page.
Oh, and EADD should probably also require EEXTEND for executable pages.
> Sadly, we don't really have the a nice in-kernel API for that right now.
> You could do down_read(mmap_sem) and find_vma(). Arguably there is no
> value to checking that PKRU allows execute to the data.
>
> Hey, Dave, if you're still paying attention to this thread, should we
> have copy_from_user_exec() that does the right thing wrt the page
> permissions and PKRU.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 15:30 ` Haitao Huang
@ 2019-05-14 20:45 ` Andy Lutomirski
2019-05-14 21:08 ` Haitao Huang
2019-05-14 21:58 ` Xing, Cedric
0 siblings, 2 replies; 285+ messages in thread
From: Andy Lutomirski @ 2019-05-14 20:45 UTC (permalink / raw)
To: Haitao Huang
Cc: Andy Lutomirski, Jethro Beekman, Xing, Cedric, Hansen, Dave,
Thomas Gleixner, Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML,
X86 ML, linux-sgx, Andrew Morton, Christopherson, Sean J,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, Andy Shevchenko, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
> On May 14, 2019, at 8:30 AM, Haitao Huang <haitao.huang@linux.intel.com> wrote:
>
>> On Tue, 14 May 2019 10:17:29 -0500, Andy Lutomirski <luto@kernel.org> wrote:
>>
>> On Tue, May 14, 2019 at 7:33 AM Haitao Huang
>> <haitao.huang@linux.intel.com> wrote:
>>>
>>> On Fri, 10 May 2019 14:22:34 -0500, Andy Lutomirski <luto@kernel.org>
>>> wrote:
>>>
>>> > On Fri, May 10, 2019 at 12:04 PM Jethro Beekman <jethro@fortanix.com>
>>> > wrote:
>>> >>
>>> >> On 2019-05-10 11:56, Xing, Cedric wrote:
>>> >> > Hi Jethro,
>>> >> >
>>> >> >> ELF files are explicitly designed such that you can map them (with
>>> >> mmap)
>>> >> >> in 4096-byte chunks. However, sometimes there's overlap and you will
>>> >> >> sometimes see that a particular offset is mapped twice because the
>>> >> first
>>> >> >> half of the page in the file belongs to an RX range and the second
>>> >> half
>>> >> >> to an R-only range. Also, ELF files don't (normally) describe stack,
>>> >> >> heap, etc. which you do need for enclaves.
>>> >> >
>>> >> > You have probably misread my email. By mmap(), I meant the enclave
>>> >> file would be mapped via *multiple* mmap() calls, in the same way as
>>> >> what dlopen() would do in loading regular shared object. The intention
>>> >> here is to make the enclave file subject to the same checks as regular
>>> >> shared objects.
>>> >>
>>> >> No, I didn't misread your email. My original point still stands:
>>> >> requiring that an enclave's memory is created from one or more mmap
>>> >> calls of a file puts significant restrictions on the enclave's on-disk
>>> >> representation.
>>> >>
>>> >
>>> > For a tiny bit of background, Linux (AFAIK*) makes no effort to ensure
>>> > the complete integrity of DSOs. What Linux *does* do (if so
>>> > configured) is to make sure that only approved data is mapped
>>> > executable. So, if you want to have some bytes be executable, those
>>> > bytes have to come from a file that passes the relevant LSM and IMA
>>> > checks.
>>>
>>> Given this, I just want to step back a little to understand the exact
>>> issue that SGX is causing here for LSM/IMA. Sorry if I missed points
>>> discussed earlier.
>>>
>>> By the time of EADD, enclave file is opened and should have passed IMA and
>>> SELinux policy enforcement gates if any. We really don't need extra mmaps
>>> on the enclave files to be IMA and SELinux compliant.
>>
>> The problem, as i see it, is that they passed the *wrong* checks,
>> because, as you noticed:
>>
>>> We are loading
>>> enclave files as RO and copying those into EPC.
>>
>> Which is, semantically, a lot like loading a normal file as RO and
>> then mprotecting() it to RX, which is disallowed under quite a few LSM
>> policies.
>>
>>> An IMA policy can enforce
>>> RO files (or any file). And SELinux policy can say which processes can
>>> open the file for what permissions. No extra needed here.
>>
>> If SELinux says a process may open a file as RO, that does *not* mean
>> that it can be opened as RX.
>>
>
> But in this case, file itself is mapped as RO treated like data and it is not for execution. SGX enclave pages have EPCM enforced permissions. So from SELinux point of view I would think it can treat it as RO and that's fine.
As an example, SELinux has an “execute” permission (via
security_mmap_file — see file_map_prot_check()) that controls whether
you can execute code from that file. If you lack this permission on a
file, you may still be able to map it PROT_READ, but you may not map
it PROT_EXEC. Similarly, if you want to malloc() some memory, write
*code* to it, and execute it, you need a specific permission.
So, unless we somehow think it’s okay for SGX to break the existing
model, we need to respect these restrictions in the SGX driver. In
other words, we either need to respect execmem, etc or require
PROT_EXEC or the equivalent. I like the latter a lot more.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 20:45 ` Andy Lutomirski
@ 2019-05-14 21:08 ` Haitao Huang
2019-05-14 21:58 ` Xing, Cedric
1 sibling, 0 replies; 285+ messages in thread
From: Haitao Huang @ 2019-05-14 21:08 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jethro Beekman, Xing, Cedric, Hansen, Dave, Thomas Gleixner,
Dr. Greg, Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, Christopherson, Sean J, nhorman,
npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao,
Andy Shevchenko, Svahn, Kai, Borislav Petkov, Josh Triplett,
Huang, Kai, David Rientjes
On Tue, 14 May 2019 15:45:54 -0500, Andy Lutomirski <luto@kernel.org>
wrote:
>> On May 14, 2019, at 8:30 AM, Haitao Huang
>> <haitao.huang@linux.intel.com> wrote:
>>
>>> On Tue, 14 May 2019 10:17:29 -0500, Andy Lutomirski <luto@kernel.org>
>>> wrote:
>>>
>>> On Tue, May 14, 2019 at 7:33 AM Haitao Huang
>>> <haitao.huang@linux.intel.com> wrote:
>>>>
>>>> On Fri, 10 May 2019 14:22:34 -0500, Andy Lutomirski <luto@kernel.org>
>>>> wrote:
>>>>
>>>> > On Fri, May 10, 2019 at 12:04 PM Jethro Beekman
>>>> <jethro@fortanix.com>
>>>> > wrote:
>>>> >>
>>>> >> On 2019-05-10 11:56, Xing, Cedric wrote:
>>>> >> > Hi Jethro,
>>>> >> >
>>>> >> >> ELF files are explicitly designed such that you can map them
>>>> (with
>>>> >> mmap)
>>>> >> >> in 4096-byte chunks. However, sometimes there's overlap and you
>>>> will
>>>> >> >> sometimes see that a particular offset is mapped twice because
>>>> the
>>>> >> first
>>>> >> >> half of the page in the file belongs to an RX range and the
>>>> second
>>>> >> half
>>>> >> >> to an R-only range. Also, ELF files don't (normally) describe
>>>> stack,
>>>> >> >> heap, etc. which you do need for enclaves.
>>>> >> >
>>>> >> > You have probably misread my email. By mmap(), I meant the
>>>> enclave
>>>> >> file would be mapped via *multiple* mmap() calls, in the same way
>>>> as
>>>> >> what dlopen() would do in loading regular shared object. The
>>>> intention
>>>> >> here is to make the enclave file subject to the same checks as
>>>> regular
>>>> >> shared objects.
>>>> >>
>>>> >> No, I didn't misread your email. My original point still stands:
>>>> >> requiring that an enclave's memory is created from one or more mmap
>>>> >> calls of a file puts significant restrictions on the enclave's
>>>> on-disk
>>>> >> representation.
>>>> >>
>>>> >
>>>> > For a tiny bit of background, Linux (AFAIK*) makes no effort to
>>>> ensure
>>>> > the complete integrity of DSOs. What Linux *does* do (if so
>>>> > configured) is to make sure that only approved data is mapped
>>>> > executable. So, if you want to have some bytes be executable, those
>>>> > bytes have to come from a file that passes the relevant LSM and IMA
>>>> > checks.
>>>>
>>>> Given this, I just want to step back a little to understand the exact
>>>> issue that SGX is causing here for LSM/IMA. Sorry if I missed points
>>>> discussed earlier.
>>>>
>>>> By the time of EADD, enclave file is opened and should have passed
>>>> IMA and
>>>> SELinux policy enforcement gates if any. We really don't need extra
>>>> mmaps
>>>> on the enclave files to be IMA and SELinux compliant.
>>>
>>> The problem, as i see it, is that they passed the *wrong* checks,
>>> because, as you noticed:
>>>
>>>> We are loading
>>>> enclave files as RO and copying those into EPC.
>>>
>>> Which is, semantically, a lot like loading a normal file as RO and
>>> then mprotecting() it to RX, which is disallowed under quite a few LSM
>>> policies.
>>>
>>>> An IMA policy can enforce
>>>> RO files (or any file). And SELinux policy can say which processes can
>>>> open the file for what permissions. No extra needed here.
>>>
>>> If SELinux says a process may open a file as RO, that does *not* mean
>>> that it can be opened as RX.
>>>
>>
>> But in this case, file itself is mapped as RO treated like data and it
>> is not for execution. SGX enclave pages have EPCM enforced permissions.
>> So from SELinux point of view I would think it can treat it as RO and
>> that's fine.
>
> As an example, SELinux has an “execute” permission (via
> security_mmap_file — see file_map_prot_check()) that controls whether
> you can execute code from that file. If you lack this permission on a
> file, you may still be able to map it PROT_READ, but you may not map
> it PROT_EXEC. Similarly, if you want to malloc() some memory, write
> *code* to it, and execute it, you need a specific permission.
>
> So, unless we somehow think it’s okay for SGX to break the existing
> model, we need to respect these restrictions in the SGX driver. In
> other words, we either need to respect execmem, etc or require
> PROT_EXEC or the equivalent. I like the latter a lot more.
What puzzles me is that this restriction does not add real value to
security.
When enclave files are mapped with PROT_READ, without SE execute
permission. No breakage to LSM model in normal process address space as no
one can execute code directly from the file in normal memory. When enclave
is built and loaded into EPC by EADDs, if the SIGSTRUCT is trusted (either
signer or MRENCLAVE), EINIT will guarantee security (both integrity and
permissions). LSM may not like the fact the a piece of code got loaded
into EPC page without specifically giving SE execute permission. However,
LSM can be used to control what SIGSTRUCTs can be trusted as you suggested
and indirectly enforce what code got executed inside EPC.
So to me, only SIGSTRUCT verification would add value.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 20:45 ` Sean Christopherson
@ 2019-05-14 21:27 ` Andy Lutomirski
2019-05-14 22:28 ` Xing, Cedric
2019-05-15 1:30 ` Sean Christopherson
2019-05-15 10:35 ` [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
1 sibling, 2 replies; 285+ messages in thread
From: Andy Lutomirski @ 2019-05-14 21:27 UTC (permalink / raw)
To: Sean Christopherson
Cc: Andy Lutomirski, Jarkko Sakkinen, Jethro Beekman, Xing, Cedric,
Hansen, Dave, Thomas Gleixner, Dr. Greg, Linus Torvalds, LKML,
X86 ML, linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun,
Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn,
Kai, Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Tue, May 14, 2019 at 1:45 PM Sean Christopherson
<sean.j.christopherson@intel.com> wrote:
>
> On Tue, May 14, 2019 at 08:13:36AM -0700, Andy Lutomirski wrote:
> > On Tue, May 14, 2019 at 3:43 AM Jarkko Sakkinen
> > <jarkko.sakkinen@linux.intel.com> wrote:
> > >
> > > On Mon, May 13, 2019 at 01:29:26PM +0300, Jarkko Sakkinen wrote:
> > > > I did study through SDK's file format and realized that it does not
> > > > does make sense after all to embed one.
> > > >
> > > > To implement it properly you would probably need a new syscall (lets say
> > > > sgx_load_enclave) and also that enclaves are not just executables
> > > > binaries. It is hard to find a generic format for them as applications
> > > > range from simply protecting part of an application to running a
> > > > containter inside enclave.
> > >
> > > I'm still puzzling what kind of changes you were discussing considering
> > > SGX_IOC_ENCLAVE_ADD_PAGE.
> >
> > I think it's as simple as requiring that, if SECINFO.X is set, then
> > the src pointer points to the appropriate number of bytes of
> > executable memory. (Unless there's some way for an enclave to change
> > SECINFO after the fact -- is there?)
>
> Nit: SECINFO is just the struct passed to EADD, I think what you're really
> asking is "can the EPCM permissions be changed after the fact".
>
> And the answer is, yes.
>
> On SGX2 hardware, the enclave can extend the EPCM permissions at runtime
> via ENCLU[EMODPE], e.g. to make a page writable.
>
> Hardware also doesn't prevent doing EADD to the same virtual address
> multiple times, e.g. an enclave could EADD a RX page, and then EADD a
> RW page at the same virtual address with different data. The second EADD
> will affect MRENCLAVE, but so long as it's accounted for by the enclave's
> signer, it's "legal". SGX_IOC_ENCLAVE_ADD_PAGE *does* prevent adding the
> "same" page to an enclave multiple times, so effectively this scenario is
> blocked by the current implementation, but it's more of a side effect (of
> a sane implementation) as opposed to deliberately preventing shenanigans.
>
> Regarding EMODPE, the kernel doesn't rely on EPCM permissions in any way
> shape or form (the EPCM permissions are purely to protect the enclave
> from the kernel), e.g. adding +X to a page in the EPCM doesn't magically
> change the kernel's page tables and attempting to execute from the page
> will still generate a (non-SGX) #PF.
>
> So rather than check SECINFO.X, I think we'd want to have EADD check that
> the permissions in SECINFO are a subset of the VMA's perms (IIUC, this is
> essentially what Cedric proposed). That would prevent using EMODPE to
> gain executable permissions, and would explicitly deny the scenario of a
> double EADD to load non-executable data into an executable page.
Let me make sure I'm understanding this correctly: when an enclave
tries to execute code, it only works if *both* the EPCM and the page
tables grant the access, right? This seems to be that 37.3 is trying
to say. So we should probably just ignore SECINFO for these purposes.
But thinking this all through, it's a bit more complicated than any of
this. Looking at the SELinux code for inspiration, there are quite a
few paths, but they boil down to two cases: EXECUTE is the right to
map an unmodified file executably, and EXECMOD/EXECMEM (the
distinction seems mostly irrelevant) is the right to create (via mmap
or mprotect) a modified anonymous file mapping or a non-file-backed
mapping that is executable. So, if we do nothing, then mapping an
enclave with execute permission will require either EXECUTE on the
enclave inode or EXECMOD/EXECMEM, depending on exactly how this gets
set up.
So all is well, sort of. The problem is that I expect there will be
people who want enclaves to work in a process that does not have these
rights. To make this work, we probably need do some surgery on
SELinux. ISTM the act of copying (via the EADD ioctl) data from a
PROT_EXEC mapping to an enclave should not be construed as "modifying"
the enclave for SELinux purposes. Actually doing this could be
awkward, since the same inode will have executable parts and
non-executable parts, and SELinux can't really tell the difference.
Maybe the enclave should track a bitmap of which pages have ever been
either mapped for write or EADDed with a *source* that wasn't
PROT_EXEC. And then SELinux could learn to allow those pages (and
only those pages) to be mapped executably without EXECUTE or EXECMOD
or whatever permission.
Does this seem at all reasonable?
I suppose it's not the end of the world if the initially merged
version doesn't do this, as long as there's some reasonable path to
adding a mechanism like this when there's demand for it.
^ permalink raw reply [flat|nested] 285+ messages in thread
* RE: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 20:45 ` Andy Lutomirski
2019-05-14 21:08 ` Haitao Huang
@ 2019-05-14 21:58 ` Xing, Cedric
2019-05-15 5:15 ` Haitao Huang
1 sibling, 1 reply; 285+ messages in thread
From: Xing, Cedric @ 2019-05-14 21:58 UTC (permalink / raw)
To: Andy Lutomirski, Haitao Huang
Cc: Jethro Beekman, Hansen, Dave, Thomas Gleixner, Dr. Greg,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
Hi Everyone,
I think we are talking about 2 different kinds of criteria for determining the sanity of an enclave.
The first kind determines an enclave's sanity by generally accepted good practices. For example, no executable pages shall ever be writable.
The second kind determines an enclave's sanity by "who stands behind it", such as whether the file containing SIGSTRUCT has the proper SELinux label/type, or whether the signing key is trusted.
I'd say those 2 kinds of criteria should be orthogonal because they don't get into each other's way and it could also be beneficial to enable both at the same time. For example, a user may want to allow launching enclaves signed by himself/herself only, however, as a human being he/she may make mistakes so would also like to ensure no RWX pages even for those enclaves explicitly authorized.
I think those 2 kinds of criteria could be abstracted as 2 new LSM hooks - security_sgx_add_pages() and security_sgx_initialize_enclave().
security_sgx_add_pages() is invoked by SGX driver to determine if a range of source pages are allowed to be EADD'ed with the requested EPCM attributes, and by default it returns 0 (allowed) iff the requested EPCM attributes are a subset of the permissions of the VMA covering that range of source pages. An (SGX-aware) LSM module/policy could employ different criteria, such as making sure the source pages are backed by an enclave file (using SELinux label, for example).
security_sgx_initialize_enclave() is invoked by SGX driver to determine if a given SIGSTRUCT is valid (hence allowed to be EINIT'ed), and it always returns 0 (allowed) by default. An LSM implementation may enforce custom policies, such as whether the signing public key is trusted by the current user, or whether it was backed (mmap()'ed) by an authorized file (e.g. of an expected type in the case of SELinux, or located in a particular directory in the case of AppArmor).
With regard to SGX2/EDMM (Enclave Dynamic Memory Management) support, RW->RX transitions are inevitable to support certain usages such as dynamic loading/linking, meaning those usages may be blocked by existing policies. But from security perspective, I think they should be allowed by default (i.e. for non-SGX-aware LSM modules/policies) because such permission changes are inherent behaviors of the enclave itself, which is considered "sane" after passing all the checks performed in security_sgx_add_pages()/security_sgx_initialize_enclave(). Of course, an SGX-aware LSM module/policy shall be allowed to override. How about adding a new security_sgx_mprotect() LSM hook?
> From: Andy Lutomirski [mailto:luto@kernel.org]
>
> > On May 14, 2019, at 8:30 AM, Haitao Huang
> <haitao.huang@linux.intel.com> wrote:
> >
> >> On Tue, 14 May 2019 10:17:29 -0500, Andy Lutomirski <luto@kernel.org>
> wrote:
> >>
> >> On Tue, May 14, 2019 at 7:33 AM Haitao Huang
> >> <haitao.huang@linux.intel.com> wrote:
> >>>
> >>> On Fri, 10 May 2019 14:22:34 -0500, Andy Lutomirski
> >>> <luto@kernel.org>
> >>> wrote:
> >>>
> >>> > On Fri, May 10, 2019 at 12:04 PM Jethro Beekman
> >>> > <jethro@fortanix.com>
> >>> > wrote:
> >>> >>
> >>> >> On 2019-05-10 11:56, Xing, Cedric wrote:
> >>> >> > Hi Jethro,
> >>> >> >
> >>> >> >> ELF files are explicitly designed such that you can map them
> >>> >> >> (with
> >>> >> mmap)
> >>> >> >> in 4096-byte chunks. However, sometimes there's overlap and
> >>> >> >> you will sometimes see that a particular offset is mapped
> >>> >> >> twice because the
> >>> >> first
> >>> >> >> half of the page in the file belongs to an RX range and the
> >>> >> >> second
> >>> >> half
> >>> >> >> to an R-only range. Also, ELF files don't (normally) describe
> >>> >> >> stack, heap, etc. which you do need for enclaves.
> >>> >> >
> >>> >> > You have probably misread my email. By mmap(), I meant the
> >>> >> > enclave
> >>> >> file would be mapped via *multiple* mmap() calls, in the same way
> >>> >> as what dlopen() would do in loading regular shared object. The
> >>> >> intention here is to make the enclave file subject to the same
> >>> >> checks as regular shared objects.
> >>> >>
> >>> >> No, I didn't misread your email. My original point still stands:
> >>> >> requiring that an enclave's memory is created from one or more
> >>> >> mmap calls of a file puts significant restrictions on the
> >>> >> enclave's on-disk representation.
> >>> >>
> >>> >
> >>> > For a tiny bit of background, Linux (AFAIK*) makes no effort to
> >>> > ensure the complete integrity of DSOs. What Linux *does* do (if
> >>> > so
> >>> > configured) is to make sure that only approved data is mapped
> >>> > executable. So, if you want to have some bytes be executable,
> >>> > those bytes have to come from a file that passes the relevant LSM
> >>> > and IMA checks.
> >>>
> >>> Given this, I just want to step back a little to understand the
> >>> exact issue that SGX is causing here for LSM/IMA. Sorry if I missed
> >>> points discussed earlier.
> >>>
> >>> By the time of EADD, enclave file is opened and should have passed
> >>> IMA and SELinux policy enforcement gates if any. We really don't
> >>> need extra mmaps on the enclave files to be IMA and SELinux
> compliant.
> >>
> >> The problem, as i see it, is that they passed the *wrong* checks,
> >> because, as you noticed:
> >>
> >>> We are loading
> >>> enclave files as RO and copying those into EPC.
> >>
> >> Which is, semantically, a lot like loading a normal file as RO and
> >> then mprotecting() it to RX, which is disallowed under quite a few
> >> LSM policies.
> >>
> >>> An IMA policy can enforce
> >>> RO files (or any file). And SELinux policy can say which processes
> >>> can open the file for what permissions. No extra needed here.
> >>
> >> If SELinux says a process may open a file as RO, that does *not* mean
> >> that it can be opened as RX.
> >>
> >
> > But in this case, file itself is mapped as RO treated like data and it
> is not for execution. SGX enclave pages have EPCM enforced permissions.
> So from SELinux point of view I would think it can treat it as RO and
> that's fine.
>
> As an example, SELinux has an “execute” permission (via
> security_mmap_file — see file_map_prot_check()) that controls whether
> you can execute code from that file. If you lack this permission on a
> file, you may still be able to map it PROT_READ, but you may not map it
> PROT_EXEC. Similarly, if you want to malloc() some memory, write
> *code* to it, and execute it, you need a specific permission.
>
> So, unless we somehow think it’s okay for SGX to break the existing
> model, we need to respect these restrictions in the SGX driver. In other
> words, we either need to respect execmem, etc or require PROT_EXEC or
> the equivalent. I like the latter a lot more.
-Cedric
^ permalink raw reply [flat|nested] 285+ messages in thread
* RE: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 21:27 ` Andy Lutomirski
@ 2019-05-14 22:28 ` Xing, Cedric
2019-05-15 1:30 ` Sean Christopherson
1 sibling, 0 replies; 285+ messages in thread
From: Xing, Cedric @ 2019-05-14 22:28 UTC (permalink / raw)
To: Andy Lutomirski, Christopherson, Sean J
Cc: Jarkko Sakkinen, Jethro Beekman, Hansen, Dave, Thomas Gleixner,
Dr. Greg, Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
Haitao, Andy Shevchenko, Svahn, Kai, Borislav Petkov,
Josh Triplett, Huang, Kai, David Rientjes
Hi Andy,
> Let me make sure I'm understanding this correctly: when an enclave tries
> to execute code, it only works if *both* the EPCM and the page tables
> grant the access, right? This seems to be that 37.3 is trying to say.
> So we should probably just ignore SECINFO for these purposes.
>
> But thinking this all through, it's a bit more complicated than any of
> this. Looking at the SELinux code for inspiration, there are quite a
> few paths, but they boil down to two cases: EXECUTE is the right to map
> an unmodified file executably, and EXECMOD/EXECMEM (the distinction
> seems mostly irrelevant) is the right to create (via mmap or mprotect) a
> modified anonymous file mapping or a non-file-backed mapping that is
> executable. So, if we do nothing, then mapping an enclave with execute
> permission will require either EXECUTE on the enclave inode or
> EXECMOD/EXECMEM, depending on exactly how this gets set up.
>
> So all is well, sort of. The problem is that I expect there will be
> people who want enclaves to work in a process that does not have these
> rights. To make this work, we probably need do some surgery on SELinux.
> ISTM the act of copying (via the EADD ioctl) data from a PROT_EXEC
> mapping to an enclave should not be construed as "modifying"
> the enclave for SELinux purposes. Actually doing this could be awkward,
> since the same inode will have executable parts and non-executable parts,
> and SELinux can't really tell the difference.
>
Enclave files are pretty much like shared objects in that they both contain executable code mapped into the host process's address space. Do shared objects need EXECUTE to be mapped executable? If so, why would people not want EXECUTE in enclave files?
Wrt to which part of an executable file is executable, the limitation resides in security_mmap_file(), which doesn't take the range of bytes as input. It isn't SGX specific. I'd say just let enclaves inherit applicable checks for shared objects. Then it will also inherit all future enhancements transparently.
> Maybe the enclave should track a bitmap of which pages have ever been
> either mapped for write or EADDed with a *source* that wasn't PROT_EXEC.
> And then SELinux could learn to allow those pages (and only those pages)
> to be mapped executably without EXECUTE or EXECMOD or whatever
> permission.
What do you mean by "enclave" here? The enclave range (ELRANGE) created by mmap()'ing /dev/sgx/enclave device? My argument is, an enclave's sanity/insanity is determined at load time (EADD/EEXTEND and EINIT) and all page accesses are enforced by EPCM, so PTE permissions really don't matter. As I discussed in an earlier email, I'd allow RWX for any range backed by /dev/sgx/enclave device file by default, unless an SGX-aware LSM module/policy objects to that (e.g. via a new security_sgx_mprotect() LSM hook).
>
> Does this seem at all reasonable?
>
> I suppose it's not the end of the world if the initially merged version
> doesn't do this, as long as there's some reasonable path to adding a
> mechanism like this when there's demand for it.
Agreed!
-Cedric
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 21:27 ` Andy Lutomirski
2019-05-14 22:28 ` Xing, Cedric
@ 2019-05-15 1:30 ` Sean Christopherson
2019-05-15 18:27 ` SGX vs LSM (Re: [PATCH v20 00/28] Intel SGX1 support) Andy Lutomirski
1 sibling, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-05-15 1:30 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jarkko Sakkinen, Jethro Beekman, Xing, Cedric, Hansen, Dave,
Thomas Gleixner, Dr. Greg, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Tue, May 14, 2019 at 02:27:08PM -0700, Andy Lutomirski wrote:
> On Tue, May 14, 2019 at 1:45 PM Sean Christopherson
> <sean.j.christopherson@intel.com> wrote:
> >
> > On Tue, May 14, 2019 at 08:13:36AM -0700, Andy Lutomirski wrote:
> > > On Tue, May 14, 2019 at 3:43 AM Jarkko Sakkinen
> > > <jarkko.sakkinen@linux.intel.com> wrote:
> > > >
> > > > On Mon, May 13, 2019 at 01:29:26PM +0300, Jarkko Sakkinen wrote:
> > > > > I did study through SDK's file format and realized that it does not
> > > > > does make sense after all to embed one.
> > > > >
> > > > > To implement it properly you would probably need a new syscall (lets say
> > > > > sgx_load_enclave) and also that enclaves are not just executables
> > > > > binaries. It is hard to find a generic format for them as applications
> > > > > range from simply protecting part of an application to running a
> > > > > containter inside enclave.
> > > >
> > > > I'm still puzzling what kind of changes you were discussing considering
> > > > SGX_IOC_ENCLAVE_ADD_PAGE.
> > >
> > > I think it's as simple as requiring that, if SECINFO.X is set, then
> > > the src pointer points to the appropriate number of bytes of
> > > executable memory. (Unless there's some way for an enclave to change
> > > SECINFO after the fact -- is there?)
> >
> > Nit: SECINFO is just the struct passed to EADD, I think what you're really
> > asking is "can the EPCM permissions be changed after the fact".
> >
> > And the answer is, yes.
> >
> > On SGX2 hardware, the enclave can extend the EPCM permissions at runtime
> > via ENCLU[EMODPE], e.g. to make a page writable.
> >
> > Hardware also doesn't prevent doing EADD to the same virtual address
> > multiple times, e.g. an enclave could EADD a RX page, and then EADD a
> > RW page at the same virtual address with different data. The second EADD
> > will affect MRENCLAVE, but so long as it's accounted for by the enclave's
> > signer, it's "legal". SGX_IOC_ENCLAVE_ADD_PAGE *does* prevent adding the
> > "same" page to an enclave multiple times, so effectively this scenario is
> > blocked by the current implementation, but it's more of a side effect (of
> > a sane implementation) as opposed to deliberately preventing shenanigans.
> >
> > Regarding EMODPE, the kernel doesn't rely on EPCM permissions in any way
> > shape or form (the EPCM permissions are purely to protect the enclave
> > from the kernel), e.g. adding +X to a page in the EPCM doesn't magically
> > change the kernel's page tables and attempting to execute from the page
> > will still generate a (non-SGX) #PF.
> >
> > So rather than check SECINFO.X, I think we'd want to have EADD check that
> > the permissions in SECINFO are a subset of the VMA's perms (IIUC, this is
> > essentially what Cedric proposed). That would prevent using EMODPE to
> > gain executable permissions, and would explicitly deny the scenario of a
> > double EADD to load non-executable data into an executable page.
>
> Let me make sure I'm understanding this correctly: when an enclave
> tries to execute code, it only works if *both* the EPCM and the page
> tables grant the access, right? This seems to be that 37.3 is trying
> to say. So we should probably just ignore SECINFO for these purposes.
Yep. More specifically, the EPCM is consulted if and only if the access
is allowed by the page tables.
I agree on ignoring SECINFO.
> But thinking this all through, it's a bit more complicated than any of
> this. Looking at the SELinux code for inspiration, there are quite a
> few paths, but they boil down to two cases: EXECUTE is the right to
> map an unmodified file executably, and EXECMOD/EXECMEM (the
> distinction seems mostly irrelevant) is the right to create (via mmap
> or mprotect) a modified anonymous file mapping or a non-file-backed
> mapping that is executable. So, if we do nothing, then mapping an
> enclave with execute permission will require either EXECUTE on the
> enclave inode or EXECMOD/EXECMEM, depending on exactly how this gets
> set up.
If we do literally nothing, then I'm pretty sure mapping an enclave will
require PROCESS__EXECMEM. The mmap() for the actual enclave is done
using an anon inode, e.g. from /dev/sgx/enclave. Anon inodes are marked
private, which means inode_has_perm() will always return "success". The
only effective check is in file_map_prot_check() when default_noexec is
true, in which case requesting PROT_EXEC on private inodes requires
PROCESS__EXECMEM.
> So all is well, sort of. The problem is that I expect there will be
> people who want enclaves to work in a process that does not have these
> rights. To make this work, we probably need do some surgery on
> SELinux. ISTM the act of copying (via the EADD ioctl) data from a
> PROT_EXEC mapping to an enclave should not be construed as "modifying"
> the enclave for SELinux purposes. Actually doing this could be
> awkward, since the same inode will have executable parts and
> non-executable parts, and SELinux can't really tell the difference.
Rather the do surgery on SELinux, why not go with Cedric's original
proposal and propagate the permissions from the source VMA to the EPC
VMA? The enclave mmap() from userspace could then be done with RO
permissions so as to not run afoul of LSMs. Adding PROT_EXEC after
EADD would require PROCESS__EXECMEM, but that's in line with mprotect()
on regular memory. It also punts the EMODPE hiccup to userspace, e.g.
any enclave that wants to do fancy things with PROT_EXEC needs
PROCESS__EXECMEM.
The only downside I see is that SGX would be doing a bit of magic with
enclave VMAs.
> Maybe the enclave should track a bitmap of which pages have ever been
> either mapped for write or EADDed with a *source* that wasn't
> PROT_EXEC. And then SELinux could learn to allow those pages (and
> only those pages) to be mapped executably without EXECUTE or EXECMOD
> or whatever permission.
>
> Does this seem at all reasonable?
No? I don't understand why you want to special case enclaves from an
LSM perspective. Enforcing LSM policies on the source provides the
same security for enclaves as it does for normal code, no more, no less.
> I suppose it's not the end of the world if the initially merged
> version doesn't do this, as long as there's some reasonable path to
> adding a mechanism like this when there's demand for it.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 21:58 ` Xing, Cedric
@ 2019-05-15 5:15 ` Haitao Huang
0 siblings, 0 replies; 285+ messages in thread
From: Haitao Huang @ 2019-05-15 5:15 UTC (permalink / raw)
To: Andy Lutomirski, Xing, Cedric
Cc: Jethro Beekman, Hansen, Dave, Thomas Gleixner, Dr. Greg,
Jarkko Sakkinen, Linus Torvalds, LKML, X86 ML, linux-sgx,
Andrew Morton, Christopherson, Sean J, nhorman, npmccallum,
Ayoun, Serge, Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
Svahn, Kai, Borislav Petkov, Josh Triplett, Huang, Kai,
David Rientjes
On Tue, 14 May 2019 16:58:24 -0500, Xing, Cedric <cedric.xing@intel.com>
wrote:
> Hi Everyone,
>
> I think we are talking about 2 different kinds of criteria for
> determining the sanity of an enclave.
>
> The first kind determines an enclave's sanity by generally accepted good
> practices. For example, no executable pages shall ever be writable.
>
We'll have to trust user space doing mmap with right permissions as
SELinux does not enforce which segment to be RW and which to be RX. The
file needs to have SELinux EXECUTE and WRITE both, if we need map some
segments with RW and others with RX.
We could say EINIT would ensure user is doing the right thing because it
would fail if user map permission wrongly. Then the extra mmaps are
redundant of doing SIGSTRUCT verification.
Additionally, per Sean's comments, after EADD in current implementation,
we will still need PROCESS_EXECMEM for mprotect on enclave fd to change
some EPC pages PTE to RX before enclave can execute. So I don't think mmap
the source enclave file would gain anything in addition to what your
proposed security_sgx_initialize_enclave() does.
Since security_sgx_initialize_enclave() is a lot like launch control
policy enforcement we discussed a lot and resolved, I tend to agree with
Andy's assessment we can just do nothing for the initial merge and add
hooks needed if someone wants them. And the initial merge would require
the enclave hosting processes ask for PROCESS_EXECMEM permission to do
mmap/mprotect with enclave fd.
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 15:08 ` Andy Lutomirski
@ 2019-05-15 8:31 ` Jarkko Sakkinen
0 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-15 8:31 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Xing, Cedric, Jethro Beekman, Hansen, Dave, Thomas Gleixner,
Dr. Greg, Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Tue, May 14, 2019 at 08:08:03AM -0700, Andy Lutomirski wrote:
> > Putting everything together, I'd suggest to:
> > - Change EADD ioctl to take source page's VMA permission as ("upper bound" of) EPCM permission. This make sure no one can circumvent LSM to generate executable code on the fly using SGX driver.
> > - Change EINIT ioctl to invoke (new?) LSM hook to validate SIGSTRUCT before issuing EINIT.
>
> I'm okay with this if the consensus is that having a .sigstruct file
> is too annoying.
SIGSTRUCT has two nice properties from kernel perspective:
- Static structure
- Fully defines enclave contents including the page permissions as
they are part of the measurement.
Making it as the "root of trust" really is the right thing and the most
robust way to deal with this.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 15:13 ` Andy Lutomirski
2019-05-14 20:45 ` Sean Christopherson
@ 2019-05-15 8:49 ` Jarkko Sakkinen
2019-05-15 9:58 ` Jarkko Sakkinen
1 sibling, 1 reply; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-15 8:49 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jethro Beekman, Xing, Cedric, Hansen, Dave, Thomas Gleixner,
Dr. Greg, Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Tue, May 14, 2019 at 08:13:36AM -0700, Andy Lutomirski wrote:
> On Tue, May 14, 2019 at 3:43 AM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> >
> > On Mon, May 13, 2019 at 01:29:26PM +0300, Jarkko Sakkinen wrote:
> > > I did study through SDK's file format and realized that it does not
> > > does make sense after all to embed one.
> > >
> > > To implement it properly you would probably need a new syscall (lets say
> > > sgx_load_enclave) and also that enclaves are not just executables
> > > binaries. It is hard to find a generic format for them as applications
> > > range from simply protecting part of an application to running a
> > > containter inside enclave.
> >
> > I'm still puzzling what kind of changes you were discussing considering
> > SGX_IOC_ENCLAVE_ADD_PAGE.
>
> I think it's as simple as requiring that, if SECINFO.X is set, then
> the src pointer points to the appropriate number of bytes of
> executable memory. (Unless there's some way for an enclave to change
> SECINFO after the fact -- is there?) Sadly, we don't really have the
> a nice in-kernel API for that right now. You could do
> down_read(mmap_sem) and find_vma(). Arguably there is no value to
> checking that PKRU allows execute to the data.
OK, so you would actually go on to check whether the VMA where the data
is copied contains executable data?
What if SECINFO.X is not set and you EADD to region that is executable
in the enclave VMA? E.g. have RWX VMA for the enclave just to give a
simple example. Then you could carry executable code in the data
sections of the binary.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-15 8:49 ` Jarkko Sakkinen
@ 2019-05-15 9:58 ` Jarkko Sakkinen
0 siblings, 0 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-15 9:58 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jethro Beekman, Xing, Cedric, Hansen, Dave, Thomas Gleixner,
Dr. Greg, Linus Torvalds, LKML, X86 ML, linux-sgx, Andrew Morton,
Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Wed, May 15, 2019 at 11:49:09AM +0300, Jarkko Sakkinen wrote:
> On Tue, May 14, 2019 at 08:13:36AM -0700, Andy Lutomirski wrote:
> > On Tue, May 14, 2019 at 3:43 AM Jarkko Sakkinen
> > <jarkko.sakkinen@linux.intel.com> wrote:
> > >
> > > On Mon, May 13, 2019 at 01:29:26PM +0300, Jarkko Sakkinen wrote:
> > > > I did study through SDK's file format and realized that it does not
> > > > does make sense after all to embed one.
> > > >
> > > > To implement it properly you would probably need a new syscall (lets say
> > > > sgx_load_enclave) and also that enclaves are not just executables
> > > > binaries. It is hard to find a generic format for them as applications
> > > > range from simply protecting part of an application to running a
> > > > containter inside enclave.
> > >
> > > I'm still puzzling what kind of changes you were discussing considering
> > > SGX_IOC_ENCLAVE_ADD_PAGE.
> >
> > I think it's as simple as requiring that, if SECINFO.X is set, then
> > the src pointer points to the appropriate number of bytes of
> > executable memory. (Unless there's some way for an enclave to change
> > SECINFO after the fact -- is there?) Sadly, we don't really have the
> > a nice in-kernel API for that right now. You could do
> > down_read(mmap_sem) and find_vma(). Arguably there is no value to
> > checking that PKRU allows execute to the data.
>
> OK, so you would actually go on to check whether the VMA where the data
> is copied contains executable data?
>
> What if SECINFO.X is not set and you EADD to region that is executable
> in the enclave VMA? E.g. have RWX VMA for the enclave just to give a
> simple example. Then you could carry executable code in the data
> sections of the binary.
This would require to be done after the enclave is initialized of course
with EMODPR, which requires the enclave to accept the permission change
with EACCEPT, which limits somewhat. This means that the static enclave
would be fully covered.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-14 20:45 ` Sean Christopherson
2019-05-14 21:27 ` Andy Lutomirski
@ 2019-05-15 10:35 ` Jarkko Sakkinen
2019-05-15 11:00 ` Jarkko Sakkinen
2019-05-15 13:21 ` Sean Christopherson
1 sibling, 2 replies; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-15 10:35 UTC (permalink / raw)
To: Sean Christopherson
Cc: Andy Lutomirski, Jethro Beekman, Xing, Cedric, Hansen, Dave,
Thomas Gleixner, Dr. Greg, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Tue, May 14, 2019 at 01:45:27PM -0700, Sean Christopherson wrote:
> On Tue, May 14, 2019 at 08:13:36AM -0700, Andy Lutomirski wrote:
> > On Tue, May 14, 2019 at 3:43 AM Jarkko Sakkinen
> > <jarkko.sakkinen@linux.intel.com> wrote:
> > >
> > > On Mon, May 13, 2019 at 01:29:26PM +0300, Jarkko Sakkinen wrote:
> > > > I did study through SDK's file format and realized that it does not
> > > > does make sense after all to embed one.
> > > >
> > > > To implement it properly you would probably need a new syscall (lets say
> > > > sgx_load_enclave) and also that enclaves are not just executables
> > > > binaries. It is hard to find a generic format for them as applications
> > > > range from simply protecting part of an application to running a
> > > > containter inside enclave.
> > >
> > > I'm still puzzling what kind of changes you were discussing considering
> > > SGX_IOC_ENCLAVE_ADD_PAGE.
> >
> > I think it's as simple as requiring that, if SECINFO.X is set, then
> > the src pointer points to the appropriate number of bytes of
> > executable memory. (Unless there's some way for an enclave to change
> > SECINFO after the fact -- is there?)
>
> Nit: SECINFO is just the struct passed to EADD, I think what you're really
> asking is "can the EPCM permissions be changed after the fact".
>
> And the answer is, yes.
>
> On SGX2 hardware, the enclave can extend the EPCM permissions at runtime
> via ENCLU[EMODPE], e.g. to make a page writable.
Small correction: it is EMODPR.
Anyway, it is good to mention that these would require EACCEPT from the
enclave side. In order to take advantage of this is in a malicous
enclave, one would require SELinux/IMA/whatnot policy to have permitted
it in the first place.
Thus, it cannot be said that it breaks the security policy if this would
happen because policy has allowed to use the particular enclave.
> Hardware also doesn't prevent doing EADD to the same virtual address
> multiple times, e.g. an enclave could EADD a RX page, and then EADD a
> RW page at the same virtual address with different data. The second EADD
> will affect MRENCLAVE, but so long as it's accounted for by the enclave's
> signer, it's "legal". SGX_IOC_ENCLAVE_ADD_PAGE *does* prevent adding the
> "same" page to an enclave multiple times, so effectively this scenario is
> blocked by the current implementation, but it's more of a side effect (of
> a sane implementation) as opposed to deliberately preventing shenanigans.
If the security policy can define who can create legit SIGSTRUCT files,
this should not be a problem. Neither should be how EEXTEND is used.
This brings me to an open question in Andy's model: lets say that we
change the source for SIGSTRUCT from memory address to fd. How can the
policy prevent the use not creating a file containing a SIGSTRUCT and
passing fd of that to the EINIT ioctl?
If we can sort this question out, then SIGSTRUCT-centered way to control
enclave would actually be robust.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-15 10:35 ` [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
@ 2019-05-15 11:00 ` Jarkko Sakkinen
2019-05-15 14:27 ` Andy Lutomirski
2019-05-15 13:21 ` Sean Christopherson
1 sibling, 1 reply; 285+ messages in thread
From: Jarkko Sakkinen @ 2019-05-15 11:00 UTC (permalink / raw)
To: Sean Christopherson
Cc: Andy Lutomirski, Jethro Beekman, Xing, Cedric, Hansen, Dave,
Thomas Gleixner, Dr. Greg, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Wed, May 15, 2019 at 01:35:31PM +0300, Jarkko Sakkinen wrote:
> This brings me to an open question in Andy's model: lets say that we
> change the source for SIGSTRUCT from memory address to fd. How can the
> policy prevent the use not creating a file containing a SIGSTRUCT and
> passing fd of that to the EINIT ioctl?
Also wondering if a path would be better than plain fd for defining a
reasonable policy i.e. have sigstruct_path as part of the ioctl
parameters and not sigstruct_fd.
/Jarkko
^ permalink raw reply [flat|nested] 285+ messages in thread
* Re: [PATCH v20 00/28] Intel SGX1 support
2019-05-15 10:35 ` [PATCH v20 00/28] Intel SGX1 support Jarkko Sakkinen
2019-05-15 11:00 ` Jarkko Sakkinen
@ 2019-05-15 13:21 ` Sean Christopherson
2019-05-16 5:01 ` Jarkko Sakkinen
1 sibling, 1 reply; 285+ messages in thread
From: Sean Christopherson @ 2019-05-15 13:21 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Andy Lutomirski, Jethro Beekman, Xing, Cedric, Hansen, Dave,
Thomas Gleixner, Dr. Greg, Linus Torvalds, LKML, X86 ML,
linux-sgx, Andrew Morton, nhorman, npmccallum, Ayoun, Serge,
Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes
On Wed, May 15, 2019 at 01:35:31PM +0300, Jarkko Sakkinen wrote:
> On Tue, May 14, 2019 at 01:45:27PM -0700, Sean Christopherson wrote:
> > On Tue, May 14, 2019 at 08:13:36AM -0700, Andy Lutomirski wrote:
> > > I think it's as simple as requiring that, if SECINFO.X is set, then
> > > the src pointer points to the appropriate number of bytes of
> > > executable memory. (Unless there's some way for an enclave to change
> > > SECINFO after the fact -- is there?)
> >
> > Nit: SECINFO is just the struct passed to EADD, I think what you're really
> > asking is "can the EPCM permissions be changed after the fact".
> >
> > And the answer is, yes.
> >
> > On SGX2 hardware, the enclave can extend the EPCM permissions at runtime
> > via ENCLU[EMODPE], e.g. to make a page writable.
>
> Small correction: it is EMODPR.
No, I'm referring to EMODPE, note the ENCLU classification.
> Anyway, it is good to mention that these would require EACCEPT from the
> enclave side. In order to take advantage of this is in a malicous
> enclave, one would require SELinux/IMA/whatnot policy to have permitted
> it in the first place.
EMODPE doesn't require EACCEPT or any equivalent from the kernel. As
you alluded to, the page tables would still need to allow PROT_EXEC. I
was simply trying to answer Andy's question regarding SECINFO.
> Thus, it cannot be said that it breaks the security policy if this would
> happen because policy has allowed to use the particular enclave.
^