As mremap() doesn't take prot flags into consideration, the permissions of the original address are retained. If the permissions of the new address do not match the set of permissions of the old address, the syscall fails with a -EINVAL error code. A test to verify this behaviour has been added.
Signed-off-by: Chaitanya S Prakash chaitanyas.prakash@arm.com --- tools/testing/selftests/arm64/morello/mmap.c | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+)
diff --git a/tools/testing/selftests/arm64/morello/mmap.c b/tools/testing/selftests/arm64/morello/mmap.c index aa552c8affba..3b8e99aafedc 100644 --- a/tools/testing/selftests/arm64/morello/mmap.c +++ b/tools/testing/selftests/arm64/morello/mmap.c @@ -165,6 +165,68 @@ static void purecap_map_growsdown(void) ASSERT_EQ((unsigned long)addr, (unsigned long)-EOPNOTSUPP); }
+/* test to verify mremap() behaviour when permissions are modified */ +static void purecap_mremap_perms_check(void) +{ + void *old_addr, *new_addr, *ret; + int retval; + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + size_t old_perms, new_perms; + + /* permissions of capability returned by mremap must match the + * permissions returned by the original mapping */ + old_addr = mmap(NULL, MMAP_SIZE_REDUCED, prot, flags, -1, 0); + ASSERT_GT((unsigned long)old_addr, 0); + old_perms = cheri_perms_get(old_addr); + new_addr = mremap(old_addr, MMAP_SIZE_REDUCED, MMAP_SIZE, + MREMAP_MAYMOVE, 0); + ASSERT_GT((unsigned long)new_addr, 0); + new_perms = cheri_perms_get(new_addr); + ASSERT_EQ(old_perms, new_perms); + EXPECT_EQ(0, probe_mem_range(new_addr, MMAP_SIZE, + PROBE_MODE_TOUCH | PROBE_MODE_VERIFY)); + + retval = munmap(new_addr, MMAP_SIZE); + ASSERT_EQ(retval, 0); + + /* remapping to a new_addr having reduced permissions from old_addr */ + old_addr = mmap(NULL, MMAP_SIZE_REDUCED, PROT_MAX(prot | PROT_EXEC) | + prot | PROT_EXEC, flags, -1, 0); + ASSERT_GT((unsigned long)old_addr, 0); + + new_addr = mmap(NULL, MMAP_SIZE, PROT_MAX(prot) | prot, flags, -1, 0); + ASSERT_GT((unsigned long)new_addr, 0); + retval = munmap(new_addr, MMAP_SIZE); + ASSERT_EQ(0, retval); + + ret = mremap(old_addr, MMAP_SIZE_REDUCED, MMAP_SIZE, + MREMAP_MAYMOVE | MREMAP_FIXED, new_addr); + + EXPECT_EQ(0, probe_mem_range(ret, MMAP_SIZE, + PROBE_MODE_TOUCH | PROBE_MODE_VERIFY)); + + retval = munmap(ret, MMAP_SIZE); + ASSERT_EQ(retval, 0); + + /* remapping to new_addr having increased permissions from old_addr */ + old_addr = mmap(NULL, MMAP_SIZE_REDUCED, PROT_MAX(prot) | prot, flags, + -1, 0); + ASSERT_GT((unsigned long)old_addr, 0); + + new_addr = mmap(NULL, MMAP_SIZE, PROT_MAX(prot | PROT_EXEC) | prot | + PROT_EXEC, flags, -1, 0); + ASSERT_GT((unsigned long)new_addr, 0); + retval = munmap(new_addr, MMAP_SIZE); + ASSERT_EQ(retval, 0); + + ret = mremap(old_addr, MMAP_SIZE_REDUCED, MMAP_SIZE, + MREMAP_MAYMOVE | MREMAP_FIXED, new_addr); + ASSERT_EQ((unsigned long)ret, (unsigned long)-EINVAL); + + retval = munmap(old_addr, MMAP_SIZE_REDUCED); + ASSERT_EQ(retval, 0); +}
/* test to validate parameters passed to address space management syscalls */ static inline void purecap_param_check(void) @@ -507,6 +569,11 @@ TEST(test_purecap_mremap_bounds_check) purecap_mremap_bounds_check(); }
+TEST(test_purecap_mremap_perms_check) +{ + purecap_mremap_perms_check(); +} + int main(void) { test_syscall_mmap(); @@ -516,5 +583,6 @@ int main(void) test_purecap_range_check(); test_purecap_mmap_bounds_check(); test_purecap_mremap_bounds_check(); + test_purecap_mremap_perms_check(); return 0; }