summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorriastradh <riastradh@NetBSD.org>2021-09-07 10:42:34 +0000
committerriastradh <riastradh@NetBSD.org>2021-09-07 10:42:34 +0000
commit4ac1b5f915f8feacd45c7fec12f37e4e498f5fbe (patch)
tree55a8c6c4bd90d755d8d315b8edfe3113f33bae1e /sys/dev
parentda08b1461a4da546e48d6a7d95e167a6c5895650 (diff)
ugen(4): Fix race of ugenopen against itself.
Even though we have the kernel lock held, a sleep during kmem_alloc or usbd_open_pipe could allow another ugenopen to run concurrently before we have marked the endpoint opened. To avoid this, mark the endpoint open immediately (while we still have the kernel lock held and before any sleeps, so there is no TOCTOU error here), and then revert on unwind in the event of failure.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/ugen.c13
1 files changed, 8 insertions, 5 deletions
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c
index c7532b6075e..54d61642276 100644
--- a/sys/dev/usb/ugen.c
+++ b/sys/dev/usb/ugen.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ugen.c,v 1.159 2021/09/07 10:42:22 riastradh Exp $ */
+/* $NetBSD: ugen.c,v 1.160 2021/09/07 10:42:34 riastradh Exp $ */
/*
* Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.159 2021/09/07 10:42:22 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.160 2021/09/07 10:42:34 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_compat_netbsd.h"
@@ -510,6 +510,7 @@ ugenopen(dev_t dev, int flag, int mode, struct lwp *l)
struct usbd_xfer *xfer;
int i, j;
int error;
+ int opened;
KASSERT(KERNEL_LOCKED_P()); /* sc_is_open */
@@ -521,7 +522,7 @@ ugenopen(dev_t dev, int flag, int mode, struct lwp *l)
/* The control endpoint allows multiple opens. */
if (endpt == USB_CONTROL_ENDPOINT) {
- sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
+ opened = sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
error = 0;
goto out;
}
@@ -530,6 +531,7 @@ ugenopen(dev_t dev, int flag, int mode, struct lwp *l)
error = EBUSY;
goto out;
}
+ opened = sc->sc_is_open[endpt] = 1;
/* Make sure there are pipes for all directions. */
for (dir = OUT; dir <= IN; dir++) {
@@ -663,9 +665,10 @@ ugenopen(dev_t dev, int flag, int mode, struct lwp *l)
goto out;
}
}
- sc->sc_is_open[endpt] = 1;
error = 0;
-out: ugenif_release(sc);
+out: if (error && opened)
+ sc->sc_is_open[endpt] = 0;
+ ugenif_release(sc);
return error;
}