aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJun'ichi Nomura2012-03-02 03:38:33 -0600
committerJens Axboe2012-03-02 03:38:33 -0600
commitfe316bf2d5847bc5dd975668671a7b1067603bc7 (patch)
tree617f6a89b96a8c074d0a2fc1ef4ffc259fb4ed2f /block/partition-generic.c
parent621032ad6eaabf2fe771c4fa0d8f58e1fcfcdba6 (diff)
downloadkernel-common-fe316bf2d5847bc5dd975668671a7b1067603bc7.tar.gz
kernel-common-fe316bf2d5847bc5dd975668671a7b1067603bc7.tar.xz
kernel-common-fe316bf2d5847bc5dd975668671a7b1067603bc7.zip
block: 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. 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> Cc: stable@kernel.org Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block/partition-generic.c')
-rw-r--r--block/partition-generic.c48
1 files changed, 40 insertions, 8 deletions
diff --git a/block/partition-generic.c b/block/partition-generic.c
index d06ec1c829c..6df5d6928a4 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -389,17 +389,11 @@ static bool disk_unlock_native_capacity(struct gendisk *disk)
389 } 389 }
390} 390}
391 391
392int rescan_partitions(struct gendisk *disk, struct block_device *bdev) 392static int drop_partitions(struct gendisk *disk, struct block_device *bdev)
393{ 393{
394 struct parsed_partitions *state = NULL;
395 struct disk_part_iter piter; 394 struct disk_part_iter piter;
396 struct hd_struct *part; 395 struct hd_struct *part;
397 int p, highest, res; 396 int res;
398rescan:
399 if (state && !IS_ERR(state)) {
400 kfree(state);
401 state = NULL;
402 }
403 397
404 if (bdev->bd_part_count) 398 if (bdev->bd_part_count)
405 return -EBUSY; 399 return -EBUSY;
@@ -412,6 +406,24 @@ rescan:
412 delete_partition(disk, part->partno); 406 delete_partition(disk, part->partno);
413 disk_part_iter_exit(&piter); 407 disk_part_iter_exit(&piter);
414 408
409 return 0;
410}
411
412int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
413{
414 struct parsed_partitions *state = NULL;
415 struct hd_struct *part;
416 int p, highest, res;
417rescan:
418 if (state && !IS_ERR(state)) {
419 kfree(state);
420 state = NULL;
421 }
422
423 res = drop_partitions(disk, bdev);
424 if (res)
425 return res;
426
415 if (disk->fops->revalidate_disk) 427 if (disk->fops->revalidate_disk)
416 disk->fops->revalidate_disk(disk); 428 disk->fops->revalidate_disk(disk);
417 check_disk_size_change(disk, bdev); 429 check_disk_size_change(disk, bdev);
@@ -515,6 +527,26 @@ rescan:
515 return 0; 527 return 0;
516} 528}
517 529
530int invalidate_partitions(struct gendisk *disk, struct block_device *bdev)
531{
532 int res;
533
534 if (!bdev->bd_invalidated)
535 return 0;
536
537 res = drop_partitions(disk, bdev);
538 if (res)
539 return res;
540
541 set_capacity(disk, 0);
542 check_disk_size_change(disk, bdev);
543 bdev->bd_invalidated = 0;
544 /* tell userspace that the media / partition table may have changed */
545 kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
546
547 return 0;
548}
549
518unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p) 550unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p)
519{ 551{
520 struct address_space *mapping = bdev->bd_inode->i_mapping; 552 struct address_space *mapping = bdev->bd_inode->i_mapping;