public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
@ 2011-11-24 14:30 Huajun Li
  2011-11-26  9:51 ` Stefan Richter
  0 siblings, 1 reply; 9+ messages in thread
From: Huajun Li @ 2011-11-24 14:30 UTC (permalink / raw)
  To: JBottomley, linux-scsi; +Cc: Huajun Li

  While unplugging usb disk, scsi_disk(disk)->device  may be released
before sd_revalidate_disk() is called, then there will occur Oops as
shown below:

  [  285.988055] BUG: unable to handle kernel NULL pointer dereference
at 0000000000000008
  [  285.988112] IP: [<ffffffffa0111bb9>]
sd_revalidate_disk+0x49/0x23b0 [sd_mod]
  [  285.988158] PGD 60ea1067 PUD 6442c067 PMD 0
  [  285.988196] Oops: 0000 [#1] SMP
  [  285.988226] CPU 0
  [  285.988239] Modules linked in: usb_storage usb_libusual uas
bluetooth dm_crypt snd_hda_codec_analog snd_hda_intel snd_hda_codec
snd_hwdep
  [  285.988329] PM: Removing info for scsi:host16
  [  285.988361]  snd_pcm snd_seq_midi snd_rawmidi hp_wmi
snd_seq_midi_event snd_seq sparse_keymap ppdev snd_timer i915
snd_seq_device binfmt_misc snd psmouse serio_raw soundcore
snd_page_alloc tpm_infineon tpm_tis drm_kms_helper
  [  285.988518] bus: 'scsi': remove device host16
  [  285.988549]  tpm parport_pc tpm_bios drm i2c_algo_bit video lp
parport usbhid hid sg sr_mod sd_mod floppy uhci_hcd ehci_hcd usbcore
e1000e usb_common
  [  285.988682]
  [  285.990007] Pid: 2890, comm: blkid Tainted: G          I
3.2.0-rc3+ #1 Hewlett-Packard HP Compaq dc7800p Convertible
Minitower/0AACh
  [  285.990007] RIP: 0010:[<ffffffffa0111bb9>]  [<ffffffffa0111bb9>]
sd_revalidate_disk+0x49/0x23b0 [sd_mod]
  [  285.990007] RSP: 0018:ffff880060ebfa48  EFLAGS: 00010206
  [  285.990007] RAX: 0000000000000000 RBX: 0000000000000000 RCX:
000000005ab95ab8
  [  285.990007] RDX: 0000000000000000 RSI: 0000000000000001 RDI:
0000000000000202
  [  285.990007] RBP: ffff880060ebfb08 R08: 0000000000000002 R09:
0000000000000000
  [  285.990007] R10: 0000000000000000 R11: 0000000000000000 R12:
ffff88006e2f37b0
  [  285.999544] R13: ffff88006e2f37b0 R14: ffff8800022032d8 R15:
ffff88006e2f37b0
  [  285.999544] FS:  00007f71eab70760(0000) GS:ffff88007a200000(0000)
knlGS:0000000000000000
  [  285.999544] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
  [  285.999544] CR2: 0000000000000008 CR3: 0000000064c19000 CR4:
00000000000006f0
  [  285.999544] DR0: 0000000000000000 DR1: 0000000000000000 DR2:
0000000000000000
  [  285.999544] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7:
0000000000000400
  [  285.999544] Process blkid (pid: 2890, threadinfo
ffff880060ebe000, task ffff88005bbdc800)
  [  285.999544] Stack:
  [  285.999544]  0000000000000000 ffffffff81490c80 ffffffff00000000
ffff8800022032c0
  [  285.999544]  ffff880060ebfab8 0000000000000206 ffffffff81e311e0
0000000000000206
  [  285.999544]  ffffffff81e311e0 ffff880060ebfb40 0000000000000000
0000000000000002
  [  285.999544] Call Trace:
  [  285.999544]  [<ffffffff81490c80>] ? disk_part_iter_next+0x360/0x360
  [  285.999544]  [<ffffffff81490ae0>] ? disk_part_iter_next+0x1c0/0x360
  [  285.999544]  [<ffffffff8149096b>] ? disk_part_iter_next+0x4b/0x360
  [  285.999544]  [<ffffffff81490c80>] ? disk_part_iter_next+0x360/0x360
  [  285.999544]  [<ffffffff812f73ca>] rescan_partitions+0xfa/0x7b0
  [  285.999544]  [<ffffffff812a4f06>] __blkdev_get+0x436/0x690
  [  285.999544]  [<ffffffff812a51c3>] blkdev_get+0x63/0x590
  [  285.999544]  [<ffffffff814c77f0>] ? do_raw_spin_unlock+0x70/0x110
  [  285.999544]  [<ffffffff8192a3c3>] ? _raw_spin_unlock+0x43/0x60
  [  285.999544]  [<ffffffff812a5784>] blkdev_open+0x94/0xd0
  [  285.999544]  [<ffffffff8124a044>] __dentry_open+0x3f4/0x630
  [  285.999544]  [<ffffffff814c77f0>] ? do_raw_spin_unlock+0x70/0x110
  [  285.999544]  [<ffffffff812a56f0>] ? blkdev_get+0x590/0x590
  [  285.999544]  [<ffffffff8124c0a4>] nameidata_to_filp+0x94/0xb0
  [  285.999544]  [<ffffffff812639a8>] do_last+0x3e8/0xe70
  [  285.999544]  [<ffffffff81267183>] path_openat+0x103/0x5c0
  [  285.999544]  [<ffffffff812677ca>] do_filp_open+0x4a/0xd0
  [  285.999544]  [<ffffffff8192a3c3>] ? _raw_spin_unlock+0x43/0x60
  [  285.999544]  [<ffffffff8127c5e2>] ? alloc_fd+0x202/0x350
  [  285.999544]  [<ffffffff8124c214>] do_sys_open+0x154/0x280
  [  285.999544]  [<ffffffff8124c368>] sys_open+0x28/0x40
  [  285.999544]  [<ffffffff81937202>] system_call_fastpath+0x16/0x1b
  [  285.999544] Code: 00 00 48 83 05 80 84 00 00 01 65 48 8b 04 25 28
00 00 00 48 89 45 c8 31 c0 49 89 fd 48 85 db 0f 84 7a 20 00 00 8b 05
87 85 45 e3 <4c> 8b 63 08 c1 e8 15 83 e0 07 83 f8 03 0f 87 1b 20 00 00
41 8b
  [  286.051169] RIP  [<ffffffffa0111bb9>]
sd_revalidate_disk+0x49/0x23b0 [sd_mod]
  [  286.051169]  RSP <ffff880060ebfa48>
  [  286.051169] CR2: 0000000000000008


Signed-off-by: Huajun Li <huajun.li.lee@gmail.com>
---
 drivers/scsi/sd.c |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index fa3a591..06d874d 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2354,10 +2354,15 @@ static int sd_try_extended_inquiry(struct
scsi_device *sdp)
 static int sd_revalidate_disk(struct gendisk *disk)
 {
 	struct scsi_disk *sdkp = scsi_disk(disk);
-	struct scsi_device *sdp = sdkp->device;
+	struct scsi_device *sdp;
 	unsigned char *buffer;
 	unsigned flush = 0;

+	if (sdkp)
+		sdp = sdkp->device;
+	else
+		goto out;
+
 	SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
 				      "sd_revalidate_disk\n"));

-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
  2011-11-24 14:30 [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk Huajun Li
@ 2011-11-26  9:51 ` Stefan Richter
  2011-11-26 14:00   ` James Bottomley
  0 siblings, 1 reply; 9+ messages in thread
From: Stefan Richter @ 2011-11-26  9:51 UTC (permalink / raw)
  To: Huajun Li; +Cc: JBottomley, linux-scsi

On Nov 24 Huajun Li wrote:
>   While unplugging usb disk, scsi_disk(disk)->device  may be released
> before sd_revalidate_disk() is called, then there will occur Oops as
> shown below:
[...]
> --- a/drivers/scsi/sd.c
> +++ b/drivers/scsi/sd.c
> @@ -2354,10 +2354,15 @@ static int sd_try_extended_inquiry(struct
> scsi_device *sdp)
>  static int sd_revalidate_disk(struct gendisk *disk)
>  {
>  	struct scsi_disk *sdkp = scsi_disk(disk);
> -	struct scsi_device *sdp = sdkp->device;
> +	struct scsi_device *sdp;
>  	unsigned char *buffer;
>  	unsigned flush = 0;
> 
> +	if (sdkp)
> +		sdp = sdkp->device;
> +	else
> +		goto out;
> +
>  	SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
>  				      "sd_revalidate_disk\n"));
> 

Shouldn't rather the [block -- command-set-driver -- scsi-core -- lld]
stack be structured along the lines that lower-level device instances live
as long as higher levels rely on them?

For about a year now or so, I am seeing patches floating by that add NULL
pointer checks here and there (or patches that clear pointers), and every
time I wonder
  - where else such NULL pointer checks might be needed,
  - in what way (if at all) it is ensured that a function which is made to
    check for a valid pointer at the top does not race with pointer
    invalidation further down the road.
-- 
Stefan Richter
-=====-==-== =-== ==-=-
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
  2011-11-26  9:51 ` Stefan Richter
@ 2011-11-26 14:00   ` James Bottomley
  2011-11-27  1:28     ` Huajun Li
  0 siblings, 1 reply; 9+ messages in thread
From: James Bottomley @ 2011-11-26 14:00 UTC (permalink / raw)
  To: Stefan Richter; +Cc: Huajun Li, linux-scsi@vger.kernel.org

On Sat, 2011-11-26 at 10:51 +0100, Stefan Richter wrote:
> On Nov 24 Huajun Li wrote:
> >   While unplugging usb disk, scsi_disk(disk)->device  may be released
> > before sd_revalidate_disk() is called, then there will occur Oops as
> > shown below:
> [...]
> > --- a/drivers/scsi/sd.c
> > +++ b/drivers/scsi/sd.c
> > @@ -2354,10 +2354,15 @@ static int sd_try_extended_inquiry(struct
> > scsi_device *sdp)
> >  static int sd_revalidate_disk(struct gendisk *disk)
> >  {
> >  	struct scsi_disk *sdkp = scsi_disk(disk);
> > -	struct scsi_device *sdp = sdkp->device;
> > +	struct scsi_device *sdp;
> >  	unsigned char *buffer;
> >  	unsigned flush = 0;
> > 
> > +	if (sdkp)
> > +		sdp = sdkp->device;
> > +	else
> > +		goto out;
> > +
> >  	SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
> >  				      "sd_revalidate_disk\n"));
> > 
> 
> Shouldn't rather the [block -- command-set-driver -- scsi-core -- lld]
> stack be structured along the lines that lower-level device instances live
> as long as higher levels rely on them?

Not really, no.  The problem is the lifetime rules are complex.  The
lowest level objects actually live the longest because they're first to
be discovered before we know what higher level functions to attach.

That's why you see complex paired gets in things like scsi_disk_get
because we try to get references both to the sdkp and the underlying
sdp ... so the sdkp can be torn down by last reference release from
either above or below (this is the menace of hot unplug).

> For about a year now or so, I am seeing patches floating by that add NULL
> pointer checks here and there (or patches that clear pointers), and every
> time I wonder
>   - where else such NULL pointer checks might be needed,
>   - in what way (if at all) it is ensured that a function which is made to
>     check for a valid pointer at the top does not race with pointer
>     invalidation further down the road.

I agree.  The patch is clearly wrong because sdkp is a refcounted object
that never actually sets sdkp->device to NULL.  If we find a NULL in
there it must be because the sdkp object is wrong.  The implication from
the trace seems to be that something allowed blkid to open a non
existent device.

James


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
  2011-11-26 14:00   ` James Bottomley
@ 2011-11-27  1:28     ` Huajun Li
  2011-11-27  2:20       ` Huajun Li
  0 siblings, 1 reply; 9+ messages in thread
From: Huajun Li @ 2011-11-27  1:28 UTC (permalink / raw)
  To: James Bottomley, Stefan Richter; +Cc: linux-scsi@vger.kernel.org, Huajun Li

在 2011年11月26日 下午10:00,James Bottomley <jbottomley@parallels.com> 写道:
> On Sat, 2011-11-26 at 10:51 +0100, Stefan Richter wrote:
>> On Nov 24 Huajun Li wrote:
>> >   While unplugging usb disk, scsi_disk(disk)->device  may be released
>> > before sd_revalidate_disk() is called, then there will occur Oops as
>> > shown below:
>> [...]
>> > --- a/drivers/scsi/sd.c
>> > +++ b/drivers/scsi/sd.c
>> > @@ -2354,10 +2354,15 @@ static int sd_try_extended_inquiry(struct
>> > scsi_device *sdp)
>> >  static int sd_revalidate_disk(struct gendisk *disk)
>> >  {
>> >     struct scsi_disk *sdkp = scsi_disk(disk);
>> > -   struct scsi_device *sdp = sdkp->device;
>> > +   struct scsi_device *sdp;
>> >     unsigned char *buffer;
>> >     unsigned flush = 0;
>> >
>> > +   if (sdkp)
>> > +           sdp = sdkp->device;
>> > +   else
>> > +           goto out;
>> > +
>> >     SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
>> >                                   "sd_revalidate_disk\n"));
>> >
>>
>> Shouldn't rather the [block -- command-set-driver -- scsi-core -- lld]
>> stack be structured along the lines that lower-level device instances live
>> as long as higher levels rely on them?
>
> Not really, no.  The problem is the lifetime rules are complex.  The
> lowest level objects actually live the longest because they're first to
> be discovered before we know what higher level functions to attach.
>
> That's why you see complex paired gets in things like scsi_disk_get
> because we try to get references both to the sdkp and the underlying
> sdp ... so the sdkp can be torn down by last reference release from
> either above or below (this is the menace of hot unplug).
>
>> For about a year now or so, I am seeing patches floating by that add NULL
>> pointer checks here and there (or patches that clear pointers), and every
>> time I wonder
>>   - where else such NULL pointer checks might be needed,
>>   - in what way (if at all) it is ensured that a function which is made to
>>     check for a valid pointer at the top does not race with pointer
>>     invalidation further down the road.
>
> I agree.  The patch is clearly wrong because sdkp is a refcounted object
> that never actually sets sdkp->device to NULL.  If we find a NULL in
> there it must be because the sdkp object is wrong.  The implication from
> the trace seems to be that something allowed blkid to open a non
> existent device.
>
> James
>
>

Thanks for your response.

Yes, in this case, actually sdkp is NULL rather than sdkp->device, and
the patch indicates this.
However, there is typo in my comments, maybe it misleads you, sorry!
In fact, the comment should be:

" While unplugging usb disk, scsi_disk(disk)  may be released
 before sd_revalidate_disk() is called, then there will occur Oops."
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
  2011-11-27  1:28     ` Huajun Li
@ 2011-11-27  2:20       ` Huajun Li
  2011-11-27  2:38         ` James Bottomley
  0 siblings, 1 reply; 9+ messages in thread
From: Huajun Li @ 2011-11-27  2:20 UTC (permalink / raw)
  To: James Bottomley, Stefan Richter; +Cc: linux-scsi@vger.kernel.org, Huajun Li

在 2011年11月27日 上午9:28,Huajun Li <huajun.li.lee@gmail.com> 写道:
> 在 2011年11月26日 下午10:00,James Bottomley <jbottomley@parallels.com> 写道:
>> On Sat, 2011-11-26 at 10:51 +0100, Stefan Richter wrote:
>>> On Nov 24 Huajun Li wrote:
>>> >   While unplugging usb disk, scsi_disk(disk)->device  may be released
>>> > before sd_revalidate_disk() is called, then there will occur Oops as
>>> > shown below:
>>> [...]
>>> > --- a/drivers/scsi/sd.c
>>> > +++ b/drivers/scsi/sd.c
>>> > @@ -2354,10 +2354,15 @@ static int sd_try_extended_inquiry(struct
>>> > scsi_device *sdp)
>>> >  static int sd_revalidate_disk(struct gendisk *disk)
>>> >  {
>>> >     struct scsi_disk *sdkp = scsi_disk(disk);
>>> > -   struct scsi_device *sdp = sdkp->device;
>>> > +   struct scsi_device *sdp;
>>> >     unsigned char *buffer;
>>> >     unsigned flush = 0;
>>> >
>>> > +   if (sdkp)
>>> > +           sdp = sdkp->device;
>>> > +   else
>>> > +           goto out;
>>> > +
>>> >     SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
>>> >                                   "sd_revalidate_disk\n"));
>>> >
>>>
>>> Shouldn't rather the [block -- command-set-driver -- scsi-core -- lld]
>>> stack be structured along the lines that lower-level device instances live
>>> as long as higher levels rely on them?
>>
>> Not really, no.  The problem is the lifetime rules are complex.  The
>> lowest level objects actually live the longest because they're first to
>> be discovered before we know what higher level functions to attach.
>>
>> That's why you see complex paired gets in things like scsi_disk_get
>> because we try to get references both to the sdkp and the underlying
>> sdp ... so the sdkp can be torn down by last reference release from
>> either above or below (this is the menace of hot unplug).
>>
>>> For about a year now or so, I am seeing patches floating by that add NULL
>>> pointer checks here and there (or patches that clear pointers), and every
>>> time I wonder
>>>   - where else such NULL pointer checks might be needed,
>>>   - in what way (if at all) it is ensured that a function which is made to
>>>     check for a valid pointer at the top does not race with pointer
>>>     invalidation further down the road.
>>
>> I agree.  The patch is clearly wrong because sdkp is a refcounted object
>> that never actually sets sdkp->device to NULL.  If we find a NULL in
>> there it must be because the sdkp object is wrong.  The implication from
>> the trace seems to be that something allowed blkid to open a non
>> existent device.
>>
>> James
>>
>>
>
> Thanks for your response.
>
> Yes, in this case, actually sdkp is NULL rather than sdkp->device, and
> the patch indicates this.
> However, there is typo in my comments, maybe it misleads you, sorry!
> In fact, the comment should be:
>
> " While unplugging usb disk, scsi_disk(disk)  may be released
>  before sd_revalidate_disk() is called, then there will occur Oops."
>

BTW, after applied the patch and repeatedly plug/unplug the USB stick,
I did not see this crash.

However, the other concern is, need we validate scsi_disk(disk) in
some other functions specified in sd_fops ?  since they may be also
called from other layer after  scsi_disk(disk) is released.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
  2011-11-27  2:20       ` Huajun Li
@ 2011-11-27  2:38         ` James Bottomley
  2011-11-27  3:22           ` Huajun Li
  2011-11-30 14:43           ` Huajun Li
  0 siblings, 2 replies; 9+ messages in thread
From: James Bottomley @ 2011-11-27  2:38 UTC (permalink / raw)
  To: Huajun Li; +Cc: Stefan Richter, linux-scsi@vger.kernel.org

On Sun, 2011-11-27 at 10:20 +0800, Huajun Li wrote:
> 在 2011年11月27日 上午9:28,Huajun Li <huajun.li.lee@gmail.com> 写道:
> > 在 2011年11月26日 下午10:00,James Bottomley <jbottomley@parallels.com> 写道:
> >> On Sat, 2011-11-26 at 10:51 +0100, Stefan Richter wrote:
> >>> On Nov 24 Huajun Li wrote:
> >>> >   While unplugging usb disk, scsi_disk(disk)->device  may be released
> >>> > before sd_revalidate_disk() is called, then there will occur Oops as
> >>> > shown below:
> >>> [...]
> >>> > --- a/drivers/scsi/sd.c
> >>> > +++ b/drivers/scsi/sd.c
> >>> > @@ -2354,10 +2354,15 @@ static int sd_try_extended_inquiry(struct
> >>> > scsi_device *sdp)
> >>> >  static int sd_revalidate_disk(struct gendisk *disk)
> >>> >  {
> >>> >     struct scsi_disk *sdkp = scsi_disk(disk);
> >>> > -   struct scsi_device *sdp = sdkp->device;
> >>> > +   struct scsi_device *sdp;
> >>> >     unsigned char *buffer;
> >>> >     unsigned flush = 0;
> >>> >
> >>> > +   if (sdkp)
> >>> > +           sdp = sdkp->device;
> >>> > +   else
> >>> > +           goto out;
> >>> > +
> >>> >     SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
> >>> >                                   "sd_revalidate_disk\n"));
> >>> >
> >>>
> >>> Shouldn't rather the [block -- command-set-driver -- scsi-core -- lld]
> >>> stack be structured along the lines that lower-level device instances live
> >>> as long as higher levels rely on them?
> >>
> >> Not really, no.  The problem is the lifetime rules are complex.  The
> >> lowest level objects actually live the longest because they're first to
> >> be discovered before we know what higher level functions to attach.
> >>
> >> That's why you see complex paired gets in things like scsi_disk_get
> >> because we try to get references both to the sdkp and the underlying
> >> sdp ... so the sdkp can be torn down by last reference release from
> >> either above or below (this is the menace of hot unplug).
> >>
> >>> For about a year now or so, I am seeing patches floating by that add NULL
> >>> pointer checks here and there (or patches that clear pointers), and every
> >>> time I wonder
> >>>   - where else such NULL pointer checks might be needed,
> >>>   - in what way (if at all) it is ensured that a function which is made to
> >>>     check for a valid pointer at the top does not race with pointer
> >>>     invalidation further down the road.
> >>
> >> I agree.  The patch is clearly wrong because sdkp is a refcounted object
> >> that never actually sets sdkp->device to NULL.  If we find a NULL in
> >> there it must be because the sdkp object is wrong.  The implication from
> >> the trace seems to be that something allowed blkid to open a non
> >> existent device.
> >>
> >> James
> >>
> >>
> >
> > Thanks for your response.
> >
> > Yes, in this case, actually sdkp is NULL rather than sdkp->device, and
> > the patch indicates this.
> > However, there is typo in my comments, maybe it misleads you, sorry!
> > In fact, the comment should be:

No, it's not that ... it's how did the open get far enough to do the
revalidation?

> > " While unplugging usb disk, scsi_disk(disk)  may be released
> >  before sd_revalidate_disk() is called, then there will occur Oops."
> >
> 
> BTW, after applied the patch and repeatedly plug/unplug the USB stick,
> I did not see this crash.
> 
> However, the other concern is, need we validate scsi_disk(disk) in
> some other functions specified in sd_fops ?  since they may be also
> called from other layer after  scsi_disk(disk) is released.

No, it's nothing to do with that.  To call any of the block operations,
you need to call ->open successfully first.  since ->open either gets a
ref to the sdkp or returns an error, how did we happen to be in
sd_revalidate with a NULL sdkp?

The call trace says we came through rescan_partitions() which only
happens if ->open returns zero (or -ERESTARTSYS).

James


--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
  2011-11-27  2:38         ` James Bottomley
@ 2011-11-27  3:22           ` Huajun Li
  2011-11-30 14:43           ` Huajun Li
  1 sibling, 0 replies; 9+ messages in thread
From: Huajun Li @ 2011-11-27  3:22 UTC (permalink / raw)
  To: James Bottomley; +Cc: Stefan Richter, linux-scsi@vger.kernel.org, Huajun Li

在 2011年11月27日 上午10:38,James Bottomley
<James.Bottomley@hansenpartnership.com> 写道:
> On Sun, 2011-11-27 at 10:20 +0800, Huajun Li wrote:
>> 在 2011年11月27日 上午9:28,Huajun Li <huajun.li.lee@gmail.com> 写道:
>> > 在 2011年11月26日 下午10:00,James Bottomley <jbottomley@parallels.com> 写道:
>> >> On Sat, 2011-11-26 at 10:51 +0100, Stefan Richter wrote:
>> >>> On Nov 24 Huajun Li wrote:
>> >>> >   While unplugging usb disk, scsi_disk(disk)->device  may be released
>> >>> > before sd_revalidate_disk() is called, then there will occur Oops as
>> >>> > shown below:
>> >>> [...]
>> >>> > --- a/drivers/scsi/sd.c
>> >>> > +++ b/drivers/scsi/sd.c
>> >>> > @@ -2354,10 +2354,15 @@ static int sd_try_extended_inquiry(struct
>> >>> > scsi_device *sdp)
>> >>> >  static int sd_revalidate_disk(struct gendisk *disk)
>> >>> >  {
>> >>> >     struct scsi_disk *sdkp = scsi_disk(disk);
>> >>> > -   struct scsi_device *sdp = sdkp->device;
>> >>> > +   struct scsi_device *sdp;
>> >>> >     unsigned char *buffer;
>> >>> >     unsigned flush = 0;
>> >>> >
>> >>> > +   if (sdkp)
>> >>> > +           sdp = sdkp->device;
>> >>> > +   else
>> >>> > +           goto out;
>> >>> > +
>> >>> >     SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
>> >>> >                                   "sd_revalidate_disk\n"));
>> >>> >
>> >>>
>> >>> Shouldn't rather the [block -- command-set-driver -- scsi-core -- lld]
>> >>> stack be structured along the lines that lower-level device instances live
>> >>> as long as higher levels rely on them?
>> >>
>> >> Not really, no.  The problem is the lifetime rules are complex.  The
>> >> lowest level objects actually live the longest because they're first to
>> >> be discovered before we know what higher level functions to attach.
>> >>
>> >> That's why you see complex paired gets in things like scsi_disk_get
>> >> because we try to get references both to the sdkp and the underlying
>> >> sdp ... so the sdkp can be torn down by last reference release from
>> >> either above or below (this is the menace of hot unplug).
>> >>
>> >>> For about a year now or so, I am seeing patches floating by that add NULL
>> >>> pointer checks here and there (or patches that clear pointers), and every
>> >>> time I wonder
>> >>>   - where else such NULL pointer checks might be needed,
>> >>>   - in what way (if at all) it is ensured that a function which is made to
>> >>>     check for a valid pointer at the top does not race with pointer
>> >>>     invalidation further down the road.
>> >>
>> >> I agree.  The patch is clearly wrong because sdkp is a refcounted object
>> >> that never actually sets sdkp->device to NULL.  If we find a NULL in
>> >> there it must be because the sdkp object is wrong.  The implication from
>> >> the trace seems to be that something allowed blkid to open a non
>> >> existent device.
>> >>
>> >> James
>> >>
>> >>
>> >
>> > Thanks for your response.
>> >
>> > Yes, in this case, actually sdkp is NULL rather than sdkp->device, and
>> > the patch indicates this.
>> > However, there is typo in my comments, maybe it misleads you, sorry!
>> > In fact, the comment should be:
>
> No, it's not that ... it's how did the open get far enough to do the
> revalidation?
>

USB disconnection can happen at any time, even after an open
successfully get called, then revalidation will be triggered ?

To reproduce the crash, running a script like below, at the same time,
plug/unplug USB repeatedly, then easily reproduce it.
------------------------------------------------
while [ 1 ]
do
        fdisk -l /dev/sdb     # <------ The USB will be shown as
/dev/sdb on my host
        sleep 0.05
done

>> > " While unplugging usb disk, scsi_disk(disk)  may be released
>> >  before sd_revalidate_disk() is called, then there will occur Oops."
>> >
>>
>> BTW, after applied the patch and repeatedly plug/unplug the USB stick,
>> I did not see this crash.
>>
>> However, the other concern is, need we validate scsi_disk(disk) in
>> some other functions specified in sd_fops ?  since they may be also
>> called from other layer after  scsi_disk(disk) is released.
>
> No, it's nothing to do with that.  To call any of the block operations,
> you need to call ->open successfully first.  since ->open either gets a
> ref to the sdkp or returns an error, how did we happen to be in
> sd_revalidate with a NULL sdkp?
>
> The call trace says we came through rescan_partitions() which only
> happens if ->open returns zero (or -ERESTARTSYS).
>
> James
>

If a USB disk is removed, then scsi_disk_release()  will be called,
and it will release sdkp, right?
and after this point, if sd_revalidate is triggered from other layer
then it will find sdkp is freed, does this scenario make sense ?

2695 static void scsi_disk_release(struct device *dev)
2696 {
2697         struct scsi_disk *sdkp = to_scsi_disk(dev);
2698         struct gendisk *disk = sdkp->disk;
2699
2700         spin_lock(&sd_index_lock);
2701         ida_remove(&sd_index_ida, sdkp->index);
2702         spin_unlock(&sd_index_lock);
2703
2704         disk->private_data = NULL;
2705         put_disk(disk);
2706         put_device(&sdkp->device->sdev_gendev);
2707
2708         kfree(sdkp);        <==============
2709 }
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
  2011-11-27  2:38         ` James Bottomley
  2011-11-27  3:22           ` Huajun Li
@ 2011-11-30 14:43           ` Huajun Li
  2011-11-30 14:50             ` Huajun Li
  1 sibling, 1 reply; 9+ messages in thread
From: Huajun Li @ 2011-11-30 14:43 UTC (permalink / raw)
  To: James Bottomley; +Cc: Stefan Richter, linux-scsi@vger.kernel.org, Huajun Li

在 2011年11月27日 上午10:38,James Bottomley
<James.Bottomley@hansenpartnership.com> 写道:
> On Sun, 2011-11-27 at 10:20 +0800, Huajun Li wrote:
>> 在 2011年11月27日 上午9:28,Huajun Li <huajun.li.lee@gmail.com> 写道:
>> > 在 2011年11月26日 下午10:00,James Bottomley <jbottomley@parallels.com> 写道:
>> >> On Sat, 2011-11-26 at 10:51 +0100, Stefan Richter wrote:
>> >>> On Nov 24 Huajun Li wrote:
>> >>> >   While unplugging usb disk, scsi_disk(disk)->device  may be released
>> >>> > before sd_revalidate_disk() is called, then there will occur Oops as
>> >>> > shown below:
>> >>> [...]
>> >>> > --- a/drivers/scsi/sd.c
>> >>> > +++ b/drivers/scsi/sd.c
>> >>> > @@ -2354,10 +2354,15 @@ static int sd_try_extended_inquiry(struct
>> >>> > scsi_device *sdp)
>> >>> >  static int sd_revalidate_disk(struct gendisk *disk)
>> >>> >  {
>> >>> >     struct scsi_disk *sdkp = scsi_disk(disk);
>> >>> > -   struct scsi_device *sdp = sdkp->device;
>> >>> > +   struct scsi_device *sdp;
>> >>> >     unsigned char *buffer;
>> >>> >     unsigned flush = 0;
>> >>> >
>> >>> > +   if (sdkp)
>> >>> > +           sdp = sdkp->device;
>> >>> > +   else
>> >>> > +           goto out;
>> >>> > +
>> >>> >     SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
>> >>> >                                   "sd_revalidate_disk\n"));
>> >>> >
>> >>>
>> >>> Shouldn't rather the [block -- command-set-driver -- scsi-core -- lld]
>> >>> stack be structured along the lines that lower-level device instances live
>> >>> as long as higher levels rely on them?
>> >>
>> >> Not really, no.  The problem is the lifetime rules are complex.  The
>> >> lowest level objects actually live the longest because they're first to
>> >> be discovered before we know what higher level functions to attach.
>> >>
>> >> That's why you see complex paired gets in things like scsi_disk_get
>> >> because we try to get references both to the sdkp and the underlying
>> >> sdp ... so the sdkp can be torn down by last reference release from
>> >> either above or below (this is the menace of hot unplug).
>> >>
>> >>> For about a year now or so, I am seeing patches floating by that add NULL
>> >>> pointer checks here and there (or patches that clear pointers), and every
>> >>> time I wonder
>> >>>   - where else such NULL pointer checks might be needed,
>> >>>   - in what way (if at all) it is ensured that a function which is made to
>> >>>     check for a valid pointer at the top does not race with pointer
>> >>>     invalidation further down the road.
>> >>
>> >> I agree.  The patch is clearly wrong because sdkp is a refcounted object
>> >> that never actually sets sdkp->device to NULL.  If we find a NULL in
>> >> there it must be because the sdkp object is wrong.  The implication from
>> >> the trace seems to be that something allowed blkid to open a non
>> >> existent device.
>> >>
>> >> James
>> >>
>> >>
>> >
>> > Thanks for your response.
>> >
>> > Yes, in this case, actually sdkp is NULL rather than sdkp->device, and
>> > the patch indicates this.
>> > However, there is typo in my comments, maybe it misleads you, sorry!
>> > In fact, the comment should be:
>
> No, it's not that ... it's how did the open get far enough to do the
> revalidation?
>
>> > " While unplugging usb disk, scsi_disk(disk)  may be released
>> >  before sd_revalidate_disk() is called, then there will occur Oops."
>> >
>>
>> BTW, after applied the patch and repeatedly plug/unplug the USB stick,
>> I did not see this crash.
>>
>> However, the other concern is, need we validate scsi_disk(disk) in
>> some other functions specified in sd_fops ?  since they may be also
>> called from other layer after  scsi_disk(disk) is released.
>
> No, it's nothing to do with that.  To call any of the block operations,
> you need to call ->open successfully first.  since ->open either gets a
> ref to the sdkp or returns an error, how did we happen to be in
> sd_revalidate with a NULL sdkp?
>

Hi James,
Thanks for your hints.

Here is a case may cause sd_revalidate_disk() triggered with a NULL sdkp:
In sd_open(), if the removable medium does not present, it will call
scsi_disk_put(sdkp) and return -ENOMEDIUM,  then scsi_disk_put() may
cause scsi_disk_release() called and sdkp get freed.

In consequence, while __blkdev_get()  calling disk->fops->open(), it
will trigger rescan_partitions() if  ->open returns -ENOMEDIUM,
finally sd_revalidate_disk() will be called with a NULL sdkp.

BTW, I tried above path, it really could happen on my host. :)

> The call trace says we came through rescan_partitions() which only
> happens if ->open returns zero (or -ERESTARTSYS).
>

Not exactly. It happens if  ->open returns zero or -ENOMEDIUM, please
see this commit:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=1196f8b814f32cd04df334abf47648c2a9fd8324


Thanks,
--Huajun
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk
  2011-11-30 14:43           ` Huajun Li
@ 2011-11-30 14:50             ` Huajun Li
  0 siblings, 0 replies; 9+ messages in thread
From: Huajun Li @ 2011-11-30 14:50 UTC (permalink / raw)
  To: James Bottomley; +Cc: Stefan Richter, linux-scsi@vger.kernel.org, Huajun Li

> Not exactly. It happens if  ->open returns zero or -ENOMEDIUM, please
> see this commit:
> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=1196f8b814f32cd04df334abf47648c2a9fd8324
>
Sorry, I should remove my word "Not exxxxx" ..
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2011-11-30 14:50 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-11-24 14:30 [PATCH] SCSI/sd: Fix NULL dereference in sd_revalidate_disk Huajun Li
2011-11-26  9:51 ` Stefan Richter
2011-11-26 14:00   ` James Bottomley
2011-11-27  1:28     ` Huajun Li
2011-11-27  2:20       ` Huajun Li
2011-11-27  2:38         ` James Bottomley
2011-11-27  3:22           ` Huajun Li
2011-11-30 14:43           ` Huajun Li
2011-11-30 14:50             ` Huajun Li

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox