code: plan9front

Download patch

ref: a327175a3c01d18e3e4c061ce4579cc420ee3561
parent: 6d199d6a314d268d10b1e4b4c70c26f0b36a6f75
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Sep 30 13:06:58 EDT 2023

nusb/usbd: attach spam protection for reset loops and flanky devices

When attach happens on a port within Attachdelay (3 seconds),
we count it as a repeated attach and if this happens
Attachcount (5) times in a row, we print an error and
refuse to attach the device.

Ori needs this as he has a usb hid device rgb led controller
in his computer that just gives read errors and causes
a continuous reset loop on the port, spamming the console.

--- a/sys/src/cmd/nusb/usbd/dat.h
+++ b/sys/src/cmd/nusb/usbd/dat.h
@@ -54,14 +54,14 @@
 	Pconfiged,
 
 	/* Delays, timeouts (ms) */
-	Spawndelay	= 250,		/* how often may we re-spawn a driver */
 	Resetdelay	= 20,		/* how much to wait after a reset */
 	Enabledelay	= 20,		/* how much to wait after an enable */
 	Powerdelay	= 100,		/* after powering up ports */
 	Pollms		= 250, 		/* port poll interval */
-	Chgdelay	= 100,		/* waiting for port become stable */
-	Chgtmout	= 1000,		/* ...but at most this much */
 
+	Attachdelay	= 3000,		/* attach considered repeated if within Attachdelay */
+	Attachcount	= 5,		/* maximum number of repeated attaches before giving up */
+
 	/*
 	 * device tab for embedded usb drivers.
 	 */
@@ -91,6 +91,8 @@
 {
 	int	state;		/* state of the device */
 	u32int	sts;		/* old port status */
+	ulong	atime;		/* time of last attach in milliseconds */
+	int	acount;		/* rapid attach counter */
 	uchar	removable;
 	uchar	pwrctl;
 	Dev	*dev;		/* attached device (if non-nil) */
--- a/sys/src/cmd/nusb/usbd/hub.c
+++ b/sys/src/cmd/nusb/usbd/hub.c
@@ -10,6 +10,7 @@
 static int nhubs;
 static int mustdump;
 static int pollms = Pollms;
+static ulong nowms;
 
 static char *dsname[] = { "disabled", "attached", "configed" };
 
@@ -358,6 +359,20 @@
 	d = h->dev;
 	pp = &h->port[p];
 	nd = nil;
+
+	/*
+	 * prevent repeated attaches in short succession as it is a indication
+	 * for a reset loop or a very flanky device.
+	 */
+	if(pp->acount && nowms - pp->atime >= Attachdelay)
+		pp->acount = 0;
+	pp->atime = nowms;
+	if(++pp->acount > Attachcount){
+		fprint(2, "%s: %s: port %d: too many attaches in short succession\n",
+			argv0, d->dir, p);
+		goto Fail;
+	}
+
 	pp->state = Pattached;
 	dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts);
 	if(h->dev->isusb3){
@@ -558,7 +573,12 @@
 				dprint(2, "%s: %s: port %d: unsuspend: %r\n", argv0, d->dir, p);
 			sleep(Enabledelay);
 			sts = portstatus(h, p);
-			fprint(2, "%s: %s: port %d: unsuspended (sts %#ux)\n", argv0, d->dir, p, sts);
+			if(sts == -1){
+				hubfail(h);
+				return -1;
+			}
+			fprint(2, "%s: %s: port %d: unsuspended sts: %s %#ux\n", argv0, d->dir, p,
+				stsstr(sts, h->dev->isusb3), sts);
 		}
 	}
 	if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){
@@ -568,7 +588,7 @@
 	}else if(portgone(pp, sts)){
 		portdetach(h, p);
 	}else if(portresetwanted(h, p)){
-		portdetach(h, p);	
+		portdetach(h, p);
 		if(!d->isusb3)
 			portfeature(h, p, Fportenable, 0);
 		/* pretend it is gone, causing a re-attach and port reset */
@@ -614,7 +634,7 @@
 	 * Do not use hub interrupt endpoint because we
 	 * have to poll the root hub(s) in any case.
 	 */
-	for(;;){
+	for(;;nowms += pollms){
 		qlock(&hublock);
 Again:
 		for(h = hubs; h != nil; h = h->next)