madvise(2) and msync(2) have some memory/mapping destructive ops which should
authorderaadt <deraadt@openbsd.org>
Sun, 21 Jan 2024 16:57:06 +0000 (16:57 +0000)
committerderaadt <deraadt@openbsd.org>
Sun, 21 Jan 2024 16:57:06 +0000 (16:57 +0000)
not be allowed upon immutable memory, instead return EPERM.
Some of these ops are not destructive in OpenBSD, but they are destructive
on other systems, so we take the "all ops" are illegal approach.

Related to this, it should not be allowed to minherit(MAP_INHERIT_ZERO)
immutable regions, or vice versa, calling mimmutable() upon MAP_INHERIT_ZERO
regions, because such a range will be zero'd post-fork in the child.
These now also return EPERM.

Adjusting the madvise / msync behaviour upon immutable memory brings us
closer to the behaviour of the mimmutable clone "mseal" being proposed by
google for inclusion in Linux.
ok kettenis

sys/uvm/uvm_map.c

index e74948b..0cc03de 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvm_map.c,v 1.323 2024/01/21 00:26:14 deraadt Exp $   */
+/*     $OpenBSD: uvm_map.c,v 1.324 2024/01/21 16:57:06 deraadt Exp $   */
 /*     $NetBSD: uvm_map.c,v 1.86 2000/11/27 08:40:03 chs Exp $ */
 
 /*
@@ -4166,7 +4166,8 @@ int
 uvm_map_inherit(struct vm_map *map, vaddr_t start, vaddr_t end,
     vm_inherit_t new_inheritance)
 {
-       struct vm_map_entry *entry;
+       struct vm_map_entry *entry, *entry1;
+       int error = EPERM;
 
        switch (new_inheritance) {
        case MAP_INHERIT_NONE:
@@ -4193,14 +4194,24 @@ uvm_map_inherit(struct vm_map *map, vaddr_t start, vaddr_t end,
        else
                entry = RBT_NEXT(uvm_map_addr, entry);
 
+       /* First check for illegal operations */
+       entry1 = entry;
+       while (entry1 != NULL && entry1->start < end) {
+               if (entry1->etype & UVM_ET_IMMUTABLE)
+                       goto out;
+               entry1 = RBT_NEXT(uvm_map_addr, entry1);
+       }
+
        while (entry != NULL && entry->start < end) {
                UVM_MAP_CLIP_END(map, entry, end);
                entry->inheritance = new_inheritance;
                entry = RBT_NEXT(uvm_map_addr, entry);
        }
 
+       error = 0;
+out:
        vm_map_unlock(map);
-       return (0);
+       return (error);
 }
 
 #ifdef PMAP_CHECK_COPYIN
@@ -4289,7 +4300,8 @@ uvm_map_syscall(struct vm_map *map, vaddr_t start, vaddr_t end)
 int
 uvm_map_immutable(struct vm_map *map, vaddr_t start, vaddr_t end, int imut)
 {
-       struct vm_map_entry *entry;
+       struct vm_map_entry *entry, *entry1;
+       int error = EPERM;
 
        if (start > end)
                return EINVAL;
@@ -4306,6 +4318,14 @@ uvm_map_immutable(struct vm_map *map, vaddr_t start, vaddr_t end, int imut)
        else
                entry = RBT_NEXT(uvm_map_addr, entry);
 
+       /* First check for illegal operations */
+       entry1 = entry;
+       while (entry1 != NULL && entry1->start < end) {
+               if (entry1->inheritance == MAP_INHERIT_ZERO)
+                       goto out;
+               entry1 = RBT_NEXT(uvm_map_addr, entry1);
+       }
+       
        while (entry != NULL && entry->start < end) {
                UVM_MAP_CLIP_END(map, entry, end);
                if (imut)
@@ -4316,6 +4336,8 @@ uvm_map_immutable(struct vm_map *map, vaddr_t start, vaddr_t end, int imut)
        }
 
        map->wserial++;
+       error = 0;
+out:
        vm_map_unlock(map);
        return (0);
 }
@@ -4553,9 +4575,13 @@ uvm_map_clean(struct vm_map *map, vaddr_t start, vaddr_t end, int flags)
        vm_map_lock(map);
        first = uvm_map_entrybyaddr(&map->addr, start);
 
-       /* Make a first pass to check for holes. */
+       /* Make a first pass to check for various conditions. */
        for (entry = first; entry != NULL && entry->start < end;
            entry = RBT_NEXT(uvm_map_addr, entry)) {
+               if (entry->etype & UVM_ET_IMMUTABLE) {
+                       vm_map_unlock(map);
+                       return EPERM;
+               }
                if (UVM_ET_ISSUBMAP(entry)) {
                        vm_map_unlock(map);
                        return EINVAL;