git: 9front

Download patch

ref: 0fa3ff0598bfee59d64b88eb78e8d68c1c823e05
parent: dc9558b7c645baaa6f6eacfc90f89d2d35795934
author: Jacob Moody <moody@posixcafe.org>
date: Sat Aug 20 15:21:55 EDT 2022

rio: make it harder to deadlock from kbdtap

It was quite easy for a single threaded
tap program to cause a deadlock. The
following describes the scenario:

prog: read a key from /dev/kbdtap
prog: processing key
rio: read a key from the 'upstream' /dev/kbd
rio: submit key to tap thread
prog: writes translated key back to /dev/kbdtap

Because the tap thread is blocked on waiting for
the program to read the new character, the program's
write never completes and we hit deadlock.

This was "conviently" avoided in testing with ktrans
because the reader and writer are in two different
processes. So in this case ktrans could make forward
progress.

To solve this we first turn the tap channels to have
a small buffer, this grases the wheel and allows the
above case to become tolerant for some amount of
new upstream characters.

Second we move the tap processing thread in to its
own process. This isolates this processing from the
rest of the main rio thread. This ensures that a misbehaving
kbdtap user will never lock you out of being able to 'Delete' it
and restore normal functionality.

--- a/sys/src/cmd/rio/rio.c
+++ b/sys/src/cmd/rio/rio.c
@@ -38,7 +38,7 @@
 int		threadrforkflag = 0;	/* should be RFENVG but that hides rio from plumber */
 
 void	mousethread(void*);
-void	keyboardthread(void*);
+void	keyboardtap(void*);
 void	winclosethread(void*);
 void	initcmd(void*);
 Channel* initkbd(void);
@@ -196,8 +196,9 @@
 	kbdchan = initkbd();
 	if(kbdchan == nil)
 		error("can't find keyboard");
-	totap = chancreate(sizeof(char*), 0);
-	fromtap = chancreate(sizeof(char*), 0);
+	totap = chancreate(sizeof(char*), 32);
+	fromtap = chancreate(sizeof(char*), 32);
+	proccreate(keyboardtap, nil, STACK);
 
 	wscreen = allocscreen(screen, background, 0);
 	if(wscreen == nil)
@@ -206,7 +207,6 @@
 	flushimage(display, 1);
 
 	timerinit();
-	threadcreate(keyboardthread, nil, STACK);
 	threadcreate(mousethread, nil, STACK);
 	threadcreate(winclosethread, nil, STACK);
 	filsys = filsysinit(xfidinit());
@@ -337,7 +337,7 @@
 }
 
 void
-keyboardthread(void*)
+keyboardtap(void*)
 {
 	char *s;
 	Channel *out;
@@ -345,7 +345,7 @@
 	enum { Kdev, Ktap, NALT};
 	enum { Mnorm, Mtap };
 
-	threadsetname("keyboardthread");	
+	threadsetname("keyboardtap");	
 
 	static Alt alts[NALT+1];
 	alts[Kdev].c = kbdchan;
--- a/sys/src/cmd/rio/xfid.c
+++ b/sys/src/cmd/rio/xfid.c
@@ -575,8 +575,14 @@
 			return;
 		}
 		e = x->data + cnt;
-		for(p = x->data; p < e; p += strlen(p)+1)
+		for(p = x->data; p < e; p += strlen(p)+1){
+			if(*p == '\0'){
+				fc.count = p - x->data;
+				filsysrespond(x->fs, x, &fc, "null message type");
+				return;
+			}
 			chanprint(fromtap, "%s", p);
+		}
 		break;
 
 	default:
@@ -704,6 +710,7 @@
 		alts[Aflush].v = nil;
 		alts[Aflush].op = CHANRCV;
 		alts[Aend].op = CHANEND;
+
 		switch(alt(alts)){
 		case Adata:
 			break;
@@ -715,6 +722,7 @@
 			return;
 		}
 		fc.data = t;
+		/* kbdproc ensures we're only dealing with one message */
 		fc.count = strlen(t)+1;
 		filsysrespond(x->fs, x, &fc, nil);
 		free(t);
--