From d1af67ca27b1cf044244e61071fe2e538e452815 Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Thu, 21 Apr 2022 19:00:58 +0200
Subject: kernel: recursive kill()

---
 src/kernel/proc.c        | 67 ++++++++++++++++++++++++++++++++++++++----------
 src/kernel/proc.h        |  3 +++
 src/kernel/vfs/request.c |  2 +-
 3 files changed, 58 insertions(+), 14 deletions(-)

(limited to 'src/kernel')

diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index 611fd81..9e8e4c5 100644
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -169,9 +169,16 @@ void process_transition(struct process *p, enum process_state state) {
 	switch (state) {
 		case PS_RUNNING:
 			assert(last != PS_DEAD && last != PS_DEADER);
+			if (p->deathbed)
+				process_kill(p, -1);
+			/* TODO return something to warn caller if deathbedded, to prevent
+			 * use after free
+			 *
+			 * alternatively assert(!p->deathbed); and move the responsibility
+			 * to the caller */
 			break;
 		case PS_DEAD:
-			assert(last == PS_RUNNING);
+			// see process_kill
 			break;
 		case PS_DEADER:
 			assert(last == PS_DEAD);
@@ -188,13 +195,20 @@ void process_transition(struct process *p, enum process_state state) {
 	}
 }
 
-void process_kill(struct process *proc, int ret) {
-	// TODO kill children
-	if (proc->controlled) {
-		proc->controlled->potential_handlers--;
-		if (proc->controlled->potential_handlers == 0) {
+void process_kill(struct process *p, int ret) {
+	if (p->state == PS_DEAD || p->state == PS_DEADER) return;
+
+	if (p->handled_req) {
+		vfs_request_cancel(p->handled_req, ret);
+		p->handled_req = NULL;
+	}
+	if (p->controlled) {
+		// code stink: i don't like how handling controlled backends is split
+		// between this if and the switch lower down
+		p->controlled->potential_handlers--;
+		if (p->controlled->potential_handlers == 0) {
 			// orphaned
-			struct vfs_request *q = proc->controlled->queue;
+			struct vfs_request *q = p->controlled->queue;
 			while (q) {
 				struct vfs_request *q2 = q->queue_next;
 				vfs_request_cancel(q, ret);
@@ -202,13 +216,40 @@ void process_kill(struct process *proc, int ret) {
 			}
 		}
 	}
-	if (proc->handled_req) {
-		vfs_request_cancel(proc->handled_req, ret);
+
+	// TODO VULN unbounded recursion
+	for (struct process *c = p->child; c; c = c->sibling)
+		process_kill(c, -1);
+
+	switch (p->state) {
+		case PS_RUNNING:
+		case PS_WAITS4CHILDDEATH:
+			break;
+
+		case PS_WAITS4FS:
+			// if the request wasn't accepted we could just remove this process from the queue
+		case PS_WAITS4IRQ:
+			/* the system doesn't shut down until it receives one of each of the interrupts it's waiting for
+			 * more broadly: killing processes stuck on long io calls doesn't free up the drivers
+			 * TODO add a syscall for checking if a request is still valid, to bail early
+			 *  ( not needed for our kernel drivers, but we need feature parity )
+			 */
+			p->deathbed = true;
+			return;
+
+		case PS_WAITS4REQUEST:
+			assert(p->controlled);
+			if (p->controlled->handler == p)
+				p->controlled->handler = NULL;
+			break;
+
+		default:
+			kprintf("process_kill unexpected state 0x%x\n", p->state);
+			panic_invalid_state();
 	}
-	process_transition(proc, PS_DEAD);
-	proc->death_msg = ret;
-	process_try2collect(proc);
-	if (proc == process_first) shutdown();
+	process_transition(p, PS_DEAD);
+	p->death_msg = ret;
+	process_try2collect(p);
 }
 
 int process_try2collect(struct process *dead) {
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index f0bae50..255a9af 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -3,6 +3,7 @@
 #include <kernel/handle.h>
 #include <kernel/vfs/mount.h>
 #include <shared/syscalls.h>
+#include <stdbool.h>
 
 enum process_state {
 	PS_RUNNING,
@@ -20,6 +21,8 @@ struct process {
 	struct registers regs;
 	enum process_state state;
 
+	bool deathbed; // kill instead of switching to PS_RUNNING
+
 	struct process *sibling;
 	struct process *child;
 	struct process *parent;
diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c
index 625448d..b3769cf 100644
--- a/src/kernel/vfs/request.c
+++ b/src/kernel/vfs/request.c
@@ -92,8 +92,8 @@ int vfs_request_finish(struct vfs_request *req, int ret) {
 		kfree(req->input.buf_kern);
 
 	assert(req->caller->state == PS_WAITS4FS || req->caller->state == PS_WAITS4IRQ);
-	process_transition(req->caller, PS_RUNNING);
 	regs_savereturn(&req->caller->regs, ret);
+	process_transition(req->caller, PS_RUNNING);
 	return ret;
 }
 
-- 
cgit v1.2.3