diff options
author | Tony Battersby <tonyb@cybernetics.com> | 2009-01-20 17:00:09 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-03-12 13:58:05 -0400 |
commit | a2dd3b4cea335713b58996bb07b3abcde1175f47 (patch) | |
tree | 3097816f8abd5fb57dda669d3e776c35f1455f43 /drivers/scsi/sg.c | |
parent | c6517b7942fad663cc1cf3235cbe4207cf769332 (diff) |
[SCSI] sg: fix races with ioctl(SG_IO)
sg_io_owned needs to be set before the command is sent to the midlevel;
otherwise, a quickly-completing command may cause a different CPU
to see "srp->done == 1 && !srp->sg_io_owned", which would lead to
incorrect behavior.
Check srp->done and set srp->orphan while holding rq_list_lock to
prevent races with sg_rq_end_io().
There is no need to check sfp->closed from read/write/ioctl/poll/etc.
since the kernel guarantees that this won't happen.
The usefulness of sg_srp_done() was questionable before; now it is
definitely not needed.
Signed-off-by: Tony Battersby <tonyb@cybernetics.com>
Acked-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/sg.c')
-rw-r--r-- | drivers/scsi/sg.c | 39 |
1 files changed, 14 insertions, 25 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index b447527555a7..18d079e3990e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c | |||
@@ -189,7 +189,7 @@ static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, | |||
189 | Sg_request * srp); | 189 | Sg_request * srp); |
190 | static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, | 190 | static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, |
191 | const char __user *buf, size_t count, int blocking, | 191 | const char __user *buf, size_t count, int blocking, |
192 | int read_only, Sg_request **o_srp); | 192 | int read_only, int sg_io_owned, Sg_request **o_srp); |
193 | static int sg_common_write(Sg_fd * sfp, Sg_request * srp, | 193 | static int sg_common_write(Sg_fd * sfp, Sg_request * srp, |
194 | unsigned char *cmnd, int timeout, int blocking); | 194 | unsigned char *cmnd, int timeout, int blocking); |
195 | static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer); | 195 | static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer); |
@@ -561,7 +561,8 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) | |||
561 | return -EFAULT; | 561 | return -EFAULT; |
562 | blocking = !(filp->f_flags & O_NONBLOCK); | 562 | blocking = !(filp->f_flags & O_NONBLOCK); |
563 | if (old_hdr.reply_len < 0) | 563 | if (old_hdr.reply_len < 0) |
564 | return sg_new_write(sfp, filp, buf, count, blocking, 0, NULL); | 564 | return sg_new_write(sfp, filp, buf, count, |
565 | blocking, 0, 0, NULL); | ||
565 | if (count < (SZ_SG_HEADER + 6)) | 566 | if (count < (SZ_SG_HEADER + 6)) |
566 | return -EIO; /* The minimum scsi command length is 6 bytes. */ | 567 | return -EIO; /* The minimum scsi command length is 6 bytes. */ |
567 | 568 | ||
@@ -642,7 +643,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) | |||
642 | 643 | ||
643 | static ssize_t | 644 | static ssize_t |
644 | sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, | 645 | sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, |
645 | size_t count, int blocking, int read_only, | 646 | size_t count, int blocking, int read_only, int sg_io_owned, |
646 | Sg_request **o_srp) | 647 | Sg_request **o_srp) |
647 | { | 648 | { |
648 | int k; | 649 | int k; |
@@ -662,6 +663,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, | |||
662 | SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n")); | 663 | SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n")); |
663 | return -EDOM; | 664 | return -EDOM; |
664 | } | 665 | } |
666 | srp->sg_io_owned = sg_io_owned; | ||
665 | hp = &srp->header; | 667 | hp = &srp->header; |
666 | if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) { | 668 | if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) { |
667 | sg_remove_request(sfp, srp); | 669 | sg_remove_request(sfp, srp); |
@@ -766,18 +768,6 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, | |||
766 | } | 768 | } |
767 | 769 | ||
768 | static int | 770 | static int |
769 | sg_srp_done(Sg_request *srp, Sg_fd *sfp) | ||
770 | { | ||
771 | unsigned long iflags; | ||
772 | int done; | ||
773 | |||
774 | read_lock_irqsave(&sfp->rq_list_lock, iflags); | ||
775 | done = srp->done; | ||
776 | read_unlock_irqrestore(&sfp->rq_list_lock, iflags); | ||
777 | return done; | ||
778 | } | ||
779 | |||
780 | static int | ||
781 | sg_ioctl(struct inode *inode, struct file *filp, | 771 | sg_ioctl(struct inode *inode, struct file *filp, |
782 | unsigned int cmd_in, unsigned long arg) | 772 | unsigned int cmd_in, unsigned long arg) |
783 | { | 773 | { |
@@ -809,27 +799,26 @@ sg_ioctl(struct inode *inode, struct file *filp, | |||
809 | return -EFAULT; | 799 | return -EFAULT; |
810 | result = | 800 | result = |
811 | sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, | 801 | sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, |
812 | blocking, read_only, &srp); | 802 | blocking, read_only, 1, &srp); |
813 | if (result < 0) | 803 | if (result < 0) |
814 | return result; | 804 | return result; |
815 | srp->sg_io_owned = 1; | ||
816 | while (1) { | 805 | while (1) { |
817 | result = 0; /* following macro to beat race condition */ | 806 | result = 0; /* following macro to beat race condition */ |
818 | __wait_event_interruptible(sfp->read_wait, | 807 | __wait_event_interruptible(sfp->read_wait, |
819 | (sdp->detached || sfp->closed || sg_srp_done(srp, sfp)), | 808 | (srp->done || sdp->detached), |
820 | result); | 809 | result); |
821 | if (sdp->detached) | 810 | if (sdp->detached) |
822 | return -ENODEV; | 811 | return -ENODEV; |
823 | if (sfp->closed) | 812 | write_lock_irq(&sfp->rq_list_lock); |
824 | return 0; /* request packet dropped already */ | 813 | if (srp->done) { |
825 | if (0 == result) | 814 | srp->done = 2; |
815 | write_unlock_irq(&sfp->rq_list_lock); | ||
826 | break; | 816 | break; |
817 | } | ||
827 | srp->orphan = 1; | 818 | srp->orphan = 1; |
819 | write_unlock_irq(&sfp->rq_list_lock); | ||
828 | return result; /* -ERESTARTSYS because signal hit process */ | 820 | return result; /* -ERESTARTSYS because signal hit process */ |
829 | } | 821 | } |
830 | write_lock_irqsave(&sfp->rq_list_lock, iflags); | ||
831 | srp->done = 2; | ||
832 | write_unlock_irqrestore(&sfp->rq_list_lock, iflags); | ||
833 | result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); | 822 | result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); |
834 | return (result < 0) ? result : 0; | 823 | return (result < 0) ? result : 0; |
835 | } | 824 | } |