Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ubi: block: Fix locking for idr_alloc/idr_remove

This fixes a race with idr_alloc where gd->first_minor can be set to the
same value for two simultaneous calls to ubiblock_create. Each instance
calls device_add_disk with the same first_minor. device_add_disk calls
bdi_register_owner which generates several warnings.

WARNING: CPU: 1 PID: 179 at kernel-source/fs/sysfs/dir.c:31
sysfs_warn_dup+0x68/0x88
sysfs: cannot create duplicate filename '/devices/virtual/bdi/252:2'

WARNING: CPU: 1 PID: 179 at kernel-source/lib/kobject.c:240
kobject_add_internal+0x1ec/0x2f8
kobject_add_internal failed for 252:2 with -EEXIST, don't try to
register things with the same name in the same directory

WARNING: CPU: 1 PID: 179 at kernel-source/fs/sysfs/dir.c:31
sysfs_warn_dup+0x68/0x88
sysfs: cannot create duplicate filename '/dev/block/252:2'

However, device_add_disk does not error out when bdi_register_owner
returns an error. Control continues until reaching blk_register_queue.
It then BUGs.

kernel BUG at kernel-source/fs/sysfs/group.c:113!
[<c01e26cc>] (internal_create_group) from [<c01e2950>]
(sysfs_create_group+0x20/0x24)
[<c01e2950>] (sysfs_create_group) from [<c00e3d38>]
(blk_trace_init_sysfs+0x18/0x20)
[<c00e3d38>] (blk_trace_init_sysfs) from [<c02bdfbc>]
(blk_register_queue+0xd8/0x154)
[<c02bdfbc>] (blk_register_queue) from [<c02cec84>]
(device_add_disk+0x194/0x44c)
[<c02cec84>] (device_add_disk) from [<c0436ec8>]
(ubiblock_create+0x284/0x2e0)
[<c0436ec8>] (ubiblock_create) from [<c0427bb8>]
(vol_cdev_ioctl+0x450/0x554)
[<c0427bb8>] (vol_cdev_ioctl) from [<c0189110>] (vfs_ioctl+0x30/0x44)
[<c0189110>] (vfs_ioctl) from [<c01892e0>] (do_vfs_ioctl+0xa0/0x790)
[<c01892e0>] (do_vfs_ioctl) from [<c0189a14>] (SyS_ioctl+0x44/0x68)
[<c0189a14>] (SyS_ioctl) from [<c0010640>] (ret_fast_syscall+0x0/0x34)

Locking idr_alloc/idr_remove removes the race and keeps gd->first_minor
unique.

Fixes: 2bf50d42f3a4 ("UBI: block: Dynamically allocate minor numbers")
Cc: stable@vger.kernel.org
Signed-off-by: Bradley Bolen <bradleybolen@gmail.com>
Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Richard Weinberger <richard@nod.at>

authored by

Bradley Bolen and committed by
Richard Weinberger
7f29ae9f 7233982a

+26 -16
+26 -16
drivers/mtd/ubi/block.c
··· 99 99 100 100 /* Linked list of all ubiblock instances */ 101 101 static LIST_HEAD(ubiblock_devices); 102 + static DEFINE_IDR(ubiblock_minor_idr); 103 + /* Protects ubiblock_devices and ubiblock_minor_idr */ 102 104 static DEFINE_MUTEX(devices_mutex); 103 105 static int ubiblock_major; 104 106 ··· 353 351 .init_request = ubiblock_init_request, 354 352 }; 355 353 356 - static DEFINE_IDR(ubiblock_minor_idr); 357 - 358 354 int ubiblock_create(struct ubi_volume_info *vi) 359 355 { 360 356 struct ubiblock *dev; ··· 365 365 /* Check that the volume isn't already handled */ 366 366 mutex_lock(&devices_mutex); 367 367 if (find_dev_nolock(vi->ubi_num, vi->vol_id)) { 368 - mutex_unlock(&devices_mutex); 369 - return -EEXIST; 368 + ret = -EEXIST; 369 + goto out_unlock; 370 370 } 371 - mutex_unlock(&devices_mutex); 372 371 373 372 dev = kzalloc(sizeof(struct ubiblock), GFP_KERNEL); 374 - if (!dev) 375 - return -ENOMEM; 373 + if (!dev) { 374 + ret = -ENOMEM; 375 + goto out_unlock; 376 + } 376 377 377 378 mutex_init(&dev->dev_mutex); 378 379 ··· 438 437 goto out_free_queue; 439 438 } 440 439 441 - mutex_lock(&devices_mutex); 442 440 list_add_tail(&dev->list, &ubiblock_devices); 443 - mutex_unlock(&devices_mutex); 444 441 445 442 /* Must be the last step: anyone can call file ops from now on */ 446 443 add_disk(dev->gd); 447 444 dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", 448 445 dev->ubi_num, dev->vol_id, vi->name); 446 + mutex_unlock(&devices_mutex); 449 447 return 0; 450 448 451 449 out_free_queue: ··· 457 457 put_disk(dev->gd); 458 458 out_free_dev: 459 459 kfree(dev); 460 + out_unlock: 461 + mutex_unlock(&devices_mutex); 460 462 461 463 return ret; 462 464 } ··· 480 478 int ubiblock_remove(struct ubi_volume_info *vi) 481 479 { 482 480 struct ubiblock *dev; 481 + int ret; 483 482 484 483 mutex_lock(&devices_mutex); 485 484 dev = find_dev_nolock(vi->ubi_num, vi->vol_id); 486 485 if (!dev) { 487 - mutex_unlock(&devices_mutex); 488 - return -ENODEV; 486 + ret = -ENODEV; 487 + goto out_unlock; 489 488 } 490 489 491 490 /* Found a device, let's lock it so we can check if it's busy */ 492 491 mutex_lock(&dev->dev_mutex); 493 492 if (dev->refcnt > 0) { 494 - mutex_unlock(&dev->dev_mutex); 495 - mutex_unlock(&devices_mutex); 496 - return -EBUSY; 493 + ret = -EBUSY; 494 + goto out_unlock_dev; 497 495 } 498 496 499 497 /* Remove from device list */ 500 498 list_del(&dev->list); 501 - mutex_unlock(&devices_mutex); 502 - 503 499 ubiblock_cleanup(dev); 504 500 mutex_unlock(&dev->dev_mutex); 501 + mutex_unlock(&devices_mutex); 502 + 505 503 kfree(dev); 506 504 return 0; 505 + 506 + out_unlock_dev: 507 + mutex_unlock(&dev->dev_mutex); 508 + out_unlock: 509 + mutex_unlock(&devices_mutex); 510 + return ret; 507 511 } 508 512 509 513 static int ubiblock_resize(struct ubi_volume_info *vi) ··· 638 630 struct ubiblock *next; 639 631 struct ubiblock *dev; 640 632 633 + mutex_lock(&devices_mutex); 641 634 list_for_each_entry_safe(dev, next, &ubiblock_devices, list) { 642 635 /* The module is being forcefully removed */ 643 636 WARN_ON(dev->desc); ··· 647 638 ubiblock_cleanup(dev); 648 639 kfree(dev); 649 640 } 641 + mutex_unlock(&devices_mutex); 650 642 } 651 643 652 644 int __init ubiblock_init(void)