From: Jeff Xu <jeffxu@xxxxxxxxxx> Add selftest for pkey_enforce_api for mprotect Signed-off-by: Jeff Xu<jeffxu@xxxxxxxxxx> --- tools/testing/selftests/mm/pkey_enforce_api.c | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) diff --git a/tools/testing/selftests/mm/pkey_enforce_api.c b/tools/testing/selftests/mm/pkey_enforce_api.c index 23663c89bc9c..92aa29248e1f 100644 --- a/tools/testing/selftests/mm/pkey_enforce_api.c +++ b/tools/testing/selftests/mm/pkey_enforce_api.c @@ -833,6 +833,429 @@ void test_mprotect_child_thread(bool enforce) clean_single_address_with_pkey(pkey, ptr, size); } +// mmap one address with one page. +// assign PKEY to the address. +// munmap on the address is protected. +void test_munmap_single_address(bool enforce) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_single_address_with_pkey(enforce, size, &pkey, &ptr); + + // disable write access. + pkey_write_deny(pkey); + + ret = munmap(ptr, size); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr, size); + assert(!ret); + } + + clean_single_address_with_pkey(pkey, ptr, size); +} + +// mmap two address (continuous two pages). +// assign PKEY to them with one mprotect_pkey call (merged address). +// munmap two address in one call (merged address). +void test_munmap_two_address_merge(bool enforce) +{ + int pkey; + int ret; + void *ptr; + void *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_two_continues_fixed_address_with_pkey(enforce, size, &pkey, &ptr, + &ptr2); + + // disable write. + pkey_write_deny(pkey); + + // munmap on both addresses with one call (merged). + ret = munmap(ptr, size * 2); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr, size * 2); + assert(!ret); + } + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap two address (continuous two pages). +// assign PKEY to the second address. +// munmap on the second address is protected. +void test_munmap_two_address_deny_second(bool enforce) +{ + int pkey; + int ret; + void *ptr; + void *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_two_continues_fixed_address_protect_second_with_pkey( + enforce, size, &pkey, &ptr, &ptr2); + + // disable write through pkey. + pkey_write_deny(pkey); + + ret = munmap(ptr2, size); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + ret = munmap(ptr, size); + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr2, size); + assert(!ret); + } + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap two address (continuous two pages). +// assign PKEY to the second address. +// munmap on the range that includes the second address. +void test_munmap_two_address_deny_range(bool enforce) +{ + int pkey; + int ret; + void *ptr; + void *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_two_continues_fixed_address_protect_second_with_pkey( + enforce, size, &pkey, &ptr, &ptr2); + + // disable write through pkey. + pkey_write_deny(pkey); + + ret = munmap(ptr, size * 2); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr, size * 2); + assert(!ret); + } + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// munmap on memory range that includes the second pages is protected. +void test_munmap_vma_middle_addr(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // munmap support merge, we are going to make sure we don't regress. + ret = munmap(addr1, size * 4); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr, size * 4); + assert(!ret); + } + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// munmap from 2nd page. +void test_munmap_shrink(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // munmap support merge, we are going to make sure we don't regress. + ret = munmap(ptr2, size * 3); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr2, size * 3); + assert(!ret); + } + + ret = munmap(ptr, size); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// munmap from 2nd page but size is less than one page +void test_munmap_unaligned(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // munmap support merge, we are going to make sure we don't regress. + ret = munmap(ptr2, size - 1); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr2, size - 1); + assert(!ret); + } + + ret = munmap(ptr, size * 4); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// munmap from 2nd page but size is less than one page +void test_munmap_unaligned2(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // munmap support merge, we are going to make sure we don't regress. + ret = munmap(ptr2, size + 1); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr2, size + 1); + assert(!ret); + } + + ret = munmap(ptr, size * 4); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with one page. +// assign PKEY to the address. +// munmap on the address but with size of 4 pages(should OK). +void test_munmap_outbound_addr(bool enforce) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_single_fixed_address_with_pkey(enforce, size, &pkey, &ptr); + + // disable write through pkey. + pkey_write_deny(pkey); + + // Interesting enough, this is allowed, even the other 3 pages are not allocated. + ret = munmap(addr1, size * 4); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(addr1, size * 4); + assert(!ret); + } + + clean_single_address_with_pkey(pkey, ptr, size); +} +// mmap two addresses, with a page gap between two. +// assign pkeys on both address. +// disable access to the second address. +// munmap from start of address1 to the end of address 2, +// because there is a gap in the memory range, mprotect will fail. +void test_munmap_gapped_address_with_two_pkeys(bool enforce) +{ + int pkey, pkey2; + int ret; + void *ptr, *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_address_with_gap_two_pkeys(enforce, size, &pkey, &pkey2, &ptr, + &ptr2); + + // disable write access. + pkey_write_deny(pkey2); + + // Interesting enough, this is allowed, even there is a gap beween address 1 and 2. + ret = munmap(addr1, size * 3); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey2); + if (enforce) { + ret = munmap(addr1, size * 3); + assert(!ret); + } +} + +// use write-deny pkey and see if program can exit properly. +// This is manual test, run it at end if needed. +void test_exit_munmap_disable_write(void) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + pkey = sys_pkey_alloc(PKEY_ENFORCE_API, 0); + assert(pkey > 0); + + // allocate 1 page. + ptr = mmap(addr1, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr == addr1); + + // assign pkey to the first address. + ret = sys_mprotect_pkey(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, + pkey); + assert(!ret); + + // disable write through pkey. + pkey_write_deny(pkey); + + ret = munmap(ptr, size); + assert(ret < 0); +} + +// use disable-all pkey and see if program can exit properly. +// This is manual test, run it at end if needed. +void test_exit_munmap_disable_all(void) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + pkey = sys_pkey_alloc(PKEY_ENFORCE_API, 0); + assert(pkey > 0); + + // allocate 1 page. + ptr = mmap(addr2, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr == addr2); + + // assign pkey to the first address. + ret = sys_mprotect_pkey(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, + pkey); + assert(!ret); + + // disable write through pkey. + pkey_access_deny(pkey); + + ret = munmap(addr1, size); + assert(ret < 0); +} + void test_enforce_api(void) { for (int i = 0; i < 2; i++) { @@ -848,7 +1271,21 @@ void test_enforce_api(void) test_mprotect_unaligned2(enforce); test_mprotect_child_thread(enforce); test_mprotect_gapped_address_with_two_pkeys(enforce); + + test_munmap_single_address(enforce); + test_munmap_two_address_merge(enforce); + test_munmap_two_address_deny_second(enforce); + test_munmap_two_address_deny_range(enforce); + test_munmap_vma_middle_addr(enforce); + test_munmap_outbound_addr(enforce); + test_munmap_shrink(enforce); + test_munmap_unaligned(enforce); + test_munmap_unaligned2(enforce); + test_munmap_gapped_address_with_two_pkeys(enforce); } + + test_exit_munmap_disable_write(); + test_exit_munmap_disable_all(); } int main(void) -- 2.40.1.606.ga4b1b128d6-goog