shithub: front

Download patch

ref: afa2d8b552a269d1bfdd0125f76c148575c6c194
parent: 81f4d1d7fc259f1e05dd101586fe98b7c38c211f
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Jun 16 14:35:36 EDT 2025

kernel: fix deadlock between sysexec() and killbig() (thanks ori)

During exec, we used to hold up->seglock while the stack
is being demand paged in.

In a low-memory situation, this leads to deadlock because
killbig() is calling procpagecount() for each process
to determine is physical memory usage. This function
acqires p->seglock. So killbig() can be stuck once it
hits a process that is starving on memory during faulting
in its stack.

This change makes sysexec release the seglock during its
its stack preparation phase, and re-aquire it once we
enter the "committ" phase (freeing our old stack).

--- a/sys/src/9/port/sysproc.c
+++ b/sys/src/9/port/sysproc.c
@@ -443,15 +443,9 @@
 	 */
 	qlock(&up->seglock);
 	if(waserror()){
-		s = up->seg[ESEG];
-		if(s != nil){
-			up->seg[ESEG] = nil;
-			putseg(s);
-		}
 		qunlock(&up->seglock);
 		nexterror();
 	}
-
 	s = up->seg[SSEG];
 	do {
 		tstk = s->base;
@@ -459,7 +453,18 @@
 			error(Enovmem);
 	} while((s = isoverlap(tstk-USTKSIZE, USTKSIZE)) != nil);
 	up->seg[ESEG] = newseg(SG_STACK | SG_NOEXEC, tstk-USTKSIZE, USTKSIZE/BY2PG);
+	qunlock(&up->seglock);
 
+	if(waserror()){
+		qlock(&up->seglock);
+		s = up->seg[ESEG];
+		if(s != nil){
+			up->seg[ESEG] = nil;
+			putseg(s);
+		}
+		nexterror();
+	}
+
 	/*
 	 * Args: pass 2: assemble; the pages will be faulted in
 	 */
@@ -520,6 +525,9 @@
 	 * Free old memory.
 	 * Special segments are maintained across exec
 	 */
+	poperror();
+	qlock(&up->seglock);
+
 	for(i = SSEG; i <= BSEG; i++) {
 		s = up->seg[i];
 		if(s != nil) {
@@ -576,10 +584,14 @@
 	 * Move the stack
 	 */
 	s = up->seg[ESEG];
+	if(s == nil)
+		error(Egreg);
 	up->seg[ESEG] = nil;
+	qlock(s);
 	s->base = USTKTOP-USTKSIZE;
 	s->top = USTKTOP;
 	relocateseg(s, USTKTOP-tstk);
+	qunlock(s);
 	up->seg[SSEG] = s;
 	qunlock(&up->seglock);
 	poperror();	/* seglock */
@@ -894,8 +906,8 @@
 		qunlock(s);
 		error(Ebadarg);
 	}
-	up->seg[i] = nil;
 	qunlock(s);
+	up->seg[i] = nil;
 	putseg(s);
 	qunlock(&up->seglock);
 	poperror();
--