summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2013-06-03 16:10:57 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2013-06-03 16:10:57 +0000
commiteb4516b76efc86d0784c3c4b27f65a90d19b1d3d (patch)
tree0f34701c7b73270b88743eca1a26ee3817e27b53 /sys
parentf2571937412ae8328d532703185a9d82318b7bca (diff)
Correctly increment the refcounter when trying to open a non connected
serial. Fix a panic triggered by removing the USB device while the opener is sleeping holding a reference to the softc. Found the hardway by tobiasu@. While here add more sanity checks to avoid races when the driver is being detached. ok tobiasu@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/usb/ucom.c71
1 files changed, 51 insertions, 20 deletions
diff --git a/sys/dev/usb/ucom.c b/sys/dev/usb/ucom.c
index a5d2c3a6792..400d11f2016 100644
--- a/sys/dev/usb/ucom.c
+++ b/sys/dev/usb/ucom.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ucom.c,v 1.58 2013/04/15 09:23:02 mglocker Exp $ */
+/* $OpenBSD: ucom.c,v 1.59 2013/06/03 16:10:56 mpi Exp $ */
/* $NetBSD: ucom.c,v 1.49 2003/01/01 00:10:25 thorpej Exp $ */
/*
@@ -127,8 +127,9 @@ void ucom_hwiflow(struct ucom_softc *);
int ucomparam(struct tty *, struct termios *);
void ucomstart(struct tty *);
void ucom_shutdown(struct ucom_softc *);
-int ucom_do_ioctl(struct ucom_softc *, u_long, caddr_t,
- int, struct proc *);
+int ucom_do_open(dev_t, int, int, struct proc *);
+int ucom_do_ioctl(struct ucom_softc *, u_long, caddr_t, int, struct proc *);
+int ucom_do_close(struct ucom_softc *, int, int , struct proc *);
void ucom_dtr(struct ucom_softc *, int);
void ucom_rts(struct ucom_softc *, int);
void ucom_break(struct ucom_softc *, int);
@@ -160,7 +161,6 @@ const struct cfattach ucom_ca = {
void
ucom_lock(struct ucom_softc *sc)
{
- sc->sc_refcnt++;
rw_enter_write(&sc->sc_lock);
}
@@ -168,8 +168,6 @@ void
ucom_unlock(struct ucom_softc *sc)
{
rw_exit_write(&sc->sc_lock);
- if (--sc->sc_refcnt < 0)
- usb_detach_wakeup(&sc->sc_dev);
}
int
@@ -295,11 +293,7 @@ int
ucomopen(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = UCOMUNIT(dev);
- usbd_status err;
struct ucom_softc *sc;
- struct tty *tp;
- struct termios t;
- int s;
int error;
if (unit >= ucom_cd.cd_ndevs)
@@ -314,6 +308,23 @@ ucomopen(dev_t dev, int flag, int mode, struct proc *p)
if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
return (ENXIO);
+ sc->sc_refcnt++;
+ error = ucom_do_open(dev, flag, mode, p);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(&sc->sc_dev);
+
+ return (error);
+}
+
+int
+ucom_do_open(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
+ usbd_status err;
+ struct tty *tp;
+ struct termios t;
+ int error, s;
+
/* open the pipes if this is the first open */
ucom_lock(sc);
s = splusb();
@@ -529,6 +540,22 @@ int
ucomclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
+ int error;
+
+ if (sc == NULL || sc->sc_dying)
+ return (EIO);
+
+ sc->sc_refcnt++;
+ error = ucom_do_close(sc, flag, mode, p);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(&sc->sc_dev);
+
+ return (error);
+}
+
+int
+ucom_do_close(struct ucom_softc *sc, int flag, int mode, struct proc *p)
+{
struct tty *tp = sc->sc_tty;
int s;
@@ -561,10 +588,11 @@ ucomread(dev_t dev, struct uio *uio, int flag)
struct tty *tp = sc->sc_tty;
int error;
- if (sc->sc_dying)
+ if (sc == NULL || sc->sc_dying)
return (EIO);
sc->sc_refcnt++;
+ tp = sc->sc_tty;
error = (*LINESW(tp, l_read))(tp, uio, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
@@ -575,13 +603,14 @@ int
ucomwrite(dev_t dev, struct uio *uio, int flag)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
- struct tty *tp = sc->sc_tty;
+ struct tty *tp;
int error;
- if (sc->sc_dying)
+ if (sc == NULL || sc->sc_dying)
return (EIO);
sc->sc_refcnt++;
+ tp = sc->sc_tty;
error = (*LINESW(tp, l_write))(tp, uio, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
@@ -592,9 +621,11 @@ struct tty *
ucomtty(dev_t dev)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
- struct tty *tp = sc->sc_tty;
- return (tp);
+ if (sc == NULL || sc->sc_dying)
+ return (NULL);
+
+ return (sc->sc_tty);
}
int
@@ -603,6 +634,9 @@ ucomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
int error;
+ if (sc == NULL || sc->sc_dying)
+ return (EIO);
+
sc->sc_refcnt++;
error = ucom_do_ioctl(sc, cmd, data, flag, p);
if (--sc->sc_refcnt < 0)
@@ -618,9 +652,6 @@ ucom_do_ioctl(struct ucom_softc *sc, u_long cmd, caddr_t data,
int error;
int s;
- if (sc->sc_dying)
- return (EIO);
-
DPRINTF(("ucomioctl: cmd=0x%08lx\n", cmd));
error = (*LINESW(tp, l_ioctl))(tp, cmd, data, flag, p);
@@ -817,7 +848,7 @@ ucomparam(struct tty *tp, struct termios *t)
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];
int error;
- if (sc->sc_dying)
+ if (sc == NULL || sc->sc_dying)
return (EIO);
/* Check requested parameters. */
@@ -914,7 +945,7 @@ ucomstart(struct tty *tp)
u_char *data;
int cnt;
- if (sc->sc_dying)
+ if (sc == NULL || sc->sc_dying)
return;
s = spltty();