diff options
| -rw-r--r-- | arch/riscv64/kernel/vmem.c | 54 | ||||
| -rw-r--r-- | include/arch/tcb.h | 2 | ||||
| -rw-r--r-- | include/kmi/regions.h | 4 | ||||
| -rw-r--r-- | include/kmi/vmem.h | 3 | ||||
| -rw-r--r-- | src/pmem.c | 1 | ||||
| -rw-r--r-- | src/proc.c | 12 | ||||
| -rw-r--r-- | src/regions.c | 13 | ||||
| -rw-r--r-- | src/tcb.c | 22 | ||||
| -rw-r--r-- | src/uapi/proc.c | 6 | ||||
| -rw-r--r-- | src/vmem.c | 18 | ||||
| -rw-r--r-- | tests/create-exhaustion/init.c | 29 | ||||
| -rw-r--r-- | tests/create-exhaustion/source.mk | 2 |
12 files changed, 132 insertions, 34 deletions
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c index b5dd1d6..29ec3d5 100644 --- a/arch/riscv64/kernel/vmem.c +++ b/arch/riscv64/kernel/vmem.c @@ -309,8 +309,8 @@ stat_t map_vpage(struct vmem *branch, pm_t paddr, vm_t vaddr, vmflags_t flags, } size_t idx = vm_to_index(vaddr, top); - if (is_branch( - branch->leaf[idx])) /* something has gone terribly wrong? */ + if (is_branch(branch->leaf[idx])) + /* something has gone terribly wrong? */ __destroy_branch(branch->leaf[idx]); branch->leaf[idx] = @@ -442,6 +442,9 @@ struct vmem *init_vmem(void *fdt) UNUSED(fdt); struct vmem *b = create_vmem(); + if (!b) + return NULL; + __populate_dmap(b); /* update which memory branch to use */ use_vmem(b); @@ -451,6 +454,9 @@ struct vmem *init_vmem(void *fdt) struct vmem *create_vmem() { struct vmem *b = (struct vmem *)alloc_page(MM_KPAGE); + if (!b) + return NULL; + memset(b, 0, MM_KPAGE_SIZE); populate_kvmem(b); return b; @@ -463,7 +469,17 @@ void use_vmem(struct vmem *b) void destroy_vmem(struct vmem *b) { - __destroy_branch(b); + if (!b) + return; + + /* don't free kernel mapping as that one is guaranteed to be statically + * allocated */ + for (size_t i = 0; i < KERNEL_PAGE; ++i) { + if (is_branch(b->leaf[i])) + __destroy_branch((struct vmem *)pte_addr(b->leaf[i])); + } + + free_page(MM_KPAGE, (pm_t)b); } /** @@ -484,6 +500,7 @@ static void map_kernel(struct vmem *b) if (addr < 0) addr = addr - VM_KERNEL + get_load_addr(); + /* branch */ b->leaf[KERNEL_PAGE] = (struct vmem *)to_pte((pm_t)addr, VM_V); } @@ -546,7 +563,7 @@ size_t max_rpc_size() return SZ_512K; } -void setup_rpc_stack(struct tcb *t) +stat_t setup_rpc_stack(struct tcb *t) { /* by default rpc stack is marked inaccessible to generate segfaults on * access so as to ease stack usage tracking */ @@ -554,13 +571,23 @@ void setup_rpc_stack(struct tcb *t) for (size_t i = 0; i < rpc_pages; ++i) { pm_t page = alloc_page(BASE_PAGE); - map_vpage(t->rpc.vmem, page, - RPC_STACK_BASE + BASE_PAGE_SIZE * i, - flags, BASE_PAGE); + if (!page) + return ERR_OOMEM; - map_vpage(t->proc.vmem, page, - RPC_STACK_BASE + BASE_PAGE_SIZE * i, - flags, BASE_PAGE); + /* map both into rpc and proc spaces so we can write to the + * current stack frame directly. Especially important when + * returning from an rpc. Technically means that we could leak + * memory if map_vpage() for proc.vmem allocates more and more + * pages, but good enough for now. */ + if (map_vpage(t->rpc.vmem, page, + RPC_STACK_BASE + BASE_PAGE_SIZE * i, + flags, BASE_PAGE)) + return ERR_OOMEM; + + if (map_vpage(t->proc.vmem, page, + RPC_STACK_BASE + BASE_PAGE_SIZE * i, + flags, BASE_PAGE)) + return ERR_OOMEM; } /* we allocated a second order page for rpc stack usage */ @@ -573,14 +600,17 @@ void setup_rpc_stack(struct tcb *t) NULL); /* we count downward in base pages */ t->arch.rpc_idx = rpc_pages; + return OK; } void destroy_rpc_stack(struct tcb *t) { for (size_t i = 0; i < rpc_pages; ++i) { pm_t page = 0; enum mm_order order = BASE_PAGE; - stat_vpage(t->rpc.vmem, RPC_STACK_BASE + BASE_PAGE_SIZE * i, - &page, &order, NULL); + if (stat_vpage(t->rpc.vmem, RPC_STACK_BASE + BASE_PAGE_SIZE * i, + &page, &order, NULL)) + return; + free_page(order, page); } } diff --git a/include/arch/tcb.h b/include/arch/tcb.h index b8b0961..f4726ad 100644 --- a/include/arch/tcb.h +++ b/include/arch/tcb.h @@ -32,7 +32,7 @@ void tcb_assign(struct tcb *t); * * @param t Thread whose RPC stack should be set up. */ -void setup_rpc_stack(struct tcb *t); +stat_t setup_rpc_stack(struct tcb *t); /** * Free memory backing rpc stack. diff --git a/include/kmi/regions.h b/include/kmi/regions.h index e612543..5faec0d 100644 --- a/include/kmi/regions.h +++ b/include/kmi/regions.h @@ -155,6 +155,10 @@ vm_t alloc_shared_region(struct mem_region_root *r, size_t size, size_t *actual_size, vmflags_t flags, id_t pid); +vm_t alloc_shared_fixed_region(struct mem_region_root *r, vm_t start, + size_t size, + size_t *actual_size, vmflags_t flags, id_t pid); + /** * Allocate fixed memory region. * Will allocate region that is at least \c size bytes, and includes \c start. diff --git a/include/kmi/vmem.h b/include/kmi/vmem.h index eb46da5..c53f009 100644 --- a/include/kmi/vmem.h +++ b/include/kmi/vmem.h @@ -129,7 +129,8 @@ void destroy_uvmem(struct tcb *r); * the start of \p base, not necessarily the start of the allocation. * If this should be freed, remember to align down to the base page size. */ -vm_t map_fixed_uvmem(struct tcb *r, pm_t base, size_t size, vmflags_t flags); +vm_t map_shared_fixed_uvmem(struct tcb *r, pm_t base, size_t size, + vmflags_t flags); /** * Clone process memory. @@ -235,6 +235,7 @@ static bool __free_page(enum mm_order order, pm_t addr) struct mm_bmap *bmap = __get_set(bucket, set); bmap->used--; + assert(bitmap_is_set(bmap->bits, bit)); bitmap_clear(bmap->bits, bit); __attach_set(bucket, bmap); @@ -65,14 +65,14 @@ stat_t init_proc(void *fdt, vm_t *proc_fdt, vm_t *proc_initrd) /** \todo start one thread per core, with special handling for init in * that each thread starts at the entry point of init? */ - *proc_fdt = map_fixed_uvmem(t, - (pm_t)fdt, fdt_totalsize(fdt), - MR_SHARED | VM_V | VM_R | VM_U); + *proc_fdt = map_shared_fixed_uvmem(t, + (pm_t)fdt, fdt_totalsize(fdt), + VM_V | VM_R | VM_U); pm_t initrd = (pm_t)__va(get_initrdbase(fdt)); - *proc_initrd = map_fixed_uvmem(t, - initrd, get_initrdsize(fdt), - MR_SHARED | VM_V | VM_R | VM_U); + *proc_initrd = map_shared_fixed_uvmem(t, + initrd, get_initrdsize(fdt), + VM_V | VM_R | VM_U); info("mapped fdt at %lx\n", *proc_fdt); info("mapped initrd at %lx\n", *proc_initrd); diff --git a/src/regions.c b/src/regions.c index fcfa0cc..64bfb88 100644 --- a/src/regions.c +++ b/src/regions.c @@ -466,8 +466,9 @@ vm_t alloc_region(struct mem_region_root *r, size_t size, size_t *actual_size, return alloc_shared_region(r, size, actual_size, flags, 0); } -vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size, - size_t *actual_size, vmflags_t flags) +vm_t alloc_shared_fixed_region(struct mem_region_root *r, vm_t start, + size_t size, size_t *actual_size, + vmflags_t flags, id_t pid) { size_t asize = align_up(size, BASE_PAGE_SIZE); if (actual_size) @@ -499,7 +500,13 @@ vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size, return 0; /* actually start marking region used */ - return __partition_region(r, m, pages, start - m->start, flags, 0); + return __partition_region(r, m, pages, start - m->start, flags, pid); +} + +vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size, + size_t *actual_size, vmflags_t flags) +{ + return alloc_shared_fixed_region(r, start, size, actual_size, flags, 0); } /** @@ -106,6 +106,7 @@ stat_t alloc_stack(struct tcb *t) { /* get parent process */ struct tcb *p = get_tcb(t->eid); + assert(p); t->thread_stack = __setup_thread_stack(p, thread_stack_size()); if (!t->thread_stack) @@ -171,16 +172,33 @@ struct tcb *create_thread(struct tcb *p) t->eid = t->pid; t->rid = p->rid; + /* hmm, the rest of this function is maybe a bit too difficult to follow + * for my liking. Will have to think about ways to make the logic more + * easy to follow */ if (!(t->rpc.vmem = create_vmem())) { - if (likely(p)) + if (likely(p)) { + free_page(MM_O0, bottom); return NULL; + } + + destroy_vmem(t->proc.vmem); + free_page(MM_O0, bottom); + return NULL; + } + + if (setup_rpc_stack(t)) { + destroy_rpc_stack(t); + destroy_vmem(t->rpc.vmem); + if (likely(p)) { + free_page(MM_O0, bottom); + return NULL; + } destroy_vmem(t->proc.vmem); free_page(MM_O0, bottom); return NULL; } - setup_rpc_stack(t); reference_thread(p); t->regs = (vm_t)t; diff --git a/src/uapi/proc.c b/src/uapi/proc.c index a99acb5..b010220 100644 --- a/src/uapi/proc.c +++ b/src/uapi/proc.c @@ -41,7 +41,11 @@ SYSCALL_DEFINE5(create)(struct tcb *t, sys_arg_t func, /** @todo there's quite a bit of overlap between this and what * core_bringup() is doing, might separate this out into its own * function? */ - alloc_stack(c); + if (alloc_stack(c)) { + destroy_thread(c); + return_args1(t, ERR_OOMEM); + } + set_thread(c); set_ret5(c, c->tid, d0, d1, d2, d3); @@ -83,8 +83,8 @@ static stat_t __copy_shared_region(struct tcb *d, struct mem_region *m) reference_thread(s); size_t size = end - start; - vm_t v = alloc_fixed_region(&d->uvmem.region, start, size, &size, - m->flags); + vm_t v = alloc_shared_fixed_region(&d->uvmem.region, start, size, &size, + m->flags, m->pid); if (ERR_CODE(v)) return v; @@ -155,9 +155,9 @@ static void __free_mapping(struct tcb *t, struct mem_region *m) size_t size = end - start; if (m->pid) - unmap_fixed_region(t->proc.vmem, start, size); + unmap_fixed_region(t->uvmem.vmem, start, size); else - unmap_region(t->proc.vmem, start, size); + unmap_region(t->uvmem.vmem, start, size); } void clear_uvmem(struct tcb *t) @@ -165,7 +165,7 @@ void clear_uvmem(struct tcb *t) if (t->uvmem.owner != t->tid) return; - struct mem_region *m = find_closest_used_region(&t->uvmem.region, 0); + struct mem_region *m = find_first_region(&t->uvmem.region); for (; m; m = m->next) { if (is_region_kept(m)) continue; @@ -183,7 +183,7 @@ void purge_uvmem(struct tcb *t) if (t->uvmem.owner != t->tid) return; - struct mem_region *m = find_closest_used_region(&t->uvmem.region, 0); + struct mem_region *m = find_first_region(&t->uvmem.region); for (; m; m = m->next) { if (!is_set(m->flags, MR_USED)) continue; @@ -266,11 +266,13 @@ vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags) return v; } -vm_t map_fixed_uvmem(struct tcb *t, pm_t start, size_t size, vmflags_t flags) +vm_t map_shared_fixed_uvmem(struct tcb *t, pm_t start, size_t size, + vmflags_t flags) { assert(is_aligned(start, BASE_PAGE_SIZE)); - const vm_t v = alloc_region(&t->uvmem.region, size, &size, flags); + const vm_t v = alloc_shared_region(&t->uvmem.region, size, &size, flags, + get_rproc(t)->tid); if (ERR_CODE(v)) return v; diff --git a/tests/create-exhaustion/init.c b/tests/create-exhaustion/init.c new file mode 100644 index 0000000..d5ac20a --- /dev/null +++ b/tests/create-exhaustion/init.c @@ -0,0 +1,29 @@ +#include <common/test.h> + +START(pid, tid, d0, d1, d2, d3) +{ + UNUSED(pid); + UNUSED(tid); + UNUSED(d0); + UNUSED(d1); + UNUSED(d2); + UNUSED(d3); + + size_t old_ram = 0; + while (1) { + printf("creating new thread\n"); + id_t new_thread = sys_create((uintptr_t)_start, 1, 2, 3, 4); + if (new_thread < 0) + break; + + old_ram = sys_conf_get(CONF_RAM_USAGE, 0); + } + + /* this might not be guaranteed due to how we're currently mapping the + * rpc stack, but good enough for now */ + size_t ram = sys_conf_get(CONF_RAM_USAGE, 0); + check(ram == old_ram, "'leaked' memory building thread without enough memory\n"); + + /* no crash is good enough */ + ok(); +} diff --git a/tests/create-exhaustion/source.mk b/tests/create-exhaustion/source.mk new file mode 100644 index 0000000..d096746 --- /dev/null +++ b/tests/create-exhaustion/source.mk @@ -0,0 +1,2 @@ +DO != ./scripts/gen-prog -n create-exhaustion -p init init.c +DO != ./scripts/gen-simple -n create-exhaustion -p init |
