@@ -25,6 +25,18 @@ static const uint64_t MAGIC = 0x1122334455667788ULL;
25
25
static const uint64_t MAGIC2 = 0x8877665544332211ULL ;
26
26
vdso_sgx_enter_enclave_t vdso_sgx_enter_enclave ;
27
27
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
+
28
40
struct vdso_symtab {
29
41
Elf64_Sym * elf_symtab ;
30
42
const char * elf_symstrtab ;
@@ -555,4 +567,206 @@ TEST_F(enclave, pte_permissions)
555
567
EXPECT_EQ (self -> run .exception_addr , 0 );
556
568
}
557
569
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
+
558
772
TEST_HARNESS_MAIN
0 commit comments