diff options
author | Kenneth R Westerback <krw@cvs.openbsd.org> | 2004-02-21 00:34:28 +0000 |
---|---|---|
committer | Kenneth R Westerback <krw@cvs.openbsd.org> | 2004-02-21 00:34:28 +0000 |
commit | 618fcbcfdb35b097922de9f99398443320146766 (patch) | |
tree | a95fb11fdfc8d2b7289cfb9c02c35fe35326399f | |
parent | 58a7a2ddeba0c89ba89db730cfad33aeb85c81d6 (diff) |
Fix a race. scsi_done() can free a scsi_xfer, so relying on the values
of fields in the scsi_xfer after scsi_done() could have been called is
bad.
cdrecord can now safely burn cd's at high speeds.
Found by grange@, original diff by costa@, cleaned up by grange@ and
further polished by me following input from deraadt@.
ok grange@ costa@ deraadt@.
-rw-r--r-- | sys/scsi/scsi_base.c | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/sys/scsi/scsi_base.c b/sys/scsi/scsi_base.c index aebee5125c1..f1608f9d4a6 100644 --- a/sys/scsi/scsi_base.c +++ b/sys/scsi/scsi_base.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scsi_base.c,v 1.52 2004/02/17 23:50:46 krw Exp $ */ +/* $OpenBSD: scsi_base.c,v 1.53 2004/02/21 00:34:27 krw Exp $ */ /* $NetBSD: scsi_base.c,v 1.43 1997/04/02 02:29:36 mycroft Exp $ */ /* @@ -451,6 +451,7 @@ scsi_execute_xs(xs) { int error; int s; + int flags; xs->flags &= ~ITSDONE; xs->error = XS_NOERROR; @@ -473,12 +474,35 @@ retry: * code both expect us to return straight to them, so as soon * as the command is queued, return. */ + + /* + * We save the flags here because the xs structure may already + * be freed by scsi_done by the time adapter->scsi_cmd returns. + * + * scsi_done is responsible for freeing the xs if either + * (flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP + * -or- + * (flags & SCSI_USER) != 0 + * + * Note: SCSI_USER must always be called with SCSI_NOSLEEP + * and never with SCSI_POLL, so the second expression should be + * is equivalent to the first. + */ + + flags = xs->flags; +#ifdef DIAGNOSTIC + if ((flags & (SCSI_USER | SCSI_NOSLEEP)) == SCSI_USER) + panic("scsi_execute_xs: USER without NOSLEEP"); + if ((flags & (SCSI_USER | SCSI_POLL)) == (SCSI_USER | SCSI_POLL)) + panic("scsi_execute_xs: USER with POLL"); +#endif + switch ((*(xs->sc_link->adapter->scsi_cmd)) (xs)) { case SUCCESSFULLY_QUEUED: - if ((xs->flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP) + if ((flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP) return EJUSTRETURN; #ifdef DIAGNOSTIC - if (xs->flags & SCSI_NOSLEEP) + if (flags & SCSI_NOSLEEP) panic("scsi_execute_xs: NOSLEEP and POLL"); #endif s = splbio(); |