On 4 Apr 2004, James Bottomley wrote:

> On Sun, 2004-04-04 at 12:46, Alan Stern wrote:
> > Ah, you have left out the third, bad alternative: open succeeds, user gets 
> > an fd that points to a deallocated device.  More details below...
> 
> No, that would be a bug.

Of course it would!  That's exactly what this thread is about: bugs caused
by improper handling of open/disconnect races.

>  I'm looking for evidence that such a bug exists
> in sd.

Put up or shut up, eh?  Okay...

> This is what's wrong.  Here you should get a reference to the device
> pointer (that's what scsi_device_get() actually does for us).

That's too vague; I can't tell what you're referring to.  In sd.c 
there's the struct scsi_device, the struct scsi_disk, the struct gendisk, 
and their embedded struct devices and struct kobjects.  Maybe you mean 
scsi_disk_get(), the one place in sd.c that calls scsi_device_get().  I 
would say that it acquires a reference to the device, not to the device 
pointer (whatever that might mean).  But let's not dwell on this point.

>  If the
> ref getting routine comes back with an error, you may not proceed.  If
> it comes back with a device, you own a reference to it and may go on
> (even if the device is now gone, the structure will behave correctly).

What if the ref getting routine causes an oops because the device has been 
deallocated?  I know, you'll say that should never happen...  See below.


> You're not obeying the object lifetime rules here.  The device may be
> gone but the device structure must stay around (obviously in a special
> state) until the last reference is dropped.  Only after that may you
> start nulling things out and killing it.

In the example I gave, the device structure _did_ stay around until the 
last reference was dropped.  The problem was that _another_ reference was 
in the middle of being acquired at the time.

> If anyone can find a similar bug in the SCSI ULD's, I'll fix it, that's
> why I asked for an example in sd (or sr with the current fixes) way back
> in this thread.

All right, let's look at sd.c.  I'll show you that _it_ doesn't obey the 
object lifetime rules.  In sd_open we see this code (lightly edited):


        static int sd_open(struct inode *inode, struct file *filp)
        {
                struct gendisk *disk = inode->i_bdev->bd_disk;
                struct scsi_disk *sdkp = scsi_disk(disk);
                struct scsi_device *sdev;
                int retval;

                retval = scsi_disk_get(sdkp);
                if (retval)
                        return retval;

                sdev = sdkp->device;


As it turns out, the block layer guarantees that when sd_open runs the
bd_disk pointer will be valid.  It does this by following the pattern I
mentioned in an earlier message -- drivers/base/map.c uses a
subsystem-wide semaphore, domain_sem, to properly synchronize lookups and
deletes.

Next, the scsi_disk inline function returns:

        container_of(disk->private_data, struct scsi_disk, driver);

How do you know that the scsi_disk pointed to by disk->private_data still 
exists?  So far as I can see, the gendisk doesn't take any references to 
it.  Correct me if I'm wrong, but there doesn't seem to be anything 
preventing a disconnect event from arriving after the open() call has got 
a valid reference to the gendisk, and succeeding in deallocating the 
scsi_disk before this code executes.  There's only one reference between 
the scsi_disk and the gendisk, and it goes the wrong way: the scsi_disk 
owns a reference to the gendisk.

But let's suppose that works okay, so sdkp is a valid pointer.  Then 
the code calls scsi_disk_get(), which in turn calls scsi_device_get() for 
sdkp->device.  How do you know that this doesn't point to deallocated 
storage?  The only reference to the scsi_device is taken (in a rather 
convoluted way) by the gendisk, and it is dropped during del_gendisk() -- 
not when the gendisk is released.  Hence it is entirely possible for a 
disconnect event to have freed the scsi_device when this code executes.


There's two potential oopses for you.  I don't have a full grasp of the
web of interlocking references (and interlocking code) in the SCSI,
gendisk, and block layers, but it seems likely that at least one of
these might actually happen.

The object lifetime rules require that in your disconnect() routine, you
must tell all your users that your structure is going away, but you must
not free the structure until your users have notified you that they won't
try to use it any more.  When the scsi_disk is on its way out, sd.c tells
the gendisk but doesn't wait for a notification in return.  When the
scsi_device is on its way out the SCSI core tells sd.c, but sd.c doesn't
send back its notification at the right time.

Alan Stern



-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to