Only valid owning capabilities have the ability to modify an address space by making use of the appropriate syscalls, any deviation from this method must result in immediate failure of the syscall. When a capability is created it is assigned the maximum permissions it may ever have by passing PROT_MAX(max_prot) as one of the prot flags. Any attempt to increase the permissions beyond this would result in failure of the syscall.
A test to validate the parmeters passed to address space management syscalls has been added.
Signed-off-by: Chaitanya S Prakash chaitanyas.prakash@arm.com --- tools/testing/selftests/arm64/morello/mmap.c | 76 ++++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/tools/testing/selftests/arm64/morello/mmap.c b/tools/testing/selftests/arm64/morello/mmap.c index 00d4c5ea9703..184dcf4ddc92 100644 --- a/tools/testing/selftests/arm64/morello/mmap.c +++ b/tools/testing/selftests/arm64/morello/mmap.c @@ -128,6 +128,76 @@ static void purecap_map_growsdown(void) ASSERT_EQ((unsigned long)addr, (unsigned long)-EOPNOTSUPP); }
+ +/* test to validate parameters passed to address space management syscalls */ +static inline void purecap_param_check(void) +{ + void *addr, *addr_cap, *null_cap_addr; + ptraddr_t address; + int retval; + int max_prot = PROT_READ | PROT_WRITE | PROT_EXEC; + int prot = max_prot; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + + /* generate null-derived capability to be used later*/ + addr = mmap(NULL, MMAP_SIZE, prot, flags, -1, 0); + ASSERT_GT((unsigned long)addr, 0); + address = cheri_address_get(addr); + null_cap_addr = (void *)(uintptr_t)address; + retval = munmap(addr, MMAP_SIZE); + ASSERT_EQ(retval, 0); + + /* passing invalid capability to sycall */ + addr = mmap(NULL, MMAP_SIZE, prot, flags, -1, 0); + ASSERT_GT((unsigned long)addr, 0); + addr_cap = cheri_tag_clear(addr); + retval = munmap(addr_cap, MMAP_SIZE); + ASSERT_EQ(retval, -EINVAL); + + retval = munmap(addr, MMAP_SIZE); + ASSERT_EQ(retval, 0); + + /* increase permission beyond the maximum prot specified for the mapping */ + addr = mmap(NULL, MMAP_SIZE, PROT_MAX(PROT_READ | PROT_WRITE) | + PROT_READ | PROT_WRITE, flags, -1, 0); + ASSERT_GT((unsigned long)addr, 0); + retval = mprotect(addr, MMAP_SIZE, PROT_EXEC); + ASSERT_EQ(retval, -EINVAL); + + retval = munmap(addr, MMAP_SIZE); + ASSERT_EQ(retval, 0); + + /* max_prot has fewer permissions than prot */ + max_prot = (prot & ~(PROT_READ)); + addr = mmap(NULL, MMAP_SIZE, PROT_MAX(max_prot) | prot, flags, -1, 0); + ASSERT_EQ((unsigned long)addr, (unsigned long)-EINVAL); + + /* max_prot has more permissions than prot */ + max_prot = prot; + prot = (prot & ~(PROT_WRITE)); + addr = mmap(NULL, MMAP_SIZE, PROT_MAX(max_prot) | prot, flags, -1, 0); + ASSERT_GT((unsigned long)addr, 0); + + retval = mprotect(addr, MMAP_SIZE, PROT_WRITE); + ASSERT_NE(retval, -EINVAL); + EXPECT_EQ(0, probe_mem_range(addr, MMAP_SIZE, + PROBE_MODE_TOUCH | PROBE_MODE_VERIFY)); + + retval = munmap(addr, MMAP_SIZE); + ASSERT_EQ(retval, 0); + + /* repeat positive max_prot test with null-derived capability */ + addr = mmap(null_cap_addr, MMAP_SIZE, PROT_MAX(max_prot) | prot, + flags | MAP_FIXED, -1, 0); + ASSERT_GT((unsigned long)addr, 0); + + retval = mprotect(addr, MMAP_SIZE, PROT_WRITE); + ASSERT_EQ(retval, 0); + + retval = munmap(addr, MMAP_SIZE); + ASSERT_EQ(retval, 0); +} + TEST(test_syscall_mmap) { syscall_mmap(); @@ -143,10 +213,16 @@ TEST(test_purecap_map_growsdown) purecap_map_growsdown(); }
+TEST(test_purecap_param_check) +{ + purecap_param_check(); +} + int main(void) { test_syscall_mmap(); test_syscall_mmap2(); test_purecap_map_growsdown(); + test_purecap_param_check(); return 0; }