@@ -25,6 +25,18 @@ static const uint64_t MAGIC = 0x1122334455667788ULL;
2525static const uint64_t MAGIC2 = 0x8877665544332211ULL ;
2626vdso_sgx_enter_enclave_t vdso_sgx_enter_enclave ;
2727
28+ /*
29+ * Security Information (SECINFO) data structure needed by a few SGX
30+ * instructions (eg. ENCLU[EACCEPT] and ENCLU[EMODPE]) holds meta-data
31+ * about an enclave page. &enum sgx_secinfo_page_state specifies the
32+ * secinfo flags used for page state.
33+ */
34+ enum sgx_secinfo_page_state {
35+ SGX_SECINFO_PENDING = (1 << 3 ),
36+ SGX_SECINFO_MODIFIED = (1 << 4 ),
37+ SGX_SECINFO_PR = (1 << 5 ),
38+ };
39+
2840struct vdso_symtab {
2941 Elf64_Sym * elf_symtab ;
3042 const char * elf_symstrtab ;
@@ -555,4 +567,206 @@ TEST_F(enclave, pte_permissions)
555567 EXPECT_EQ (self -> run .exception_addr , 0 );
556568}
557569
570+ /*
571+ * Enclave page permission test.
572+ *
573+ * Modify and restore enclave page's EPCM (enclave) permissions from
574+ * outside enclave (ENCLS[EMODPR] via kernel) as well as from within
575+ * enclave (via ENCLU[EMODPE]). Check for page fault if
576+ * VMA allows access but EPCM permissions do not.
577+ */
578+ TEST_F (enclave , epcm_permissions )
579+ {
580+ struct sgx_enclave_restrict_permissions restrict_ioc ;
581+ struct encl_op_get_from_addr get_addr_op ;
582+ struct encl_op_put_to_addr put_addr_op ;
583+ struct encl_op_eaccept eaccept_op ;
584+ struct encl_op_emodpe emodpe_op ;
585+ unsigned long data_start ;
586+ int ret , errno_save ;
587+
588+ ASSERT_TRUE (setup_test_encl (ENCL_HEAP_SIZE_DEFAULT , & self -> encl , _metadata ));
589+
590+ memset (& self -> run , 0 , sizeof (self -> run ));
591+ self -> run .tcs = self -> encl .encl_base ;
592+
593+ /*
594+ * Ensure kernel supports needed ioctl() and system supports needed
595+ * commands.
596+ */
597+ memset (& restrict_ioc , 0 , sizeof (restrict_ioc ));
598+
599+ ret = ioctl (self -> encl .fd , SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS ,
600+ & restrict_ioc );
601+ errno_save = ret == -1 ? errno : 0 ;
602+
603+ /*
604+ * Invalid parameters were provided during sanity check,
605+ * expect command to fail.
606+ */
607+ ASSERT_EQ (ret , -1 );
608+
609+ /* ret == -1 */
610+ if (errno_save == ENOTTY )
611+ SKIP (return ,
612+ "Kernel does not support SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS ioctl()" );
613+ else if (errno_save == ENODEV )
614+ SKIP (return , "System does not support SGX2" );
615+
616+ /*
617+ * Page that will have its permissions changed is the second data
618+ * page in the .data segment. This forms part of the local encl_buffer
619+ * within the enclave.
620+ *
621+ * At start of test @data_start should have EPCM as well as PTE and
622+ * VMA permissions of RW.
623+ */
624+
625+ data_start = self -> encl .encl_base +
626+ encl_get_data_offset (& self -> encl ) + PAGE_SIZE ;
627+
628+ /*
629+ * Sanity check that page at @data_start is writable before making
630+ * any changes to page permissions.
631+ *
632+ * Start by writing MAGIC to test page.
633+ */
634+ put_addr_op .value = MAGIC ;
635+ put_addr_op .addr = data_start ;
636+ put_addr_op .header .type = ENCL_OP_PUT_TO_ADDRESS ;
637+
638+ EXPECT_EQ (ENCL_CALL (& put_addr_op , & self -> run , true), 0 );
639+
640+ EXPECT_EEXIT (& self -> run );
641+ EXPECT_EQ (self -> run .exception_vector , 0 );
642+ EXPECT_EQ (self -> run .exception_error_code , 0 );
643+ EXPECT_EQ (self -> run .exception_addr , 0 );
644+
645+ /*
646+ * Read memory that was just written to, confirming that
647+ * page is writable.
648+ */
649+ get_addr_op .value = 0 ;
650+ get_addr_op .addr = data_start ;
651+ get_addr_op .header .type = ENCL_OP_GET_FROM_ADDRESS ;
652+
653+ EXPECT_EQ (ENCL_CALL (& get_addr_op , & self -> run , true), 0 );
654+
655+ EXPECT_EQ (get_addr_op .value , MAGIC );
656+ EXPECT_EEXIT (& self -> run );
657+ EXPECT_EQ (self -> run .exception_vector , 0 );
658+ EXPECT_EQ (self -> run .exception_error_code , 0 );
659+ EXPECT_EQ (self -> run .exception_addr , 0 );
660+
661+ /*
662+ * Change EPCM permissions to read-only. Kernel still considers
663+ * the page writable.
664+ */
665+ memset (& restrict_ioc , 0 , sizeof (restrict_ioc ));
666+
667+ restrict_ioc .offset = encl_get_data_offset (& self -> encl ) + PAGE_SIZE ;
668+ restrict_ioc .length = PAGE_SIZE ;
669+ restrict_ioc .permissions = SGX_SECINFO_R ;
670+
671+ ret = ioctl (self -> encl .fd , SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS ,
672+ & restrict_ioc );
673+ errno_save = ret == -1 ? errno : 0 ;
674+
675+ EXPECT_EQ (ret , 0 );
676+ EXPECT_EQ (errno_save , 0 );
677+ EXPECT_EQ (restrict_ioc .result , 0 );
678+ EXPECT_EQ (restrict_ioc .count , 4096 );
679+
680+ /*
681+ * EPCM permissions changed from kernel, need to EACCEPT from enclave.
682+ */
683+ eaccept_op .epc_addr = data_start ;
684+ eaccept_op .flags = SGX_SECINFO_R | SGX_SECINFO_REG | SGX_SECINFO_PR ;
685+ eaccept_op .ret = 0 ;
686+ eaccept_op .header .type = ENCL_OP_EACCEPT ;
687+
688+ EXPECT_EQ (ENCL_CALL (& eaccept_op , & self -> run , true), 0 );
689+
690+ EXPECT_EEXIT (& self -> run );
691+ EXPECT_EQ (self -> run .exception_vector , 0 );
692+ EXPECT_EQ (self -> run .exception_error_code , 0 );
693+ EXPECT_EQ (self -> run .exception_addr , 0 );
694+ EXPECT_EQ (eaccept_op .ret , 0 );
695+
696+ /*
697+ * EPCM permissions of page is now read-only, expect #PF
698+ * on EPCM when attempting to write to page from within enclave.
699+ */
700+ put_addr_op .value = MAGIC2 ;
701+
702+ EXPECT_EQ (ENCL_CALL (& put_addr_op , & self -> run , true), 0 );
703+
704+ EXPECT_EQ (self -> run .function , ERESUME );
705+ EXPECT_EQ (self -> run .exception_vector , 14 );
706+ EXPECT_EQ (self -> run .exception_error_code , 0x8007 );
707+ EXPECT_EQ (self -> run .exception_addr , data_start );
708+
709+ self -> run .exception_vector = 0 ;
710+ self -> run .exception_error_code = 0 ;
711+ self -> run .exception_addr = 0 ;
712+
713+ /*
714+ * Received AEX but cannot return to enclave at same entrypoint,
715+ * need different TCS from where EPCM permission can be made writable
716+ * again.
717+ */
718+ self -> run .tcs = self -> encl .encl_base + PAGE_SIZE ;
719+
720+ /*
721+ * Enter enclave at new TCS to change EPCM permissions to be
722+ * writable again and thus fix the page fault that triggered the
723+ * AEX.
724+ */
725+
726+ emodpe_op .epc_addr = data_start ;
727+ emodpe_op .flags = SGX_SECINFO_R | SGX_SECINFO_W ;
728+ emodpe_op .header .type = ENCL_OP_EMODPE ;
729+
730+ EXPECT_EQ (ENCL_CALL (& emodpe_op , & self -> run , true), 0 );
731+
732+ EXPECT_EEXIT (& self -> run );
733+ EXPECT_EQ (self -> run .exception_vector , 0 );
734+ EXPECT_EQ (self -> run .exception_error_code , 0 );
735+ EXPECT_EQ (self -> run .exception_addr , 0 );
736+
737+ /*
738+ * Attempt to return to main TCS to resume execution at faulting
739+ * instruction, PTE should continue to allow writing to the page.
740+ */
741+ self -> run .tcs = self -> encl .encl_base ;
742+
743+ /*
744+ * Wrong page permissions that caused original fault has
745+ * now been fixed via EPCM permissions.
746+ * Resume execution in main TCS to re-attempt the memory access.
747+ */
748+ self -> run .tcs = self -> encl .encl_base ;
749+
750+ EXPECT_EQ (vdso_sgx_enter_enclave ((unsigned long )& put_addr_op , 0 , 0 ,
751+ ERESUME , 0 , 0 ,
752+ & self -> run ),
753+ 0 );
754+
755+ EXPECT_EEXIT (& self -> run );
756+ EXPECT_EQ (self -> run .exception_vector , 0 );
757+ EXPECT_EQ (self -> run .exception_error_code , 0 );
758+ EXPECT_EQ (self -> run .exception_addr , 0 );
759+
760+ get_addr_op .value = 0 ;
761+
762+ EXPECT_EQ (ENCL_CALL (& get_addr_op , & self -> run , true), 0 );
763+
764+ EXPECT_EQ (get_addr_op .value , MAGIC2 );
765+ EXPECT_EEXIT (& self -> run );
766+ EXPECT_EQ (self -> run .user_data , 0 );
767+ EXPECT_EQ (self -> run .exception_vector , 0 );
768+ EXPECT_EQ (self -> run .exception_error_code , 0 );
769+ EXPECT_EQ (self -> run .exception_addr , 0 );
770+ }
771+
558772TEST_HARNESS_MAIN
0 commit comments