On 03/10/2023 07:41, Chaitanya S Prakash wrote:
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. An owning capability returned by mmap() does not include LoadCap and StoreCap permissions if the mapping is shared. A check to verify the same has been added.
mremap() doesn't take a prot argument and retains the permissions of the original capability. If the permissions of the new capability do not match the set of permissions of the old capabaility, the syscall fails with a -EINVAL error code. Tests to verify the above behaviour have been added.
Signed-off-by: Chaitanya S Prakash chaitanyas.prakash@arm.com
tools/testing/selftests/arm64/morello/mmap.c | 130 +++++++++++++++++++ 1 file changed, 130 insertions(+)
diff --git a/tools/testing/selftests/arm64/morello/mmap.c b/tools/testing/selftests/arm64/morello/mmap.c index 645d6b977bdf..5c6c8e8efd75 100644 --- a/tools/testing/selftests/arm64/morello/mmap.c +++ b/tools/testing/selftests/arm64/morello/mmap.c @@ -15,6 +15,7 @@ #define MMAP_SIZE ((1ULL << 16) << 1) /* 64k x 2 */ #define MMAP_SIZE_REDUCED (MMAP_SIZE >> 1) #define FILE_PERM 0666 +#define PROT_ALL (PROT_READ | PROT_WRITE | PROT_EXEC) #define PROBE_MODE_TOUCH 0x01 #define PROBE_MODE_VERIFY 0x02 @@ -465,6 +466,134 @@ TEST(test_mremap_bounds_check) ASSERT_EQ(retval, 0); } +/* test to verify mremap() behaviour when permissions are modified */ +TEST(test_permissions) +{
- void *addr, *ptr, *old_ptr, *new_ptr, *ret;
- int flags, retval;
- int prot, max_prot;
- size_t perms, old_perms, new_perms;
- addr = (void *)(uintcap_t)pagesize;
It would be more readable to move it next to where it is used (since it used just once). Also uintptr_t is preferable to uintcap_t (the former suggests a pointer represented as integer in general, while the latter suggests something that is actually specific to capabilities).
- /* increase permission beyond the maximum prot specified for the mapping */
- flags = MAP_PRIVATE | MAP_ANONYMOUS;
- max_prot = PROT_READ | PROT_WRITE;
- prot = PROT_READ;
- ptr = mmap(NULL, MMAP_SIZE, PROT_MAX(max_prot) | prot,
flags, -1, 0);
- ASSERT_FALSE(IS_ERR_VALUE(ptr));
- retval = mprotect(ptr, MMAP_SIZE, PROT_EXEC);
- ASSERT_EQ(retval, -EINVAL);
- retval = munmap(ptr, MMAP_SIZE);
- ASSERT_EQ(retval, 0);
- /* max_prot has fewer permissions than prot */
- flags = MAP_PRIVATE | MAP_ANONYMOUS;
- max_prot = PROT_WRITE | PROT_EXEC;
- prot = PROT_ALL;
- ptr = mmap(NULL, MMAP_SIZE, PROT_MAX(max_prot) | prot, flags, -1, 0);
- VERIFY_ERRNO((unsigned long)ptr, (unsigned long)-EINVAL);
- /* max_prot has more permissions than prot */
- flags = MAP_PRIVATE | MAP_ANONYMOUS;
- max_prot = PROT_ALL;
- prot = PROT_READ | PROT_EXEC;
- ptr = mmap(NULL, MMAP_SIZE, PROT_MAX(max_prot) | prot, flags, -1, 0);
- ASSERT_FALSE(IS_ERR_VALUE(ptr));
- retval = mprotect(ptr, MMAP_SIZE, PROT_WRITE);
- ASSERT_EQ(retval, 0);
- EXPECT_EQ(0, probe_mem_range(ptr, MMAP_SIZE,
PROBE_MODE_TOUCH | PROBE_MODE_VERIFY));
- retval = munmap(ptr, MMAP_SIZE);
- ASSERT_EQ(retval, 0);
- /* repeat positive max_prot test with fixed address */
- flags = MAP_PRIVATE | MAP_ANONYMOUS;
- max_prot = PROT_ALL;
- prot = PROT_READ | PROT_EXEC;
- ptr = mmap(addr, MMAP_SIZE, PROT_MAX(max_prot) | prot,
flags | MAP_FIXED, -1, 0);
- ASSERT_FALSE(IS_ERR_VALUE(ptr));
- retval = mprotect(ptr, MMAP_SIZE, PROT_WRITE);
- ASSERT_EQ(retval, 0);
- retval = munmap(ptr, MMAP_SIZE);
- ASSERT_EQ(retval, 0);
- /* LoadCap and StoreCap permissions must not be given to a shared mapping */
- flags = MAP_SHARED | MAP_ANONYMOUS;
- prot = PROT_READ | PROT_WRITE;
- ptr = mmap(NULL, MMAP_SIZE, PROT_MAX(prot) | PROT_READ, flags, -1, 0);
prot | PROT_MAX(prot) is equivalent to just prot. We don't need to specify MAX_PROT() in every testcase.
Kevin
[...]