mm: fix checks for PROT_NONE You gotta love PROT_NONE, O_RDONLY, and any of these flags whose value is zero. The old code assumed that the only options for 'prot' were mutually exclusive with PROT_NONE, such that we could check prot == PROT_NONE when we really meant "has no access". We checked for equality, since we can't do (prot & PROT_NONE), since PROT_NONE == 0. However, checking prot == PROT_NONE is wrong: we have other flags, like PROT_GROWSDOWN. Arguably, we shouldn't use those flags - I'll remove them shortly. Regardless, using a helper cleans up the code. Reported-by: syzbot+aafc3433b89c900f8fe1@syzkaller.appspotmail.com Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/src/mm.c b/kern/src/mm.c index 7bc6ca9..5dfb85e 100644 --- a/kern/src/mm.c +++ b/kern/src/mm.c
@@ -653,6 +653,11 @@ (flags & (MAP_PRIVATE | MAP_SHARED)) == MAP_SHARED; } +static bool prot_has_access(int prot) +{ + return prot & (PROT_READ | PROT_WRITE | PROT_EXEC); +} + /* Error values aren't quite comprehensive - check man mmap() once we do better * with the FS. * @@ -731,7 +736,7 @@ * treat as success. So when we return 0, *a* page is mapped here, but not * necessarily the one you passed in. */ static int map_page_at_addr(struct proc *p, struct page *page, uintptr_t addr, - int prot) + int pte_prot) { pte_t pte; @@ -758,10 +763,10 @@ * documentation), but it's probably a sign of another bug. */ assert(!pte_is_mapped(pte)); /* preserve the dirty bit - pm removal could be looking concurrently */ - prot |= (pte_is_dirty(pte) ? PTE_D : 0); + pte_prot |= (pte_is_dirty(pte) ? PTE_D : 0); /* We have a ref to page (for non PMs), which we are storing in the PTE */ - pte_write(pte, page2pa(page), prot); + pte_write(pte, page2pa(page), pte_prot); spin_unlock(&p->pte_lock); return 0; } @@ -941,7 +946,7 @@ vmr = merge_me(vmr); /* attempts to merge with neighbors */ - if (flags & MAP_POPULATE && prot != PROT_NONE) { + if (flags & MAP_POPULATE && prot_has_access(prot)) { int pte_prot = (prot & PROT_WRITE) ? PTE_USER_RW : (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0; unsigned long nr_pgs = len >> PGSHIFT; @@ -1367,7 +1372,7 @@ vmr = find_vmr(p, va); if (!vmr) break; - if (vmr->vm_prot == PROT_NONE) + if (!prot_has_access(vmr->vm_prot)) break; pte_prot = (vmr->vm_prot & PROT_WRITE) ? PTE_USER_RW : (vmr->vm_prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO