From: Greg Kroah-Hartman Date: Tue, 13 May 2014 23:00:23 +0000 (-0700) Subject: 3.10-stable patches X-Git-Tag: v3.4.91~27 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f2d81340a63952ff6b88e23b10086e324e95080f;p=thirdparty%2Fkernel%2Fstable-queue.git 3.10-stable patches added patches: scsi-dual-scan-thread-bug-fix.patch scsi-fix-our-current-target-reap-infrastructure.patch scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch --- diff --git a/queue-3.10/scsi-dual-scan-thread-bug-fix.patch b/queue-3.10/scsi-dual-scan-thread-bug-fix.patch new file mode 100644 index 00000000000..266ddaf2cdb --- /dev/null +++ b/queue-3.10/scsi-dual-scan-thread-bug-fix.patch @@ -0,0 +1,73 @@ +From f2495e228fce9f9cec84367547813cbb0d6db15a Mon Sep 17 00:00:00 2001 +From: James Bottomley +Date: Tue, 21 Jan 2014 07:01:41 -0800 +Subject: SCSI: dual scan thread bug fix + +From: James Bottomley + +commit f2495e228fce9f9cec84367547813cbb0d6db15a upstream. + +In the highly unusual case where two threads are running concurrently through +the scanning code scanning the same target, we run into the situation where +one may allocate the target while the other is still using it. In this case, +because the reap checks for STARGET_CREATED and kills the target without +reference counting, the second thread will do the wrong thing on reap. + +Fix this by reference counting even creates and doing the STARGET_CREATED +check in the final put. + +Tested-by: Sarah Sharp +Signed-off-by: James Bottomley +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/scsi/scsi_scan.c | 23 ++++++++++++++++------- + 1 file changed, 16 insertions(+), 7 deletions(-) + +--- a/drivers/scsi/scsi_scan.c ++++ b/drivers/scsi/scsi_scan.c +@@ -320,6 +320,7 @@ static void scsi_target_destroy(struct s + struct Scsi_Host *shost = dev_to_shost(dev->parent); + unsigned long flags; + ++ starget->state = STARGET_DEL; + transport_destroy_device(dev); + spin_lock_irqsave(shost->host_lock, flags); + if (shost->hostt->target_destroy) +@@ -384,9 +385,15 @@ static void scsi_target_reap_ref_release + struct scsi_target *starget + = container_of(kref, struct scsi_target, reap_ref); + +- transport_remove_device(&starget->dev); +- device_del(&starget->dev); +- starget->state = STARGET_DEL; ++ /* ++ * if we get here and the target is still in the CREATED state that ++ * means it was allocated but never made visible (because a scan ++ * turned up no LUNs), so don't call device_del() on it. ++ */ ++ if (starget->state != STARGET_CREATED) { ++ transport_remove_device(&starget->dev); ++ device_del(&starget->dev); ++ } + scsi_target_destroy(starget); + } + +@@ -506,11 +513,13 @@ static struct scsi_target *scsi_alloc_ta + */ + void scsi_target_reap(struct scsi_target *starget) + { ++ /* ++ * serious problem if this triggers: STARGET_DEL is only set in the if ++ * the reap_ref drops to zero, so we're trying to do another final put ++ * on an already released kref ++ */ + BUG_ON(starget->state == STARGET_DEL); +- if (starget->state == STARGET_CREATED) +- scsi_target_destroy(starget); +- else +- scsi_target_reap_ref_put(starget); ++ scsi_target_reap_ref_put(starget); + } + + /** diff --git a/queue-3.10/scsi-fix-our-current-target-reap-infrastructure.patch b/queue-3.10/scsi-fix-our-current-target-reap-infrastructure.patch new file mode 100644 index 00000000000..0a1d8931086 --- /dev/null +++ b/queue-3.10/scsi-fix-our-current-target-reap-infrastructure.patch @@ -0,0 +1,272 @@ +From e63ed0d7a98014fdfc2cfeb3f6dada313dcabb59 Mon Sep 17 00:00:00 2001 +From: James Bottomley +Date: Tue, 21 Jan 2014 07:00:50 -0800 +Subject: scsi: fix our current target reap infrastructure + +From: James Bottomley + +commit e63ed0d7a98014fdfc2cfeb3f6dada313dcabb59 upstream. + +This patch eliminates the reap_ref and replaces it with a proper kref. +On last put of this kref, the target is removed from visibility in +sysfs. The final call to scsi_target_reap() for the device is done from +__scsi_remove_device() and only if the device was made visible. This +ensures that the target disappears as soon as the last device is gone +rather than waiting until final release of the device (which is often +too long). + +Reviewed-by: Alan Stern +Tested-by: Sarah Sharp +Signed-off-by: James Bottomley +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/scsi/scsi_scan.c | 99 +++++++++++++++++++++++++++------------------ + drivers/scsi/scsi_sysfs.c | 20 ++++++--- + include/scsi/scsi_device.h | 3 - + 3 files changed, 75 insertions(+), 47 deletions(-) + +--- a/drivers/scsi/scsi_scan.c ++++ b/drivers/scsi/scsi_scan.c +@@ -371,6 +371,31 @@ static struct scsi_target *__scsi_find_t + } + + /** ++ * scsi_target_reap_ref_release - remove target from visibility ++ * @kref: the reap_ref in the target being released ++ * ++ * Called on last put of reap_ref, which is the indication that no device ++ * under this target is visible anymore, so render the target invisible in ++ * sysfs. Note: we have to be in user context here because the target reaps ++ * should be done in places where the scsi device visibility is being removed. ++ */ ++static void scsi_target_reap_ref_release(struct kref *kref) ++{ ++ struct scsi_target *starget ++ = container_of(kref, struct scsi_target, reap_ref); ++ ++ transport_remove_device(&starget->dev); ++ device_del(&starget->dev); ++ starget->state = STARGET_DEL; ++ scsi_target_destroy(starget); ++} ++ ++static void scsi_target_reap_ref_put(struct scsi_target *starget) ++{ ++ kref_put(&starget->reap_ref, scsi_target_reap_ref_release); ++} ++ ++/** + * scsi_alloc_target - allocate a new or find an existing target + * @parent: parent of the target (need not be a scsi host) + * @channel: target channel number (zero if no channels) +@@ -392,7 +417,7 @@ static struct scsi_target *scsi_alloc_ta + + shost->transportt->target_size; + struct scsi_target *starget; + struct scsi_target *found_target; +- int error; ++ int error, ref_got; + + starget = kzalloc(size, GFP_KERNEL); + if (!starget) { +@@ -401,7 +426,7 @@ static struct scsi_target *scsi_alloc_ta + } + dev = &starget->dev; + device_initialize(dev); +- starget->reap_ref = 1; ++ kref_init(&starget->reap_ref); + dev->parent = get_device(parent); + dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id); + dev->bus = &scsi_bus_type; +@@ -441,29 +466,36 @@ static struct scsi_target *scsi_alloc_ta + return starget; + + found: +- found_target->reap_ref++; ++ /* ++ * release routine already fired if kref is zero, so if we can still ++ * take the reference, the target must be alive. If we can't, it must ++ * be dying and we need to wait for a new target ++ */ ++ ref_got = kref_get_unless_zero(&found_target->reap_ref); ++ + spin_unlock_irqrestore(shost->host_lock, flags); +- if (found_target->state != STARGET_DEL) { ++ if (ref_got) { + put_device(dev); + return found_target; + } +- /* Unfortunately, we found a dying target; need to +- * wait until it's dead before we can get a new one */ ++ /* ++ * Unfortunately, we found a dying target; need to wait until it's ++ * dead before we can get a new one. There is an anomaly here. We ++ * *should* call scsi_target_reap() to balance the kref_get() of the ++ * reap_ref above. However, since the target being released, it's ++ * already invisible and the reap_ref is irrelevant. If we call ++ * scsi_target_reap() we might spuriously do another device_del() on ++ * an already invisible target. ++ */ + put_device(&found_target->dev); +- flush_scheduled_work(); ++ /* ++ * length of time is irrelevant here, we just want to yield the CPU ++ * for a tick to avoid busy waiting for the target to die. ++ */ ++ msleep(1); + goto retry; + } + +-static void scsi_target_reap_usercontext(struct work_struct *work) +-{ +- struct scsi_target *starget = +- container_of(work, struct scsi_target, ew.work); +- +- transport_remove_device(&starget->dev); +- device_del(&starget->dev); +- scsi_target_destroy(starget); +-} +- + /** + * scsi_target_reap - check to see if target is in use and destroy if not + * @starget: target to be checked +@@ -474,28 +506,11 @@ static void scsi_target_reap_usercontext + */ + void scsi_target_reap(struct scsi_target *starget) + { +- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); +- unsigned long flags; +- enum scsi_target_state state; +- int empty = 0; +- +- spin_lock_irqsave(shost->host_lock, flags); +- state = starget->state; +- if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { +- empty = 1; +- starget->state = STARGET_DEL; +- } +- spin_unlock_irqrestore(shost->host_lock, flags); +- +- if (!empty) +- return; +- +- BUG_ON(state == STARGET_DEL); +- if (state == STARGET_CREATED) ++ BUG_ON(starget->state == STARGET_DEL); ++ if (starget->state == STARGET_CREATED) + scsi_target_destroy(starget); + else +- execute_in_process_context(scsi_target_reap_usercontext, +- &starget->ew); ++ scsi_target_reap_ref_put(starget); + } + + /** +@@ -1527,6 +1542,10 @@ struct scsi_device *__scsi_add_device(st + } + mutex_unlock(&shost->scan_mutex); + scsi_autopm_put_target(starget); ++ /* ++ * paired with scsi_alloc_target(). Target will be destroyed unless ++ * scsi_probe_and_add_lun made an underlying device visible ++ */ + scsi_target_reap(starget); + put_device(&starget->dev); + +@@ -1607,8 +1626,10 @@ static void __scsi_scan_target(struct de + + out_reap: + scsi_autopm_put_target(starget); +- /* now determine if the target has any children at all +- * and if not, nuke it */ ++ /* ++ * paired with scsi_alloc_target(): determine if the target has ++ * any children at all and if not, nuke it ++ */ + scsi_target_reap(starget); + + put_device(&starget->dev); +--- a/drivers/scsi/scsi_sysfs.c ++++ b/drivers/scsi/scsi_sysfs.c +@@ -332,17 +332,14 @@ static void scsi_device_dev_release_user + { + struct scsi_device *sdev; + struct device *parent; +- struct scsi_target *starget; + struct list_head *this, *tmp; + unsigned long flags; + + sdev = container_of(work, struct scsi_device, ew.work); + + parent = sdev->sdev_gendev.parent; +- starget = to_scsi_target(parent); + + spin_lock_irqsave(sdev->host->host_lock, flags); +- starget->reap_ref++; + list_del(&sdev->siblings); + list_del(&sdev->same_target_siblings); + list_del(&sdev->starved_entry); +@@ -362,8 +359,6 @@ static void scsi_device_dev_release_user + /* NULL queue means the device can't be used */ + sdev->request_queue = NULL; + +- scsi_target_reap(scsi_target(sdev)); +- + kfree(sdev->inquiry); + kfree(sdev); + +@@ -978,6 +973,13 @@ void __scsi_remove_device(struct scsi_de + sdev->host->hostt->slave_destroy(sdev); + transport_destroy_device(dev); + ++ /* ++ * Paired with the kref_get() in scsi_sysfs_initialize(). We have ++ * remoed sysfs visibility from the device, so make the target ++ * invisible if this was the last device underneath it. ++ */ ++ scsi_target_reap(scsi_target(sdev)); ++ + put_device(dev); + } + +@@ -1040,7 +1042,7 @@ void scsi_remove_target(struct device *d + continue; + if (starget->dev.parent == dev || &starget->dev == dev) { + /* assuming new targets arrive at the end */ +- starget->reap_ref++; ++ kref_get(&starget->reap_ref); + spin_unlock_irqrestore(shost->host_lock, flags); + if (last) + scsi_target_reap(last); +@@ -1124,6 +1126,12 @@ void scsi_sysfs_device_initialize(struct + list_add_tail(&sdev->same_target_siblings, &starget->devices); + list_add_tail(&sdev->siblings, &shost->__devices); + spin_unlock_irqrestore(shost->host_lock, flags); ++ /* ++ * device can now only be removed via __scsi_remove_device() so hold ++ * the target. Target will be held in CREATED state until something ++ * beneath it becomes visible (in which case it moves to RUNNING) ++ */ ++ kref_get(&starget->reap_ref); + } + + int scsi_is_sdev_device(const struct device *dev) +--- a/include/scsi/scsi_device.h ++++ b/include/scsi/scsi_device.h +@@ -248,7 +248,7 @@ struct scsi_target { + struct list_head siblings; + struct list_head devices; + struct device dev; +- unsigned int reap_ref; /* protected by the host lock */ ++ struct kref reap_ref; /* last put renders target invisible */ + unsigned int channel; + unsigned int id; /* target id ... replace + * scsi_device.id eventually */ +@@ -272,7 +272,6 @@ struct scsi_target { + #define SCSI_DEFAULT_TARGET_BLOCKED 3 + + char scsi_level; +- struct execute_work ew; + enum scsi_target_state state; + void *hostdata; /* available to low-level driver */ + unsigned long starget_data[0]; /* for the transport */ diff --git a/queue-3.10/scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch b/queue-3.10/scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch new file mode 100644 index 00000000000..d095cf2f3a4 --- /dev/null +++ b/queue-3.10/scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch @@ -0,0 +1,34 @@ +From 3de2260140417759c669d391613d583baf03b0cf Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Wed, 30 Oct 2013 20:13:51 +0300 +Subject: SCSI: megaraid: missing bounds check in mimd_to_kioc() + +From: Dan Carpenter + +commit 3de2260140417759c669d391613d583baf03b0cf upstream. + +pthru32->dataxferlen comes from the user so we need to check that it's +not too large so we don't overflow the buffer. + +Reported-by: Nico Golde +Reported-by: Fabian Yamaguchi +Signed-off-by: Dan Carpenter +Acked-by: Sumit Saxena +Signed-off-by: James Bottomley +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/scsi/megaraid/megaraid_mm.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/scsi/megaraid/megaraid_mm.c ++++ b/drivers/scsi/megaraid/megaraid_mm.c +@@ -486,6 +486,8 @@ mimd_to_kioc(mimd_t __user *umimd, mraid + + pthru32->dataxferaddr = kioc->buf_paddr; + if (kioc->data_dir & UIOC_WR) { ++ if (pthru32->dataxferlen > kioc->xferlen) ++ return -EINVAL; + if (copy_from_user(kioc->buf_vaddr, kioc->user_data, + pthru32->dataxferlen)) { + return (-EFAULT); diff --git a/queue-3.10/series b/queue-3.10/series new file mode 100644 index 00000000000..3ec2493366e --- /dev/null +++ b/queue-3.10/series @@ -0,0 +1,3 @@ +scsi-fix-our-current-target-reap-infrastructure.patch +scsi-dual-scan-thread-bug-fix.patch +scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch diff --git a/queue-3.14/series b/queue-3.14/series new file mode 100644 index 00000000000..3ec2493366e --- /dev/null +++ b/queue-3.14/series @@ -0,0 +1,3 @@ +scsi-fix-our-current-target-reap-infrastructure.patch +scsi-dual-scan-thread-bug-fix.patch +scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch diff --git a/queue-3.4/series b/queue-3.4/series new file mode 100644 index 00000000000..07f65a4e9b7 --- /dev/null +++ b/queue-3.4/series @@ -0,0 +1 @@ +scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch