shithub: riscv

Download patch

ref: feda4b1ed25f37676b23d3ee35aa44e14257a6b0
parent: f16547d604a8fd723f81dc145b4f42532a7fbdc5
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;