Commit 9660d501 authored by Brian Norris's avatar Brian Norris Committed by Luis Henriques

mtd: fix: avoid race condition when accessing mtd->usecount

commit 073db4a5 upstream.

On A MIPS 32-cores machine a BUG_ON was triggered because some acesses to
mtd->usecount were done without taking mtd_table_mutex.
kernel: Call Trace:
kernel: [<ffffffff80401818>] __put_mtd_device+0x20/0x50
kernel: [<ffffffff804086f4>] blktrans_release+0x8c/0xd8
kernel: [<ffffffff802577e0>] __blkdev_put+0x1a8/0x200
kernel: [<ffffffff802579a4>] blkdev_close+0x1c/0x30
kernel: [<ffffffff8022006c>] __fput+0xac/0x250
kernel: [<ffffffff80171208>] task_work_run+0xd8/0x120
kernel: [<ffffffff8012c23c>] work_notifysig+0x10/0x18
kernel:
kernel:
        Code: 2442ffff  ac8202d8  000217fe <00020336> dc820128  10400003
               00000000  0040f809  00000000
kernel: ---[ end trace 080fbb4579b47a73 ]---

Fixed by taking the mutex in blktrans_open and blktrans_release.

Note that this locking is already suggested in
include/linux/mtd/blktrans.h:

struct mtd_blktrans_ops {
...
	/* Called with mtd_table_mutex held; no race with add/remove */
	int (*open)(struct mtd_blktrans_dev *dev);
	void (*release)(struct mtd_blktrans_dev *dev);
...
};

But we weren't following it.

Originally reported by (and patched by) Zhang and Giuseppe,
independently. Improved and rewritten.
Reported-by: default avatarZhang Xingcai <zhangxingcai@huawei.com>
Reported-by: default avatarGiuseppe Cantavenera <giuseppe.cantavenera.ext@nokia.com>
Tested-by: default avatarGiuseppe Cantavenera <giuseppe.cantavenera.ext@nokia.com>
Acked-by: default avatarAlexander Sverdlin <alexander.sverdlin@nokia.com>
Signed-off-by: default avatarBrian Norris <computersforpeace@gmail.com>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent a1800bf1
...@@ -200,6 +200,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) ...@@ -200,6 +200,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
mutex_lock(&mtd_table_mutex);
if (dev->open) if (dev->open)
goto unlock; goto unlock;
...@@ -223,6 +224,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) ...@@ -223,6 +224,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
unlock: unlock:
dev->open++; dev->open++;
mutex_unlock(&mtd_table_mutex);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
blktrans_dev_put(dev); blktrans_dev_put(dev);
return ret; return ret;
...@@ -233,6 +235,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) ...@@ -233,6 +235,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
error_put: error_put:
module_put(dev->tr->owner); module_put(dev->tr->owner);
kref_put(&dev->ref, blktrans_dev_release); kref_put(&dev->ref, blktrans_dev_release);
mutex_unlock(&mtd_table_mutex);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
blktrans_dev_put(dev); blktrans_dev_put(dev);
return ret; return ret;
...@@ -246,6 +249,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode) ...@@ -246,6 +249,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
return; return;
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
mutex_lock(&mtd_table_mutex);
if (--dev->open) if (--dev->open)
goto unlock; goto unlock;
...@@ -259,6 +263,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode) ...@@ -259,6 +263,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
__put_mtd_device(dev->mtd); __put_mtd_device(dev->mtd);
} }
unlock: unlock:
mutex_unlock(&mtd_table_mutex);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
blktrans_dev_put(dev); blktrans_dev_put(dev);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment