git: 9front

Download patch

ref: 62c66b9e4f72137a9e689c7af406c09fa782bb26
parent: 301ac0c488bf1f0d5fb092f47852a1a3efbc3140
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Wed Jan 3 23:45:56 EST 2024

kernel: add extra "New" process state to catch invalid state transitions

For ready() to check invalid state transitions,
we want to ensure that ready() is never called
on a dead proc (or one that is currently dying),
however when we create a new process,
its initial state was "Dead" until is is ready()d.

Instead, introduce the state "New", which is
set when the proc is removed from the freelist
by newproc(), making New -> Ready -> Running
state transition valid.

Also make sure we never leave notes to deadly
processes or ones that are in "Broken" state.

--- a/sys/src/9/port/devproc.c
+++ b/sys/src/9/port/devproc.c
@@ -1296,7 +1296,7 @@
 		qunlock(&p->seglock);
 		nexterror();
 	}
-	if(p->state == Dead || p->pid != PID(c->qid))
+	if(p->state <= New || p->pid != PID(c->qid))
 		error(Eprocdied);
 	if((s = p->seg[TSEG]) == nil)
 		error(Enonexist);
@@ -1647,7 +1647,7 @@
 		qunlock(&p->seglock);
 		nexterror();
 	}
-	if(p->state == Dead || p->pid != PID(c->qid))
+	if(p->state <= New || p->pid != PID(c->qid))
 		error(Eprocdied);
 
 	for(i = 0; i < NSEG; i++) {
--- a/sys/src/9/port/edf.c
+++ b/sys/src/9/port/edf.c
@@ -372,7 +372,7 @@
 
 	/* Look for another proc with the same period to synchronize to */
 	for(i=0; (r = proctab(i)) != nil; i++) {
-		if(r->state == Dead || r == p)
+		if(r->state <= New || r == p)
 			continue;
 		if(r->edf == nil || (r->edf->flags & Admitted) == 0)
 			continue;
--- a/sys/src/9/port/portdat.h
+++ b/sys/src/9/port/portdat.h
@@ -607,6 +607,7 @@
 {
 	Dead = 0,		/* Process states */
 	Moribund,
+	New,
 	Ready,
 	Scheding,
 	Running,
--- a/sys/src/9/port/proc.c
+++ b/sys/src/9/port/proc.c
@@ -42,6 +42,7 @@
 {	/* BUG: generate automatically */
 	"Dead",
 	"Moribund",
+	"New",
 	"Ready",
 	"Scheding",
 	"Running",
@@ -80,6 +81,7 @@
 			ready(up);
 			break;
 		case Moribund:
+			mmurelease(up);
 			up->state = Dead;
 			edfstop(up);
 			if(up->edf != nil){
@@ -86,9 +88,6 @@
 				free(up->edf);
 				up->edf = nil;
 			}
-
-			mmurelease(up);
-
 			lock(&procalloc);
 			up->mach = nil;
 			up->qnext = procalloc.free;
@@ -456,9 +455,21 @@
 	Schedq *rq;
 	void (*pt)(Proc*, int, vlong);
 
-	if(p->state == Ready){
-		print("double ready %s %lud pc %p\n", p->text, p->pid, getcallerpc(&p));
+	switch(p->state){
+	case Running:
+		if(p == up)
+			break;
+		/* wet floor */
+	case Dead:
+	case Moribund:
+	case Scheding:
+		print("ready %s %s %lud pc %p\n", statename[p->state],
+			p->text, p->pid, getcallerpc(&p));
 		return;
+	case Ready:
+		print("double ready %s %lud pc %p\n",
+			p->text, p->pid, getcallerpc(&p));
+		return;
 	}
 
 	s = splhi();
@@ -649,11 +660,13 @@
 		p->index = procalloc.nextindex++;
 		procalloc.tab[p->index] = p;
 	}
+	assert(p->state == Dead);
 	procalloc.free = p->qnext;
 	p->qnext = nil;
 	unlock(&procalloc);
 
-	p->psstate = "New";
+	p->psstate = nil;
+	p->state = New;
 	p->fpstate = FPinit;
 #ifdef KFPSTATE
 	p->kfpstate = FPinit;
@@ -930,7 +943,8 @@
 		/* try for the second lock */
 		if(canlock(r)){
 			if(p->state != Wakeme || r->p != p)
-				panic("procinterrupt: state %d %d %d", r->p != p, p->r != r, p->state);
+				panic("procinterrupt: state %d %d %d",
+					r->p != p, p->r != r, p->state);
 			p->r = nil;
 			r->p = nil;
 			ready(p);
@@ -1022,6 +1036,7 @@
 	if(u != nil && up->lastnote->ref == 1 && strncmp(up->lastnote->msg, "sys:", 4) == 0){
 		int l = strlen(up->lastnote->msg);
 		assert(l < ERRMAX);
+		assert(userureg(u));
 		snprint(up->lastnote->msg+l, ERRMAX-l, " pc=%#p", u->pc);
 	}
 
@@ -1051,7 +1066,7 @@
 int
 pushnote(Proc *p, Note *n)
 {
-	if(p->pid == 0){
+	if(p->state <= New || p->state == Broken || p->pid == 0){
 		freenote(n);
 		return 0;
 	}
@@ -1095,12 +1110,10 @@
 
 	n = mknote(msg, flag);
 	for(i = 0; (p = proctab(i)) != nil; i++){
-		if(p == up)
+		if(p == up || p->noteid != noteid || p->kp)
 			continue;
-		if(p->noteid != noteid || p->kp)
-			continue;
 		qlock(&p->debug);
-		if(p->noteid == noteid){
+		if(p->noteid == noteid && !p->kp){
 			incref(n);
 			pushnote(p, n);
 		}
@@ -1424,7 +1437,7 @@
 	memset(await, 0, conf.nmach*sizeof(await[0]));
 	nwait = 0;
 	for(i = 0; (p = proctab(i)) != nil; i++){
-		if(p->state != Dead && (*match)(p, a)){
+		if(p->state > New && (*match)(p, a)){
 			p->newtlb = 1;
 			for(nm = 0; nm < conf.nmach; nm++){
 				if(MACHP(nm)->proc == p){
@@ -1574,7 +1587,6 @@
 
 	procpriority(p, PriKproc, 0);
 
-	p->psstate = nil;
 	ready(p);
 }
 
@@ -1606,7 +1618,7 @@
 	case Proc_stopme:
 		up->procctl = 0;
 		state = up->psstate;
-		up->psstate = "Stopped";
+		up->psstate = statename[Stopped];
 		/* free a waiting debugger */
 		s = spllo();
 		qlock(&up->debug);
@@ -1691,7 +1703,7 @@
 	max = 0;
 	kp = nil;
 	for(i = 0; (p = proctab(i)) != nil; i++) {
-		if(p->state == Dead || p->kp || p->parentpid == 0)
+		if(p->state <= New || p->kp || p->parentpid == 0)
 			continue;
 		if((p->noswap || (p->procmode & 0222) == 0) && strcmp(eve, p->user) == 0)
 			continue;
@@ -1706,7 +1718,7 @@
 	print("%lud: %s killed: %s\n", kp->pid, kp->text, why);
 	qlock(&kp->seglock);
 	for(i = 0; (p = proctab(i)) != nil; i++) {
-		if(p->state == Dead || p->kp)
+		if(p->state <= New || p->kp)
 			continue;
 		if(p != kp && p->seg[BSEG] != nil && p->seg[BSEG] == kp->seg[BSEG])
 			p->procctl = Proc_exitbig;
--