Skip to content

Commit f13dd04

Browse files
committed
Add test for vanilla PAuth without helpers and refactor tests
1 parent 6609dd8 commit f13dd04

File tree

1 file changed

+131
-33
lines changed

1 file changed

+131
-33
lines changed

tests/unit/test_arm64.c

Lines changed: 131 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -740,31 +740,23 @@ static bool test_arm64_pauth_cp_reg_update(uc_engine *uc, const uint32_t cpregid
740740
return (((reg.val & setmask) == setmask) && ((reg.val & clearmask) == 0));
741741
}
742742

743-
static void test_arm64_pauth(void)
743+
static void test_arm64_pauth_check_cpu_feat(uc_engine *uc)
744744
{
745-
uc_engine *uc;
746-
const char code_paciza_x1[] = "\xe1\x23\xc1\xda"; // paciza x1
747-
748-
// We expect a PAC added somewhere in pac_mask bits in order to make the
749-
// test agnostic of TxSZ and TBI.
750-
751-
const uint64_t some_unsigned_pointer = 0x0000aaaabbbbccccULL;
752-
const uint64_t pac_mask = 0xffff000000000000ULL & ~(1ULL << 55);
753-
754-
OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
755-
OK(uc_ctl_set_cpu_model(uc, UC_CPU_ARM64_MAX));
756-
757745
// Check the CPU actually supports any form of PAuth, i.e. any APA or API
758746
// bits are set. At the time of writing, UC_CPU_ARM64_A72 does not support
759-
// PAuth, but UC_CPU_ARM64_MAX does. This is not required for the test,
760-
// but helps with diagnostics when the selected CPU does not support PAuth.
747+
// PAuth, but UC_CPU_ARM64_MAX does. This check is not required for any of
748+
// the PAuth tests to work, but helps with diagnostics when the selected
749+
// CPU does not support PAuth.
761750

762751
const uint32_t ID_AA64ISAR1_EL1[5] = { 0b11, 0b000, 0b0000, 0b0110, 0b001 };
763752
const uint64_t ID_AA64ISAR1_EL1_APA_API_MASK = (0b1111ULL << 4) | (0b1111ULL << 8);
764753
uint64_t ID_AA64ISAR1_EL1_bits = test_arm64_pauth_cp_reg_read(uc, ID_AA64ISAR1_EL1);
765754
TEST_CHECK((ID_AA64ISAR1_EL1_bits & ID_AA64ISAR1_EL1_APA_API_MASK) != 0);
755+
}
766756

767-
// Minimal PAuth setup, enabling only IA and IB. The test is agnostic to
757+
static void test_arm64_pauth_setup(uc_engine *uc)
758+
{
759+
// Minimal PAuth setup, enabling only IA and IB. The tests are agnostic to
768760
// VA size and MTE config, so don't bother touching TCR_EL1 for now.
769761

770762
const uint32_t SCR_EL3[5] = { 0b11, 0b110, 0b0001, 0b0001, 0b000 };
@@ -791,44 +783,149 @@ static void test_arm64_pauth(void)
791783
test_arm64_pauth_cp_reg_write(uc, APIBKeyHi_EL1, 0xDDDDDDDDDDDDDDDDULL);
792784
test_arm64_pauth_cp_reg_write(uc, APDAKeyLo_EL1, 0xAAAAAAAAAAAAAAAAULL); // == IA
793785
test_arm64_pauth_cp_reg_write(uc, APDAKeyHi_EL1, 0xBBBBBBBBBBBBBBBBULL);
786+
}
787+
788+
static void test_arm64_pauth_vanilla(void) {
789+
// PAuth test w/o using any uc_ctl interfaces, just PAuth on the CPU.
790+
791+
uc_engine *uc;
792+
const char code_paciza_x1[] = "\xe1\x23\xc1\xda"; // paciza x1
793+
const char code_autiza_x1[] = "\xe1\x33\xc1\xda"; // autiza x1
794+
const char code_autizb_x1[] = "\xe1\x37\xc1\xda"; // autizb x1
795+
const char code_autdza_x1[] = "\xe1\x3b\xc1\xda"; // autdza x1
796+
const char code_autia_x1_x0[] = "\x01\x10\xc1\xda"; // autia x1, x0
797+
const char code_xpaci_x1[] = "\xe1\x43\xc1\xda"; // xpaci x1
798+
799+
// We expect a PAC added somewhere in pac_mask bits in order to make the
800+
// test agnostic of TxSZ and TBI.
801+
802+
const uint64_t some_unsigned_pointer = 0x0000aaaabbbbccccULL;
803+
const uint64_t pac_mask = 0xffff000000000000ULL & ~(1ULL << 55);
804+
805+
OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
806+
OK(uc_ctl_set_cpu_model(uc, UC_CPU_ARM64_MAX));
807+
OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL));
808+
809+
test_arm64_pauth_check_cpu_feat(uc);
810+
test_arm64_pauth_setup(uc);
811+
812+
// Verify that paciza signs a pointer.
813+
814+
OK(uc_reg_write(uc, UC_ARM64_REG_X1, &some_unsigned_pointer));
815+
OK(uc_mem_write(uc, code_start, code_paciza_x1, sizeof(code_paciza_x1)));
816+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code_paciza_x1) - 1, 0, 0));
817+
uint64_t signed_pointer = 0;
818+
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &signed_pointer));
819+
TEST_CHECK(signed_pointer != some_unsigned_pointer);
820+
TEST_CHECK((signed_pointer & pac_mask) != 0);
821+
822+
// Verify that xpaci results in original pointer.
823+
824+
OK(uc_mem_write(uc, code_start, code_xpaci_x1, sizeof(code_xpaci_x1)));
825+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code_xpaci_x1) - 1, 0, 0));
826+
uint64_t stripped_pointer = 0;
827+
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &stripped_pointer));
828+
TEST_CHECK(stripped_pointer == some_unsigned_pointer);
829+
830+
// Verify autia behaviour.
831+
832+
OK(uc_reg_write(uc, UC_ARM64_REG_X1, &some_unsigned_pointer));
833+
OK(uc_mem_write(uc, code_start, code_autiza_x1, sizeof(code_autiza_x1)));
834+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code_autiza_x1) - 1, 0, 0));
835+
uint64_t authenticated_pointer = 0;
836+
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &authenticated_pointer));
837+
TEST_CHECK((authenticated_pointer & pac_mask) != 0); // unsigned pointer is invalid
838+
839+
OK(uc_reg_write(uc, UC_ARM64_REG_X1, &signed_pointer));
840+
OK(uc_mem_write(uc, code_start, code_autiza_x1, sizeof(code_autiza_x1)));
841+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code_autiza_x1) - 1, 0, 0));
842+
authenticated_pointer = 0;
843+
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &authenticated_pointer));
844+
TEST_CHECK((authenticated_pointer & pac_mask) == 0); // signed pointer is valid
845+
846+
uint64_t diversifier = 1337;
847+
OK(uc_reg_write(uc, UC_ARM64_REG_X1, &signed_pointer));
848+
OK(uc_reg_write(uc, UC_ARM64_REG_X0, &diversifier));
849+
OK(uc_mem_write(uc, code_start, code_autia_x1_x0, sizeof(code_autia_x1_x0)));
850+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code_autia_x1_x0) - 1, 0, 0));
851+
authenticated_pointer = 0;
852+
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &authenticated_pointer));
853+
TEST_CHECK((authenticated_pointer & pac_mask) != 0); // wrong diversifier is invalid
854+
855+
OK(uc_reg_write(uc, UC_ARM64_REG_X1, &signed_pointer));
856+
OK(uc_mem_write(uc, code_start, code_autizb_x1, sizeof(code_autizb_x1)));
857+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code_autizb_x1) - 1, 0, 0));
858+
authenticated_pointer = 0;
859+
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &authenticated_pointer));
860+
TEST_CHECK((authenticated_pointer & pac_mask) != 0); // wrong but enabled key is invalid
861+
862+
OK(uc_reg_write(uc, UC_ARM64_REG_X1, &signed_pointer));
863+
OK(uc_mem_write(uc, code_start, code_autdza_x1, sizeof(code_autdza_x1)));
864+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code_autdza_x1) - 1, 0, 0));
865+
authenticated_pointer = 0;
866+
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &authenticated_pointer));
867+
TEST_CHECK((authenticated_pointer & pac_mask) != 0); // disabled but same value key is invalid
868+
869+
OK(uc_close(uc));
870+
}
871+
872+
static void test_arm64_pauth_ctl(void)
873+
{
874+
// PAuth test for the uc_ctl interfaces.
875+
876+
uc_engine *uc;
877+
const char code_paciza_x1[] = "\xe1\x23\xc1\xda"; // paciza x1
878+
879+
// We expect a PAC added somewhere in pac_mask bits in order to make the
880+
// test agnostic of TxSZ and TBI.
881+
882+
const uint64_t some_unsigned_pointer = 0x0000aaaabbbbccccULL;
883+
const uint64_t pac_mask = 0xffff000000000000ULL & ~(1ULL << 55);
884+
885+
OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
886+
OK(uc_ctl_set_cpu_model(uc, UC_CPU_ARM64_MAX));
887+
OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL));
888+
889+
test_arm64_pauth_check_cpu_feat(uc);
890+
test_arm64_pauth_setup(uc);
794891

795892
// Verify that paciza and uc_ctl_pauth_sign() result in the same signed
796893
// pointer.
797894

798-
uint64_t x1 = some_unsigned_pointer;
799-
OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL));
800895
OK(uc_mem_write(uc, code_start, code_paciza_x1, sizeof(code_paciza_x1)));
801-
OK(uc_reg_write(uc, UC_ARM64_REG_X1, &x1));
896+
OK(uc_reg_write(uc, UC_ARM64_REG_X1, &some_unsigned_pointer));
802897
OK(uc_emu_start(uc, code_start, code_start + sizeof(code_paciza_x1) - 1, 0, 0));
803-
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &x1));
804-
TEST_CHECK(x1 != some_unsigned_pointer);
805-
TEST_CHECK((x1 & pac_mask) != 0);
898+
uint64_t signed_pointer_paciza = 0;
899+
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &signed_pointer_paciza));
900+
TEST_CHECK(signed_pointer_paciza != some_unsigned_pointer);
901+
TEST_CHECK((signed_pointer_paciza & pac_mask) != 0);
806902

807-
uint64_t ptr = some_unsigned_pointer;
808-
OK(uc_ctl_pauth_sign(uc, ptr, UC_ARM64_PAUTH_KEY_IA, 0, &ptr));
809-
TEST_CHECK(ptr == x1);
903+
uint64_t signed_pointer = 0;
904+
OK(uc_ctl_pauth_sign(uc, some_unsigned_pointer, UC_ARM64_PAUTH_KEY_IA, 0, &signed_pointer));
905+
TEST_CHECK(signed_pointer == signed_pointer_paciza);
810906

811907
// Verify that stripping the PAC results in the original pointer.
812908

813-
OK(uc_ctl_pauth_strip(uc, ptr, UC_ARM64_PAUTH_KEY_IA, &ptr));
814-
TEST_CHECK(ptr == some_unsigned_pointer);
909+
uint64_t stripped_pointer = 0;
910+
OK(uc_ctl_pauth_strip(uc, signed_pointer, UC_ARM64_PAUTH_KEY_IA, &stripped_pointer));
911+
TEST_CHECK(stripped_pointer == some_unsigned_pointer);
815912

816913
// Verify that authenticating works as expected.
817914

818915
bool valid = true;
819-
OK(uc_ctl_pauth_auth(uc, ptr, UC_ARM64_PAUTH_KEY_IA, 0, &valid));
916+
OK(uc_ctl_pauth_auth(uc, some_unsigned_pointer, UC_ARM64_PAUTH_KEY_IA, 0, &valid));
820917
TEST_CHECK(!valid); // unsigned pointer
821918
valid = false;
822-
OK(uc_ctl_pauth_auth(uc, x1, UC_ARM64_PAUTH_KEY_IA, 0, &valid));
919+
OK(uc_ctl_pauth_auth(uc, signed_pointer, UC_ARM64_PAUTH_KEY_IA, 0, &valid));
823920
TEST_CHECK(valid); // signed pointer
824921
valid = true;
825-
OK(uc_ctl_pauth_auth(uc, x1, UC_ARM64_PAUTH_KEY_IA, 1337, &valid));
922+
OK(uc_ctl_pauth_auth(uc, signed_pointer, UC_ARM64_PAUTH_KEY_IA, 1337, &valid));
826923
TEST_CHECK(!valid); // wrong diversifier
827924
valid = true;
828-
OK(uc_ctl_pauth_auth(uc, x1, UC_ARM64_PAUTH_KEY_IB, 0, &valid));
925+
OK(uc_ctl_pauth_auth(uc, signed_pointer, UC_ARM64_PAUTH_KEY_IB, 0, &valid));
829926
TEST_CHECK(!valid); // wrong but enabled key
830927
valid = true;
831-
OK(uc_ctl_pauth_auth(uc, x1, UC_ARM64_PAUTH_KEY_DA, 0, &valid));
928+
OK(uc_ctl_pauth_auth(uc, signed_pointer, UC_ARM64_PAUTH_KEY_DA, 0, &valid));
832929
TEST_CHECK(!valid); // disabled but same value key
833930

834931
OK(uc_close(uc));
@@ -853,5 +950,6 @@ TEST_LIST = {{"test_arm64_until", test_arm64_until},
853950
{"test_arm64_mem_prot_regress", test_arm64_mem_prot_regress},
854951
{"test_arm64_mem_hook_read_write", test_arm64_mem_hook_read_write},
855952
{"test_arm64_pc_guarantee", test_arm64_pc_guarantee},
856-
{"test_arm64_pauth", test_arm64_pauth},
953+
{"test_arm64_pauth_vanilla", test_arm64_pauth_vanilla},
954+
{"test_arm64_pauth_ctl", test_arm64_pauth_ctl},
857955
{NULL, NULL}};

0 commit comments

Comments
 (0)