code: plan9front

Download patch

ref: f39abb2923ac5faf55ce283e925671d639f3b9b9
parent: da308716c73a2b06f861e7535df425ca0dd62ee1
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Sep 17 16:12:20 EDT 2023

devip: run Medium.unbind() with ifc->conv released, cleanup

Medium.unbind() must run with ifc->conv unlocked
as mediumunbindifc() holds it while determining
if it should also unbind causing a potential
deadlock.

Note that ipifcnind() and Medium.bind() is run
with ifc->conv locked, delaying mediumunbindifc()
after ipifcbind() completes.

--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -123,7 +123,6 @@
 
 /* same as nullmedium, to prevent unbind while bind or unbind is in progress */
 extern Medium unboundmedium;
-static char *ipifcunbindmedium(Ipifc *ifc, Medium *m);
 
 /*
  *  attach a device (or pkt driver) to the interface.
@@ -151,26 +150,7 @@
 	}
 	ifc->m = &unboundmedium;	/* fake until bound */
 	ifc->arg = nil;
-	if(m->unbindonclose == 0)
-		c->inuse++;
-	snprint(ifc->dev, sizeof ifc->dev, "%s%d", ifc->m->name, c->x);
-	wunlock(ifc);
 
-	if(waserror()){
-		wlock(ifc);
-		if(m->unbindonclose == 0)
-			c->inuse--;
-		ifc->m = nil;
-		wunlock(ifc);
-		nexterror();
-	}
-	(*m->bind)(ifc, argc, argv);
-	poperror();
-
-	wlock(ifc);
-	/* switch to the real medium */
-	ifc->m = m;
-
 	/* set the bound device name */
 	if(argc > 2)
 		strncpy(ifc->dev, argv[2], sizeof(ifc->dev));
@@ -185,7 +165,23 @@
 
 	/* default router paramters */
 	ifc->rp = c->p->f->v6p->rp;
+	wunlock(ifc);
 
+	if(waserror()){
+		wlock(ifc);
+		ifc->m = nil;
+		wunlock(ifc);
+		nexterror();
+	}
+	(*m->bind)(ifc, argc, argv);
+	poperror();
+
+	wlock(ifc);
+	/* switch to real medium */
+	ifc->m = m;
+	if(m->unbindonclose == 0)
+		c->inuse++;
+
 	/* any ancillary structures (like routes) no longer pertain */
 	ifc->ifcid++;
 
@@ -216,10 +212,12 @@
 	if(m->unbind != nil){
 		ifc->m = &unboundmedium;	/* fake until unbound */
 		wunlock(ifc);
+		qunlock(ifc->conv);
 		if(!waserror()){
 			(*m->unbind)(ifc);
 			poperror();
 		}
+		qlock(ifc->conv);
 		wlock(ifc);
 	}
 
@@ -269,35 +267,21 @@
 mediumunbindifc(Ipifc *ifc)
 {
 	Medium *m;
-	Conv *conv;
 	char *err;
 
-	err = Eunbound;
-
 	rlock(ifc);
 	m = ifc->m;
-	if(m == &unboundmedium){
-		runlock(ifc);
-		return err;
-	}
-	conv = ifc->conv;
 	runlock(ifc);
 
-	assert(conv != nil);
-	assert(m != nil);
-	assert(m->unbindonclose == 0);
-
-	qlock(conv);
-
-	assert(conv->inuse > 0);
-	assert((Ipifc*)conv->ptcl == ifc);
-
-	wlock(ifc);
-	if(ifc->m == m)
-		err = ipifcunbindmedium(ifc, m);
-	wunlock(ifc);
-	qunlock(conv);
-
+	err = Eunbound;
+	if(m != nil && m != &unboundmedium){
+		qlock(ifc->conv);
+		wlock(ifc);
+		if(ifc->m == m)
+			err = ipifcunbindmedium(ifc, m);
+		wunlock(ifc);
+		qunlock(ifc->conv);
+	}
 	return err;
 }
 
@@ -559,7 +543,7 @@
 	}
 
 	wlock(ifc);
-	if(ifc->m == nil){
+	if(ifc->m == nil || ifc->m == &unboundmedium){
 		wunlock(ifc);
 		return Eunbound;
 	}
@@ -908,7 +892,7 @@
 		err = ipifcremove6(ifc, argv, argc);
 	else {
 		wlock(ifc);
-		if(ifc->m == nil){
+		if(ifc->m == nil || ifc->m == &unboundmedium){
 			wunlock(ifc);
 			return Eunbound;
 		}
@@ -1697,7 +1681,7 @@
 		if(nifc == ifc || !canrlock(nifc))
 			continue;
 
-		if(nifc->m == nil
+		if(nifc->m == nil || nifc->m == &unboundmedium
 		|| (lifc = ipremoteonifc(nifc, ip)) == nil
 		|| (lifc->type & Rptpt) != 0
 		|| waserror()){
@@ -1760,7 +1744,7 @@
 		return Ebadarg;
 
 	rlock(ifc);
-	if(ifc->m == nil){
+	if(ifc->m == nil || ifc->m == &unboundmedium){
 		runlock(ifc);
 		return Eunbound;
 	}