From: Chaitanya S Prakash chaitanyas.prakash@arm.com
Reservations are contiguous ranges of virtual addresses that exactly match the bounds of an owning capability. When an owning capability is passed to a syscall, its bounds are first verified against the existing reservation. If the reservation created for the nulll derived capability is found to overlap with any existing reservation, the syscall fails with a -ERESERVATION error code.
According to the spec, the lifetime of reservations is generally undefined. While the reservation is eventually destroyed and the address range is made available, there should be no assumption as to when that will happen. But in the current implementation, the reservation is destroyed as soon as the last mapping is unmapped. Here a test is added to ensure that mmap(owning_cap, ...MAP_FIXED) fails even if the underlying reservation is destroyed.
A partial unmap within a particular reservation still allows the rest of the region to be accessible. Tests to verify the same have been added.
Signed-off-by: Chaitanya S Prakash chaitanyas.prakash@arm.com --- tools/testing/selftests/arm64/morello/mmap.c | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+)
diff --git a/tools/testing/selftests/arm64/morello/mmap.c b/tools/testing/selftests/arm64/morello/mmap.c index c0986b417540..cb8da2e257a9 100644 --- a/tools/testing/selftests/arm64/morello/mmap.c +++ b/tools/testing/selftests/arm64/morello/mmap.c @@ -306,6 +306,57 @@ TEST(test_range_check) ASSERT_EQ(retval, 0); }
+/* test to verify mmap() reservation semantics */ +TEST(test_check_mmap_reservation) +{ + void *ptr, *new_ptr; + size_t size; + int retval; + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + + /* test to verify rest of reservation region is accessible after a partial + * unmap + */ + ptr = mmap(NULL, MMAP_SIZE, prot, flags, -1, 0); + ASSERT_FALSE(IS_ERR_VALUE(ptr)); + + retval = munmap(ptr, pagesize); + ASSERT_EQ(retval, 0); + + ptr = ptr + pagesize; + size = MMAP_SIZE - pagesize; + EXPECT_EQ(0, probe_mem_range(ptr, size, + PROBE_MODE_TOUCH | PROBE_MODE_VERIFY)); + + retval = munmap(ptr, size); + ASSERT_EQ(retval, 0); + + /* test to verify that a subsequent mmap() call to the same region whose + * reservation has been destroyed fails. This test is in accordance with + * the current implementation. + */ + ptr = mmap(NULL, MMAP_SIZE, prot, flags, -1, 0); + ASSERT_FALSE(IS_ERR_VALUE(ptr)); + + retval = munmap(ptr, MMAP_SIZE); + ASSERT_EQ(retval, 0); + + new_ptr = mmap(ptr, MMAP_SIZE_REDUCED, prot, flags | MAP_FIXED, -1, 0); + EXPECT_EQ((unsigned long)new_ptr, (unsigned long)-ERESERVATION); + + /* null-derived ptr overlaps with an existing reservation */ + ptr = mmap((void *)(uintptr_t)address, MMAP_SIZE, prot, flags, -1, 0); + ASSERT_FALSE(IS_ERR_VALUE(ptr)); + + new_ptr = mmap((void *)(uintptr_t)address + MMAP_SIZE_REDUCED, MMAP_SIZE, prot, + flags | MAP_FIXED, -1, 0); + EXPECT_EQ((unsigned long)new_ptr, (unsigned long)-ERESERVATION); + + retval = munmap(ptr, MMAP_SIZE); + ASSERT_EQ(retval, 0); +} + int main(int argc __maybe_unused, char **argv __maybe_unused, char **envp __maybe_unused, struct morello_auxv *auxv) { pagesize = get_pagesize(auxv); @@ -315,5 +366,6 @@ int main(int argc __maybe_unused, char **argv __maybe_unused, char **envp __mayb test_map_growsdown(); test_validity_tag_check(); test_range_check(); + test_check_mmap_reservation(); return 0; }