diff --git a/kernel/src/vm/vmar/vmar_impls/protect.rs b/kernel/src/vm/vmar/vmar_impls/protect.rs index 231241544..d85f3dfff 100644 --- a/kernel/src/vm/vmar/vmar_impls/protect.rs +++ b/kernel/src/vm/vmar/vmar_impls/protect.rs @@ -43,7 +43,12 @@ impl Vmar { let new_perms = perms | (vm_mapping_perms & VmPerms::ALL_MAY_PERMS); new_perms.check()?; - let vm_mapping = inner.remove(&vm_mapping_range.start).unwrap(); + let Some(vm_mapping) = inner.remove(&vm_mapping_range.start) else { + // This can happen only if the mapping is merged to the previous one (just + // protected before). We can skip this mapping because its property is already + // correct. + continue; + }; let vm_mapping_range = vm_mapping.range(); let intersected_range = get_intersected_range(&range, &vm_mapping_range); diff --git a/test/initramfs/src/apps/mmap/mmap_and_mprotect.c b/test/initramfs/src/apps/mmap/mmap_and_mprotect.c index 50021e99c..679095ccf 100644 --- a/test/initramfs/src/apps/mmap/mmap_and_mprotect.c +++ b/test/initramfs/src/apps/mmap/mmap_and_mprotect.c @@ -69,3 +69,17 @@ FN_TEST(mprotect_invalid_permission) TEST_SUCC(munmap(addr, PAGE_SIZE)); } END_TEST() + +FN_TEST(mprotect_merge_after_split_mapping) +{ + void *addr = TEST_SUCC(mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + + // Split the mapping into two. + TEST_SUCC(mprotect(addr, PAGE_SIZE, PROT_READ)); + // After the first mapping is protected, the two mappings will merge. + TEST_SUCC(mprotect(addr, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE)); + + TEST_SUCC(munmap(addr, 2 * PAGE_SIZE)); +} +END_TEST() \ No newline at end of file