aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv64/kernel/vmem.c54
-rw-r--r--include/arch/tcb.h2
-rw-r--r--include/kmi/regions.h4
-rw-r--r--include/kmi/vmem.h3
-rw-r--r--src/pmem.c1
-rw-r--r--src/proc.c12
-rw-r--r--src/regions.c13
-rw-r--r--src/tcb.c22
-rw-r--r--src/uapi/proc.c6
-rw-r--r--src/vmem.c18
-rw-r--r--tests/create-exhaustion/init.c29
-rw-r--r--tests/create-exhaustion/source.mk2
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.
diff --git a/src/pmem.c b/src/pmem.c
index e2177ea..871e15a 100644
--- a/src/pmem.c
+++ b/src/pmem.c
@@ -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);
diff --git a/src/proc.c b/src/proc.c
index 5753659..1c90fdc 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -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);
}
/**
diff --git a/src/tcb.c b/src/tcb.c
index 64d95a5..43c5a94 100644
--- a/src/tcb.c
+++ b/src/tcb.c
@@ -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);
diff --git a/src/vmem.c b/src/vmem.c
index 96d4005..2feff45 100644
--- a/src/vmem.c
+++ b/src/vmem.c
@@ -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