Fix NULL pointer dereference in sd_revalidate_disk
Subsystems (3)
PATCH 1/1 Fix NULL pointer dereference in sd_revalidate_disk
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.
--
block/partition-generic.c | 48 ++++++++++++++++++++++++++++++++++++++-------- fs/block_dev.c | 16 +++++++++++---- include/linux/genhd.h | 1 3 files changed, 53 insertions(+), 12 deletions(-)
include/linux/genhd.h +1 -0
@@ -596,6 +596,7 @@ extern char *disk_name (struct gendisk *hd, int partno, char *buf);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,
fs/block_dev.c +12 -4
@@ -1183,8 +1183,12 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)* 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 {
@@ -1214,8 +1218,12 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)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; }
block/partition-generic.c +40 -8
69 lines not on this page · raw message
Quoted 21 lines · Jun'ichi Nomura
Since 2.6.39 (1196f8b), when a driver returns
-ENOMEDIUMforopen(),__blkdev_get()callsrescan_partitions()to remove in-kernel partition structures and raiseKOBJ_CHANGEuevent.However it ends up calling driver's
revalidate_diskwithout 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_devicetorn down>
rescan_partitions
sd_revalidate_disk
<oops>
Quoted 8 lines
Oopses are reported here:
http://marc.info/?l=linux-scsi&m=132388619710052This patch separates the partition invalidation from
rescan_partitions()and use it for-ENOMEDIUMcase.Reported-by Huajun Li <[email protected]>Signed-off-by Jun'ichi Nomura <[email protected]>
Thank you!
Signature · 1 line
tejun
Should this need pick up into stable too?
Jack
Re: [PATCH] Fix NULL pointer dereference in sd_revalidate_disk
Quoted 42 lines · Jun'ichi Nomura
Quoted 21 lines
Since 2.6.39 (1196f8b), when a driver returns
-ENOMEDIUMforopen(),__blkdev_get()callsrescan_partitions()to remove in-kernel partition structures and raiseKOBJ_CHANGEuevent.However it ends up calling driver's
revalidate_diskwithout 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_devicetorn down>
rescan_partitions
sd_revalidate_disk
<oops>Quoted 8 lines
Oopses are reported here:
http://marc.info/?l=linux-scsi&m=132388619710052This patch separates the partition invalidation from
rescan_partitions()and use it for-ENOMEDIUMcase.Reported-by Huajun Li <[email protected]>Signed-off-by Jun'ichi Nomura <[email protected]>Acked-by Tejun Heo <[email protected]>Thank you!
--
tejun
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 3 lines · Jack Wang
Should this need pick up into stable too?
Jack
The 3.2 backport seems fairly trivial (just some stuff moved to different files) Here's the patch I did for Fedora.
Dave
fs/partitions/check.c renamed from fs/partitions/check.c~ +40 -8
@@ -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;
include/linux/genhd.h renamed from include/linux/genhd.h~ +1 -0
@@ -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,
fs/block_dev.c renamed from fs/block_dev.c~ +12 -4
@@ -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_devif (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; }
Naveen Goswamy
Tested-byI have tested this patch, and it fixes the problem where when I ejected the SD card it would oops and crash and burn my kernel, as reported here :
https://lkml.org/lkml/2012/2/7/462
and here:
https://bugs.gentoo.org/show_bug.cgi?id=402433
I tested it with the 3.3-rc5 sources, found here:
http://www.kernel.org/pub/linux/kernel/v3.0/testing/
Thanks,
I have tested Dave Jones' 3.2 backport patch in gentoo, against kernel sources 3.2.1-gentoo-r2 and it works as well.
Regards,
Naveen
Quoting 'Dave Jones' <[email protected]>:
Quoted 131 lines · Jack Wang
Quoted 3 lines
Should this need pick up into stable too?
Jack
The 3.2 backport seems fairly trivial (just some stuff moved to different files)
Here's the patch I did for Fedora.Dave
fs/partitions/check.c renamed from fs/partitions/check.c~ +40
-8@@ -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) {fs/partitions/check.c renamed from fs/partitions/check.c~ +0
-0struct address_space *mapping = bdev->bd_inode->i_mapping;include/linux/genhd.h renamed from include/linux/genhd.h~ +1
-0@@ -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,include/linux/genhd.h renamed from include/linux/genhd.h~ +0
-0int partno, sector_t start, sector_t len, int flags,fs/block_dev.c renamed from fs/block_dev.c~ +12
-4@@ -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_devif (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; }