Hi Everybody, Haitao reported encountering the following WARN while stress testing SGX with the SGX2 series [1] applied: ELDU returned 1073741837 (0x4000000d) WARNING: CPU: 72 PID: 24407 at arch/x86/kernel/cpu/sgx/encl.c:81 sgx_encl_eldu+0x3cf/0x400 ... Call Trace: <TASK> ? xa_load+0x6e/0xa0 __sgx_encl_load_page+0x3d/0x80 sgx_encl_load_page_in_vma+0x4a/0x60 sgx_vma_fault+0x7f/0x3b0 ? sysvec_call_function_single+0x66/0xd0 ? asm_sysvec_call_function_single+0x12/0x20 __do_fault+0x39/0x110 __handle_mm_fault+0x1222/0x15a0 handle_mm_fault+0xdb/0x2c0 do_user_addr_fault+0x1d1/0x650 ? exit_to_user_mode_prepare+0x45/0x170 exc_page_fault+0x76/0x170 ? asm_exc_page_fault+0x8/0x30 asm_exc_page_fault+0x1e/0x30 ... </TASK> ENCLS[ELDU] is returning a #GP when attempting to load an enclave page from the backing store into the enclave. This is likely because of a MAC verification failure. Haitao's stress testing involves running two concurrent loops of the SGX selftests on a system with 4GB EPC memory. One of the tests is modified to reduce the oversubscription heap size: diff --git a/tools/testing/selftests/sgx/main.c b/tools/testing/selftests/sgx/main.c index d480c2dd2858..12008789325b 100644 --- a/tools/testing/selftests/sgx/main.c +++ b/tools/testing/selftests/sgx/main.c @@ -398,7 +398,7 @@ TEST_F_TIMEOUT(enclave, unclobbered_vdso_oversubscribed_remove, 900) * Create enclave with additional heap that is as big as all * available physical SGX memory. */ - total_mem = get_total_epc_mem(); + total_mem = get_total_epc_mem()/16; ASSERT_NE(total_mem, 0); TH_LOG("Creating an enclave with %lu bytes heap may take a while ...", total_mem); If the the test compiled with above snippet is renamed as "test_sgx_small" and the original renamed as "test_sgx_large" the two concurrent loops are run as follows: (for i in $(seq 1 999); do echo "Iteration $i"; sudo ./test_sgx_large; done ) > log.large 2>&1 (for i in $(seq 1 9999); do echo "Iteration $i"; sudo ./test_sgx_small; done ) > log.small 2>&1 If the SGX driver is modified to always WARN when ENCLS[ELDU] encounters a #GP (see below) then the WARN appears after a few iterations of "test_sgx_large" and shows up throughout the testing. diff --git a/arch/x86/kernel/cpu/sgx/encls.h b/arch/x86/kernel/cpu/sgx/encls.h index 99004b02e2ed..68c1dbc84ed3 100644 --- a/arch/x86/kernel/cpu/sgx/encls.h +++ b/arch/x86/kernel/cpu/sgx/encls.h @@ -18,7 +18,7 @@ #define ENCLS_WARN(r, name) { \ do { \ int _r = (r); \ - WARN_ONCE(_r, "%s returned %d (0x%x)\n", (name), _r, _r); \ + WARN(_r, "%s returned %d (0x%x)\n", (name), _r, _r); \ } while (0); \ } I focused on investigating this issue the past two weeks and was able to narrow down the cause but not yet fix the issue. Now I am out of ideas and would really appreciate if you could provide guidance on how to proceed. Here is what I have learned so far: * Reverting commit 08999b2489b4 ("x86/sgx: Free backing memory after faulting the enclave page") resolves the issue. With that commit reverted the concurrent selftest loops can run to completion without encountering any WARNs. * The issue is also resolved if only the calls (introduced by commit 08999b2489b4 ("x86/sgx: Free backing memory after faulting the enclave page") ) to sgx_encl_truncate_backing_page() within __sgx_encl_eldu() are disabled. * Two issues with commit 08999b2489b4 ("x86/sgx: Free backing memory after faulting the enclave page") are fixed with patch 1 and 2 in this series. These fixes do not resolve the WARN. * There is a potential race between the reclaimer and the page fault handler while interacting with the backing store. This is addressed in patch 3, but it does not resolve the WARN. * Lockdep did reveal a locking issue in SGX2 - the EAUG flow should not do direct reclaim since the page fault handler already has mmap_lock that the reclaimer will attempt to obtain. This will be fixed in the next version of SGX2 but that fix does not resolve the WARN. * Since ENCLS[ELDU] returns #GP on MAC verification failure a possible cause could be if a problem occurs while loading the page from backing store. sgx_encl_get_backing_page() is used to both allocate backing storage in the ENCLS[EWB] flow as well as to lookup backing storage in the ENCLS[ELDU] flow. As part of the investigation the interface was split to ensure that backing storage is only allocated during the ENCLS[EWB] flow and only lookup occurs during the ENCLS[ELDU] flow. This can be found in patch 4. This change revealed that there are cases during ENCLS[ELDU] flow when the backing page is not present - attempting to lookup a backing page returns -ENOENT. Before patch 4 a new backing page would be allocated and thus trigger a #GP when ENCLS[ELDU] is attempted. This change is not a fix, the code path just fails earlier now, but it reveals a cause of the WARNs encountered (MAC verification will fail on a newly allocated page) but not why the pages cannot be found in the backing store. With this change the number of WARNs are reduced significantly but not completely. Even with this patch applied the WARN was encountered once while running the stress selftests. Could you please advise on how to proceed? Do you perhaps have ideas why the backing store could not contain a page when it is loaded back during the ENCLS[ELDU] flow? There is some interesting text in the comments within shmem_fault() that hints that faulting pages should be avoided into holes as they are being punched ... but I do not understand those details and would really appreciate guidance on how to understand what is going on here. These SGX2 tests exercise the concurrent operation of reclaimer and page fault handler and have a good track record of locating issues. Thanks to it we already have: 8795359e35bc ("x86/sgx: Silence softlockup detection when releasing large enclaves") ac5d272a0ad0 ("x86/sgx: Fix free page accounting") While this issue is triggered by the SGX2 tests I cannot find a connection with the SGX2 code paths. I have made a few attempts to create an equivalent test for SGX1 but have not yet had success to create a SGX1 test that can trigger this issue. These patches are based on v5.18-rc4 with the SGX2 series applied but do not touch SGX2 code paths. Patch 1,2, and 3 also apply cleanly to v5.18-rc4 while patch 4 has a conflict in arch/x86/kernel/cpu/sgx/encl.h due to the SGX2 declarations. Your feedback will be greatly appreciated. Thank you very much Reinette [1] https://lore.kernel.org/lkml/cover.1649878359.git.reinette.chatre@xxxxxxxxx/ Reinette Chatre (4): x86/sgx: Do not free backing memory on ENCLS[ELDU] failure x86/sgx: Set dirty bit after modifying page contents x86/sgx: Obtain backing storage page with enclave mutex held x86/sgx: Do not allocate backing pages when loading from backing store arch/x86/kernel/cpu/sgx/encl.c | 89 ++++++++++++++++++++++++++++----- arch/x86/kernel/cpu/sgx/encl.h | 8 ++- arch/x86/kernel/cpu/sgx/ioctl.c | 1 + arch/x86/kernel/cpu/sgx/main.c | 15 +++--- 4 files changed, 93 insertions(+), 20 deletions(-) -- 2.25.1