| From 4fd41a8552afc01054d9d9fc7f1a63c324867d27 Mon Sep 17 00:00:00 2001 |
| From: Ken Xue <ken.xue@amd.com> |
| Date: Tue, 1 Dec 2015 14:45:46 +0800 |
| Subject: SCSI: Fix NULL pointer dereference in runtime PM |
| |
| From: Ken Xue <ken.xue@amd.com> |
| |
| commit 4fd41a8552afc01054d9d9fc7f1a63c324867d27 upstream. |
| |
| The routines in scsi_pm.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 checking q->dev in block layer before |
| handle runtime PM. Since ses doesn't define any PM callbacks and call |
| blk_pm_runtime_init(), the crash won't occur. |
| |
| This fixes Bugzilla #101371. |
| https://bugzilla.kernel.org/show_bug.cgi?id=101371 |
| |
| More discussion can be found from below link. |
| http://marc.info/?l=linux-scsi&m=144163730531875&w=2 |
| |
| Signed-off-by: Ken Xue <Ken.Xue@amd.com> |
| Acked-by: Alan Stern <stern@rowland.harvard.edu> |
| Cc: Xiangliang Yu <Xiangliang.Yu@amd.com> |
| Cc: James E.J. Bottomley <JBottomley@odin.com> |
| Cc: Jens Axboe <axboe@kernel.dk> |
| Cc: Michael Terry <Michael.terry@canonical.com> |
| Signed-off-by: Jens Axboe <axboe@fb.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| block/blk-core.c | 12 ++++++++++++ |
| 1 file changed, 12 insertions(+) |
| |
| --- a/block/blk-core.c |
| +++ b/block/blk-core.c |
| @@ -3164,6 +3164,9 @@ int blk_pre_runtime_suspend(struct reque |
| { |
| int ret = 0; |
| |
| + if (!q->dev) |
| + return ret; |
| + |
| spin_lock_irq(q->queue_lock); |
| if (q->nr_pending) { |
| ret = -EBUSY; |
| @@ -3191,6 +3194,9 @@ EXPORT_SYMBOL(blk_pre_runtime_suspend); |
| */ |
| void blk_post_runtime_suspend(struct request_queue *q, int err) |
| { |
| + if (!q->dev) |
| + return; |
| + |
| spin_lock_irq(q->queue_lock); |
| if (!err) { |
| q->rpm_status = RPM_SUSPENDED; |
| @@ -3215,6 +3221,9 @@ EXPORT_SYMBOL(blk_post_runtime_suspend); |
| */ |
| void blk_pre_runtime_resume(struct request_queue *q) |
| { |
| + if (!q->dev) |
| + return; |
| + |
| spin_lock_irq(q->queue_lock); |
| q->rpm_status = RPM_RESUMING; |
| spin_unlock_irq(q->queue_lock); |
| @@ -3237,6 +3246,9 @@ EXPORT_SYMBOL(blk_pre_runtime_resume); |
| */ |
| void blk_post_runtime_resume(struct request_queue *q, int err) |
| { |
| + if (!q->dev) |
| + return; |
| + |
| spin_lock_irq(q->queue_lock); |
| if (!err) { |
| q->rpm_status = RPM_ACTIVE; |