From 0f183416744bc5d74ecff1fc77fd4a51780b02bf Mon Sep 17 00:00:00 2001 From: Matheus Date: Fri, 30 Aug 2024 21:22:17 -0300 Subject: [PATCH] kernel: vmm_changemmuflags cred check --- kernel-src/include/kernel/vmm.h | 3 +- kernel-src/mm/vmm.c | 78 ++++++++++++++++++++++++------ kernel-src/sys/syscalls/mprotect.c | 4 +- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/kernel-src/include/kernel/vmm.h b/kernel-src/include/kernel/vmm.h index 0dfa5886..5c8a2182 100644 --- a/kernel-src/include/kernel/vmm.h +++ b/kernel-src/include/kernel/vmm.h @@ -12,6 +12,7 @@ #define VMM_FLAGS_EXACT 16 #define VMM_FLAGS_SHARED 32 #define VMM_FLAGS_REPLACE 64 +#define VMM_FLAGS_CREDCHECK 128 #define VMM_PERMANENT_FLAGS_MASK (VMM_FLAGS_FILE | VMM_FLAGS_SHARED | VMM_FLAGS_PHYSICAL) @@ -92,7 +93,7 @@ static inline int mmuflagstovnodeflags(mmuflags_t mmuflags) { return flags; } -bool vmm_changemmuflags(void *base, size_t size, mmuflags_t mmuflags, int flags); +int vmm_changemmuflags(void *base, size_t size, mmuflags_t mmuflags, int flags); void vmm_destroycontext(vmmcontext_t *context); vmmcontext_t *vmm_fork(vmmcontext_t *oldcontext); void *vmm_map(void *addr, size_t size, int flags, mmuflags_t mmuflags, void *private); diff --git a/kernel-src/mm/vmm.c b/kernel-src/mm/vmm.c index a58fcc7f..641e59b9 100644 --- a/kernel-src/mm/vmm.c +++ b/kernel-src/mm/vmm.c @@ -297,17 +297,27 @@ static void changemmurange(vmmrange_t *range, void *base, size_t size, mmuflags_ } } -static void changemap(vmmspace_t *space, void *address, size_t size, bool free, mmuflags_t newmmuflags) { +static inline bool canwritevnode(vmmrange_t *range) { + VOP_LOCK(range->vnode); + int error = VOP_ACCESS(range->vnode, V_ACCESS_WRITE, &_cpu()->thread->proc->cred); + VOP_UNLOCK(range->vnode); + return error == 0; +} + +static int changemap(vmmspace_t *space, void *address, size_t size, bool free, int flags, mmuflags_t newmmuflags) { void *top = (void *)((uintptr_t)address + size); vmmrange_t *range = space->ranges; vmmrange_t *newrange = NULL; // allocated here and as soon as its used to make sure that - // even in an allocation failure (TODO) there will always be a valid mapping + // even in an allocation failure there will always be a valid mapping if (free == false) { newrange = allocrange(); - __assert(newrange); + if (newrange == NULL) + return ENOMEM; } + int error = 0; + while (range) { __assert(range != space->ranges || range->prev == NULL); void *rangetop = RANGE_TOP(range); @@ -325,13 +335,29 @@ static void changemap(vmmspace_t *space, void *address, size_t size, bool free, destroyrange(range, 0, range->size, 0); freerange(range); } else { + if ((flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) && + (range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) { + error = EACCES; + goto leave; + } + changemmurange(range, range->start, range->size, newmmuflags); range->mmuflags = newmmuflags; } } else if (address > range->start && top < rangetop) { // split (entire change was within a single range) + if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) && + (range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) { + error = EACCES; + goto leave; + } + vmmrange_t *new = allocrange(); - __assert(new); // XXX deal with this better + if (range == NULL) { + error = ENOMEM; + goto leave; + } + *new = *range; if (free) { @@ -370,10 +396,19 @@ static void changemap(vmmspace_t *space, void *address, size_t size, bool free, insertrange(space, newrange); newrange = allocrange(); - __assert(newrange); + if (newrange == NULL) { + error = ENOMEM; + goto leave; + } } } else if (top > range->start && range->start >= address) { // partially change from start (end of change was within this range) + if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) && + (range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) { + error = EACCES; + goto leave; + } + size_t difference = (uintptr_t)top - (uintptr_t)range->start; if (free) { destroyrange(range, 0, difference, 0); @@ -400,10 +435,19 @@ static void changemap(vmmspace_t *space, void *address, size_t size, bool free, insertrange(space, newrange); newrange = allocrange(); - __assert(newrange); + if (newrange == NULL) { + error = ENOMEM; + goto leave; + } } } else if (address < rangetop && rangetop <= top) { // partially change from end (start of change was within this range) + if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) && + (range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) { + error = EACCES; + goto leave; + } + size_t difference = (uintptr_t)rangetop - (uintptr_t)address; range->size -= difference; if (free) { @@ -424,18 +468,24 @@ static void changemap(vmmspace_t *space, void *address, size_t size, bool free, insertrange(space, newrange); newrange = allocrange(); - __assert(newrange); + if (newrange == NULL) { + error = ENOMEM; + goto leave; + } } } range = range->next; } + leave: if (free == false) freerange(newrange); + + return error; } -bool vmm_changemmuflags(void *base, size_t size, mmuflags_t mmuflags, int flags) { +int vmm_changemmuflags(void *base, size_t size, mmuflags_t mmuflags, int flags) { base = (void *)ROUND_DOWN((uintptr_t)base, PAGE_SIZE); if (flags & VMM_FLAGS_PAGESIZE) @@ -444,16 +494,16 @@ bool vmm_changemmuflags(void *base, size_t size, mmuflags_t mmuflags, int flags) size = ROUND_UP(size, PAGE_SIZE); if (size == 0) - return true; + return 0; vmmspace_t *space = getspace(base); if (space == NULL) - return false; + return ENOMEM; MUTEX_ACQUIRE(&space->lock, false); - changemap(space, base, size, false, mmuflags); + int error = changemap(space, base, size, false, flags, mmuflags); MUTEX_RELEASE(&space->lock); - return true; + return error; } static void printspace(vmmspace_t *space) { @@ -655,7 +705,7 @@ void *vmm_map(void *addr, volatile size_t size, int flags, mmuflags_t mmuflags, range->flags = VMM_PERMANENT_FLAGS_MASK & flags; range->mmuflags = mmuflags; __assert((flags & (VMM_FLAGS_ALLOCATE | VMM_FLAGS_PHYSICAL)) == 0); - changemap(space, addr, size, true, 0); + changemap(space, addr, size, true, flags, 0); } else { retaddr = start; range->start = start; @@ -736,7 +786,7 @@ void vmm_unmap(void *addr, size_t size, int flags) { return; MUTEX_ACQUIRE(&space->lock, false); - changemap(space, addr, size, true, 0); + changemap(space, addr, size, true, flags, 0); MUTEX_RELEASE(&space->lock); } diff --git a/kernel-src/sys/syscalls/mprotect.c b/kernel-src/sys/syscalls/mprotect.c index bc26c80e..30da37e5 100644 --- a/kernel-src/sys/syscalls/mprotect.c +++ b/kernel-src/sys/syscalls/mprotect.c @@ -27,12 +27,14 @@ syscallret_t syscall_mprotect(context_t *context, void *address, size_t len, int mmuflags_t mmuflags = ARCH_MMU_FLAGS_USER; if (prot & PROT_READ) mmuflags |= ARCH_MMU_FLAGS_READ; + if (prot & PROT_WRITE) mmuflags |= ARCH_MMU_FLAGS_WRITE; + if ((prot & PROT_EXEC) == 0) mmuflags |= ARCH_MMU_FLAGS_NOEXEC; - ret.errno = vmm_changemmuflags(address, len, mmuflags, 0) ? 0 : ENOMEM; + ret.errno = vmm_changemmuflags(address, len, mmuflags, VMM_FLAGS_CREDCHECK); ret.ret = ret.errno ? -1 : 0; return ret;