| From 49718f0fb8c9af192b33d8af3a2826db04025371 Mon Sep 17 00:00:00 2001 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Mon, 17 Aug 2015 11:02:42 -0400 |
| Subject: SCSI: Fix NULL pointer dereference in runtime PM |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Alan Stern <stern@rowland.harvard.edu> |
| |
| commit 49718f0fb8c9af192b33d8af3a2826db04025371 upstream. |
| |
| The routines in scsi_rpm.c assume that if a runtime-PM callback is |
| invoked for a SCSI device, it can only mean that the device's driver |
| has asked the block layer to handle the runtime power management (by |
| calling blk_pm_runtime_init(), which among other things sets q->dev). |
| |
| However, this assumption turns out to be wrong for things like the ses |
| driver. Normally ses devices are not allowed to do runtime PM, but |
| userspace can override this setting. If this happens, the kernel gets |
| a NULL pointer dereference when blk_post_runtime_resume() tries to use |
| the uninitialized q->dev pointer. |
| |
| This patch fixes the problem by calling the block layer's runtime-PM |
| routines only if the device's driver really does have a runtime-PM |
| callback routine. Since ses doesn't define any such callbacks, the |
| crash won't occur. |
| |
| This fixes Bugzilla #101371. |
| |
| Signed-off-by: Alan Stern <stern@rowland.harvard.edu> |
| Reported-by: Stanisław Pitucha <viraptor@gmail.com> |
| Reported-by: Ilan Cohen <ilanco@gmail.com> |
| Tested-by: Ilan Cohen <ilanco@gmail.com> |
| Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> |
| Signed-off-by: James Bottomley <JBottomley@Odin.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/scsi/scsi_pm.c | 22 +++++++++++----------- |
| 1 file changed, 11 insertions(+), 11 deletions(-) |
| |
| --- a/drivers/scsi/scsi_pm.c |
| +++ b/drivers/scsi/scsi_pm.c |
| @@ -149,15 +149,15 @@ static int sdev_runtime_suspend(struct d |
| { |
| const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
| struct scsi_device *sdev = to_scsi_device(dev); |
| - int err; |
| + int err = 0; |
| |
| - err = blk_pre_runtime_suspend(sdev->request_queue); |
| - if (err) |
| - return err; |
| - if (pm && pm->runtime_suspend) |
| + if (pm && pm->runtime_suspend) { |
| + err = blk_pre_runtime_suspend(sdev->request_queue); |
| + if (err) |
| + return err; |
| err = pm->runtime_suspend(dev); |
| - blk_post_runtime_suspend(sdev->request_queue, err); |
| - |
| + blk_post_runtime_suspend(sdev->request_queue, err); |
| + } |
| return err; |
| } |
| |
| @@ -180,11 +180,11 @@ static int sdev_runtime_resume(struct de |
| const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
| int err = 0; |
| |
| - blk_pre_runtime_resume(sdev->request_queue); |
| - if (pm && pm->runtime_resume) |
| + if (pm && pm->runtime_resume) { |
| + blk_pre_runtime_resume(sdev->request_queue); |
| err = pm->runtime_resume(dev); |
| - blk_post_runtime_resume(sdev->request_queue, err); |
| - |
| + blk_post_runtime_resume(sdev->request_queue, err); |
| + } |
| return err; |
| } |
| |