diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2016-03-12 00:48:45 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2016-03-12 00:48:45 +0000 |
commit | f18c5f678dd337fe643b6f5827f4ecf743b7e991 (patch) | |
tree | 9c7a401e62f723ff9dfe678273435168a945713e /sys | |
parent | 2dbdefc387ff2c3d02c96b0039ba9fff57f28f8e (diff) |
When unplugging an USB umass stick, the kernel could panic because
of a use after free. In sdopen() the scsi link pointer is taken
from the scsi disk struct. While the scsi disk memory is refcounted
by autoconf, the scsi link may be detached and freed during sleep.
The solution is to check wether the disk is dying after every sleep.
The SDF_DYING flag is set before scsi bus and scsi disk are detached,
so without this flag the link must be valid.
input and OK krw@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/scsi/sd.c | 36 |
1 files changed, 34 insertions, 2 deletions
diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c index 3e56fdaa444..77a2430b6ba 100644 --- a/sys/scsi/sd.c +++ b/sys/scsi/sd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sd.c,v 1.263 2016/02/03 21:42:12 bluhm Exp $ */ +/* $OpenBSD: sd.c,v 1.264 2016/03/12 00:48:44 bluhm Exp $ */ /* $NetBSD: sd.c,v 1.111 1997/04/02 02:29:41 mycroft Exp $ */ /*- @@ -366,6 +366,10 @@ sdopen(dev_t dev, int flag, int fmt, struct proc *p) * If any partition is open, but the disk has been invalidated, * disallow further opens of non-raw partition. */ + if (sc->flags & SDF_DYING) { + error = ENXIO; + goto die; + } if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { if (rawopen) goto out; @@ -374,6 +378,10 @@ sdopen(dev_t dev, int flag, int fmt, struct proc *p) } } else { /* Spin up non-UMASS devices ready or not. */ + if (sc->flags & SDF_DYING) { + error = ENXIO; + goto die; + } if ((sc_link->flags & SDEV_UMASS) == 0) scsi_start(sc_link, SSS_START, (rawopen ? SCSI_SILENT : 0) | SCSI_IGNORE_ILLEGAL_REQUEST | @@ -385,6 +393,10 @@ sdopen(dev_t dev, int flag, int fmt, struct proc *p) * device returns "Initialization command required." and causes * a loop of scsi_start() calls. */ + if (sc->flags & SDF_DYING) { + error = ENXIO; + goto die; + } sc_link->flags |= SDEV_OPEN; /* @@ -399,10 +411,13 @@ sdopen(dev_t dev, int flag, int fmt, struct proc *p) } /* Check that it is still responding and ok. */ + if (sc->flags & SDF_DYING) { + error = ENXIO; + goto die; + } error = scsi_test_unit_ready(sc_link, TEST_READY_RETRIES, SCSI_SILENT | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); - if (error) { if (rawopen) { error = 0; @@ -412,9 +427,17 @@ sdopen(dev_t dev, int flag, int fmt, struct proc *p) } /* Load the physical device parameters. */ + if (sc->flags & SDF_DYING) { + error = ENXIO; + goto die; + } sc_link->flags |= SDEV_MEDIA_LOADED; if (sd_get_parms(sc, &sc->params, (rawopen ? SCSI_SILENT : 0)) == SDGP_RESULT_OFFLINE) { + if (sc->flags & SDF_DYING) { + error = ENXIO; + goto die; + } sc_link->flags &= ~SDEV_MEDIA_LOADED; error = ENXIO; goto bad; @@ -438,13 +461,22 @@ out: /* It's OK to fall through because dk_openmask is now non-zero. */ bad: if (sc->sc_dk.dk_openmask == 0) { + if (sc->flags & SDF_DYING) { + error = ENXIO; + goto die; + } if ((sc_link->flags & SDEV_REMOVABLE) != 0) scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); + if (sc->flags & SDF_DYING) { + error = ENXIO; + goto die; + } sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED); } +die: disk_unlock(&sc->sc_dk); device_unref(&sc->sc_dev); return (error); |