summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKenneth R Westerback <krw@cvs.openbsd.org>2004-02-21 00:34:28 +0000
committerKenneth R Westerback <krw@cvs.openbsd.org>2004-02-21 00:34:28 +0000
commit618fcbcfdb35b097922de9f99398443320146766 (patch)
treea95fb11fdfc8d2b7289cfb9c02c35fe35326399f
parent58a7a2ddeba0c89ba89db730cfad33aeb85c81d6 (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.c30
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();