kernel: vmm_changemmuflags cred check

This commit is contained in:
Matheus
2024-08-30 21:22:17 -03:00
parent 3c8308533a
commit 0f18341674
3 changed files with 69 additions and 16 deletions
+2 -1
View File
@@ -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);
+64 -14
View File
@@ -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);
}
+3 -1
View File
@@ -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;