lkml.org 
[lkml]   [2012]   [Mar]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[ 29/41] block: Fix NULL pointer dereference in sd_revalidate_disk
    3.2-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Jun'ichi Nomura <j-nomura@ce.jp.nec.com>

    commit fe316bf2d5847bc5dd975668671a7b1067603bc7 upstream.

    Since 2.6.39 (1196f8b), when a driver returns -ENOMEDIUM for open(),
    __blkdev_get() calls rescan_partitions() to remove
    in-kernel partition structures and raise KOBJ_CHANGE uevent.

    However it ends up calling driver's revalidate_disk without open
    and could cause oops.

    In the case of SCSI:

    process A process B
    ----------------------------------------------
    sys_open
    __blkdev_get
    sd_open
    returns -ENOMEDIUM
    scsi_remove_device
    <scsi_device torn down>
    rescan_partitions
    sd_revalidate_disk
    <oops>
    Oopses are reported here:
    http://marc.info/?l=linux-scsi&m=132388619710052

    This patch separates the partition invalidation from rescan_partitions()
    and use it for -ENOMEDIUM case.

    Reported-by: Huajun Li <huajun.li.lee@gmail.com>
    Signed-off-by: Jun'ichi Nomura <j-nomura@ce.jp.nec.com>
    Acked-by: Tejun Heo <tj@kernel.org>
    Signed-off-by: Jens Axboe <axboe@kernel.dk>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/block_dev.c | 16 ++++++++++++----
    fs/partitions/check.c | 48 ++++++++++++++++++++++++++++++++++++++++--------
    include/linux/genhd.h | 1 +
    3 files changed, 53 insertions(+), 12 deletions(-)

    --- a/fs/block_dev.c
    +++ b/fs/block_dev.c
    @@ -1159,8 +1159,12 @@ static int __blkdev_get(struct block_dev
    * The latter is necessary to prevent ghost
    * partitions on a removed medium.
    */
    - if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM))
    - rescan_partitions(disk, bdev);
    + if (bdev->bd_invalidated) {
    + if (!ret)
    + rescan_partitions(disk, bdev);
    + else if (ret == -ENOMEDIUM)
    + invalidate_partitions(disk, bdev);
    + }
    if (ret)
    goto out_clear;
    } else {
    @@ -1190,8 +1194,12 @@ static int __blkdev_get(struct block_dev
    if (bdev->bd_disk->fops->open)
    ret = bdev->bd_disk->fops->open(bdev, mode);
    /* the same as first opener case, read comment there */
    - if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM))
    - rescan_partitions(bdev->bd_disk, bdev);
    + if (bdev->bd_invalidated) {
    + if (!ret)
    + rescan_partitions(bdev->bd_disk, bdev);
    + else if (ret == -ENOMEDIUM)
    + invalidate_partitions(bdev->bd_disk, bdev);
    + }
    if (ret)
    goto out_unlock_bdev;
    }
    --- a/fs/partitions/check.c
    +++ b/fs/partitions/check.c
    @@ -539,17 +539,11 @@ static bool disk_unlock_native_capacity(
    }
    }

    -int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
    +static int drop_partitions(struct gendisk *disk, struct block_device *bdev)
    {
    - struct parsed_partitions *state = NULL;
    struct disk_part_iter piter;
    struct hd_struct *part;
    - int p, highest, res;
    -rescan:
    - if (state && !IS_ERR(state)) {
    - kfree(state);
    - state = NULL;
    - }
    + int res;

    if (bdev->bd_part_count)
    return -EBUSY;
    @@ -562,6 +556,24 @@ rescan:
    delete_partition(disk, part->partno);
    disk_part_iter_exit(&piter);

    + return 0;
    +}
    +
    +int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
    +{
    + struct parsed_partitions *state = NULL;
    + struct hd_struct *part;
    + int p, highest, res;
    +rescan:
    + if (state && !IS_ERR(state)) {
    + kfree(state);
    + state = NULL;
    + }
    +
    + res = drop_partitions(disk, bdev);
    + if (res)
    + return res;
    +
    if (disk->fops->revalidate_disk)
    disk->fops->revalidate_disk(disk);
    check_disk_size_change(disk, bdev);
    @@ -665,6 +677,26 @@ rescan:
    return 0;
    }

    +int invalidate_partitions(struct gendisk *disk, struct block_device *bdev)
    +{
    + int res;
    +
    + if (!bdev->bd_invalidated)
    + return 0;
    +
    + res = drop_partitions(disk, bdev);
    + if (res)
    + return res;
    +
    + set_capacity(disk, 0);
    + check_disk_size_change(disk, bdev);
    + bdev->bd_invalidated = 0;
    + /* tell userspace that the media / partition table may have changed */
    + kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
    +
    + return 0;
    +}
    +
    unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p)
    {
    struct address_space *mapping = bdev->bd_inode->i_mapping;
    --- a/include/linux/genhd.h
    +++ b/include/linux/genhd.h
    @@ -596,6 +596,7 @@ extern char *disk_name (struct gendisk *

    extern int disk_expand_part_tbl(struct gendisk *disk, int target);
    extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev);
    +extern int invalidate_partitions(struct gendisk *disk, struct block_device *bdev);
    extern struct hd_struct * __must_check add_partition(struct gendisk *disk,
    int partno, sector_t start,
    sector_t len, int flags,



    \
     
     \ /
      Last update: 2012-03-17 00:47    [W:4.480 / U:0.220 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site