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_EXACT 16
#define VMM_FLAGS_SHARED 32 #define VMM_FLAGS_SHARED 32
#define VMM_FLAGS_REPLACE 64 #define VMM_FLAGS_REPLACE 64
#define VMM_FLAGS_CREDCHECK 128
#define VMM_PERMANENT_FLAGS_MASK (VMM_FLAGS_FILE | VMM_FLAGS_SHARED | VMM_FLAGS_PHYSICAL) #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; 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); void vmm_destroycontext(vmmcontext_t *context);
vmmcontext_t *vmm_fork(vmmcontext_t *oldcontext); vmmcontext_t *vmm_fork(vmmcontext_t *oldcontext);
void *vmm_map(void *addr, size_t size, int flags, mmuflags_t mmuflags, void *private); 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); void *top = (void *)((uintptr_t)address + size);
vmmrange_t *range = space->ranges; vmmrange_t *range = space->ranges;
vmmrange_t *newrange = NULL; vmmrange_t *newrange = NULL;
// allocated here and as soon as its used to make sure that // 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) { if (free == false) {
newrange = allocrange(); newrange = allocrange();
__assert(newrange); if (newrange == NULL)
return ENOMEM;
} }
int error = 0;
while (range) { while (range) {
__assert(range != space->ranges || range->prev == NULL); __assert(range != space->ranges || range->prev == NULL);
void *rangetop = RANGE_TOP(range); 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); destroyrange(range, 0, range->size, 0);
freerange(range); freerange(range);
} else { } 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); changemmurange(range, range->start, range->size, newmmuflags);
range->mmuflags = newmmuflags; range->mmuflags = newmmuflags;
} }
} else if (address > range->start && top < rangetop) { } else if (address > range->start && top < rangetop) {
// split (entire change was within a single range) // 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(); vmmrange_t *new = allocrange();
__assert(new); // XXX deal with this better if (range == NULL) {
error = ENOMEM;
goto leave;
}
*new = *range; *new = *range;
if (free) { if (free) {
@@ -370,10 +396,19 @@ static void changemap(vmmspace_t *space, void *address, size_t size, bool free,
insertrange(space, newrange); insertrange(space, newrange);
newrange = allocrange(); newrange = allocrange();
__assert(newrange); if (newrange == NULL) {
error = ENOMEM;
goto leave;
}
} }
} else if (top > range->start && range->start >= address) { } else if (top > range->start && range->start >= address) {
// partially change from start (end of change was within this range) // 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; size_t difference = (uintptr_t)top - (uintptr_t)range->start;
if (free) { if (free) {
destroyrange(range, 0, difference, 0); 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); insertrange(space, newrange);
newrange = allocrange(); newrange = allocrange();
__assert(newrange); if (newrange == NULL) {
error = ENOMEM;
goto leave;
}
} }
} else if (address < rangetop && rangetop <= top) { } else if (address < rangetop && rangetop <= top) {
// partially change from end (start of change was within this range) // 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; size_t difference = (uintptr_t)rangetop - (uintptr_t)address;
range->size -= difference; range->size -= difference;
if (free) { if (free) {
@@ -424,18 +468,24 @@ static void changemap(vmmspace_t *space, void *address, size_t size, bool free,
insertrange(space, newrange); insertrange(space, newrange);
newrange = allocrange(); newrange = allocrange();
__assert(newrange); if (newrange == NULL) {
error = ENOMEM;
goto leave;
}
} }
} }
range = range->next; range = range->next;
} }
leave:
if (free == false) if (free == false)
freerange(newrange); 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); base = (void *)ROUND_DOWN((uintptr_t)base, PAGE_SIZE);
if (flags & VMM_FLAGS_PAGESIZE) 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); size = ROUND_UP(size, PAGE_SIZE);
if (size == 0) if (size == 0)
return true; return 0;
vmmspace_t *space = getspace(base); vmmspace_t *space = getspace(base);
if (space == NULL) if (space == NULL)
return false; return ENOMEM;
MUTEX_ACQUIRE(&space->lock, false); MUTEX_ACQUIRE(&space->lock, false);
changemap(space, base, size, false, mmuflags); int error = changemap(space, base, size, false, flags, mmuflags);
MUTEX_RELEASE(&space->lock); MUTEX_RELEASE(&space->lock);
return true; return error;
} }
static void printspace(vmmspace_t *space) { 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->flags = VMM_PERMANENT_FLAGS_MASK & flags;
range->mmuflags = mmuflags; range->mmuflags = mmuflags;
__assert((flags & (VMM_FLAGS_ALLOCATE | VMM_FLAGS_PHYSICAL)) == 0); __assert((flags & (VMM_FLAGS_ALLOCATE | VMM_FLAGS_PHYSICAL)) == 0);
changemap(space, addr, size, true, 0); changemap(space, addr, size, true, flags, 0);
} else { } else {
retaddr = start; retaddr = start;
range->start = start; range->start = start;
@@ -736,7 +786,7 @@ void vmm_unmap(void *addr, size_t size, int flags) {
return; return;
MUTEX_ACQUIRE(&space->lock, false); MUTEX_ACQUIRE(&space->lock, false);
changemap(space, addr, size, true, 0); changemap(space, addr, size, true, flags, 0);
MUTEX_RELEASE(&space->lock); 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; mmuflags_t mmuflags = ARCH_MMU_FLAGS_USER;
if (prot & PROT_READ) if (prot & PROT_READ)
mmuflags |= ARCH_MMU_FLAGS_READ; mmuflags |= ARCH_MMU_FLAGS_READ;
if (prot & PROT_WRITE) if (prot & PROT_WRITE)
mmuflags |= ARCH_MMU_FLAGS_WRITE; mmuflags |= ARCH_MMU_FLAGS_WRITE;
if ((prot & PROT_EXEC) == 0) if ((prot & PROT_EXEC) == 0)
mmuflags |= ARCH_MMU_FLAGS_NOEXEC; 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; ret.ret = ret.errno ? -1 : 0;
return ret; return ret;