diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2015-05-25 11:52:16 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2015-05-25 11:52:16 +0000 |
commit | 578109e2ba007bf60faa5e8eae4c6855d9800baa (patch) | |
tree | 54b2a372eab38df25055ca63947db79d70e48a47 /sys/dev | |
parent | a89ac0129da778ec7ba1ac76947a7f6baab80c3b (diff) |
Prevent a use after free in by closing all open endpoints upon detach.
Fix a panic reported by landry@ with Android's ADB.
Tested and ok ajacoutot@
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/usb/ugen.c | 44 |
1 files changed, 30 insertions, 14 deletions
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c index 0ff96505d10..22a62200c3e 100644 --- a/sys/dev/usb/ugen.c +++ b/sys/dev/usb/ugen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ugen.c,v 1.82 2015/03/14 03:38:50 jsg Exp $ */ +/* $OpenBSD: ugen.c,v 1.83 2015/05/25 11:52:15 mpi Exp $ */ /* $NetBSD: ugen.c,v 1.63 2002/11/26 18:49:48 christos Exp $ */ /* $FreeBSD: src/sys/dev/usb/ugen.c,v 1.26 1999/11/17 22:33:41 n_hibma Exp $ */ @@ -107,8 +107,9 @@ void ugenintr(struct usbd_xfer *xfer, void *addr, usbd_status status); void ugen_isoc_rintr(struct usbd_xfer *xfer, void *addr, usbd_status status); int ugen_do_read(struct ugen_softc *, int, struct uio *, int); int ugen_do_write(struct ugen_softc *, int, struct uio *, int); -int ugen_do_ioctl(struct ugen_softc *, int, u_long, - caddr_t, int, struct proc *); +int ugen_do_ioctl(struct ugen_softc *, int, u_long, caddr_t, int, + struct proc *); +int ugen_do_close(struct ugen_softc *, int, int); int ugen_set_config(struct ugen_softc *sc, int configno); int ugen_set_interface(struct ugen_softc *, int, int); int ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx); @@ -398,17 +399,30 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) int ugenclose(dev_t dev, int flag, int mode, struct proc *p) { + struct ugen_softc *sc = ugen_cd.cd_devs[UGENUNIT(dev)]; int endpt = UGENENDPOINT(dev); - struct ugen_softc *sc; - struct ugen_endpoint *sce; - int dir; - int i; + int error; - sc = ugen_cd.cd_devs[UGENUNIT(dev)]; + if (sc == NULL || usbd_is_dying(sc->sc_udev)) + return (EIO); DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n", flag, mode, UGENUNIT(dev), endpt)); + sc->sc_refcnt++; + error = ugen_do_close(sc, endpt, flag); + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(&sc->sc_dev); + + return (error); +} + +int +ugen_do_close(struct ugen_softc *sc, int endpt, int flag) +{ + struct ugen_endpoint *sce; + int dir, i; + #ifdef DIAGNOSTIC if (!sc->sc_is_open[endpt]) { printf("ugenclose: not open\n"); @@ -431,7 +445,6 @@ ugenclose(dev_t dev, int flag, int mode, struct proc *p) DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", endpt, dir, sce)); - usbd_abort_pipe(sce->pipeh); usbd_close_pipe(sce->pipeh); sce->pipeh = NULL; @@ -739,9 +752,8 @@ ugen_detach(struct device *self, int flags) { struct ugen_softc *sc = (struct ugen_softc *)self; struct ugen_endpoint *sce; - int i, dir; - int s; - int maj, mn; + int i, dir, endptno; + int s, maj, mn; DPRINTF(("ugen_detach: sc=%p flags=%d\n", sc, flags)); @@ -773,6 +785,10 @@ ugen_detach(struct device *self, int flags) mn = self->dv_unit * USB_MAX_ENDPOINTS; vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR); + for (endptno = 0; endptno < USB_MAX_ENDPOINTS; endptno++) { + if (sc->sc_is_open[endptno]) + ugen_do_close(sc, endptno, FREAD|FWRITE); + } return (0); } @@ -939,8 +955,8 @@ ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx) } int -ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, - caddr_t addr, int flag, struct proc *p) +ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, caddr_t addr, + int flag, struct proc *p) { struct ugen_endpoint *sce; int err; |