| From af309226db916e2c6e08d3eba3fa5c34225200c4 Mon Sep 17 00:00:00 2001 |
| From: Rabin Vincent <rabinv@axis.com> |
| Date: Thu, 1 Dec 2016 09:18:28 +0100 |
| Subject: block: protect iterate_bdevs() against concurrent close |
| |
| From: Rabin Vincent <rabinv@axis.com> |
| |
| commit af309226db916e2c6e08d3eba3fa5c34225200c4 upstream. |
| |
| If a block device is closed while iterate_bdevs() is handling it, the |
| following NULL pointer dereference occurs because bdev->b_disk is NULL |
| in bdev_get_queue(), which is called from blk_get_backing_dev_info() (in |
| turn called by the mapping_cap_writeback_dirty() call in |
| __filemap_fdatawrite_range()): |
| |
| BUG: unable to handle kernel NULL pointer dereference at 0000000000000508 |
| IP: [<ffffffff81314790>] blk_get_backing_dev_info+0x10/0x20 |
| PGD 9e62067 PUD 9ee8067 PMD 0 |
| Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC |
| Modules linked in: |
| CPU: 1 PID: 2422 Comm: sync Not tainted 4.5.0-rc7+ #400 |
| Hardware name: QEMU Standard PC (i440FX + PIIX, 1996) |
| task: ffff880009f4d700 ti: ffff880009f5c000 task.ti: ffff880009f5c000 |
| RIP: 0010:[<ffffffff81314790>] [<ffffffff81314790>] blk_get_backing_dev_info+0x10/0x20 |
| RSP: 0018:ffff880009f5fe68 EFLAGS: 00010246 |
| RAX: 0000000000000000 RBX: ffff88000ec17a38 RCX: ffffffff81a4e940 |
| RDX: 7fffffffffffffff RSI: 0000000000000000 RDI: ffff88000ec176c0 |
| RBP: ffff880009f5fe68 R08: 0000000000000000 R09: 0000000000000000 |
| R10: 0000000000000001 R11: 0000000000000000 R12: ffff88000ec17860 |
| R13: ffffffff811b25c0 R14: ffff88000ec178e0 R15: ffff88000ec17a38 |
| FS: 00007faee505d700(0000) GS:ffff88000fb00000(0000) knlGS:0000000000000000 |
| CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b |
| CR2: 0000000000000508 CR3: 0000000009e8a000 CR4: 00000000000006e0 |
| Stack: |
| ffff880009f5feb8 ffffffff8112e7f5 0000000000000000 7fffffffffffffff |
| 0000000000000000 0000000000000000 7fffffffffffffff 0000000000000001 |
| ffff88000ec178e0 ffff88000ec17860 ffff880009f5fec8 ffffffff8112e81f |
| Call Trace: |
| [<ffffffff8112e7f5>] __filemap_fdatawrite_range+0x85/0x90 |
| [<ffffffff8112e81f>] filemap_fdatawrite+0x1f/0x30 |
| [<ffffffff811b25d6>] fdatawrite_one_bdev+0x16/0x20 |
| [<ffffffff811bc402>] iterate_bdevs+0xf2/0x130 |
| [<ffffffff811b2763>] sys_sync+0x63/0x90 |
| [<ffffffff815d4272>] entry_SYSCALL_64_fastpath+0x12/0x76 |
| Code: 0f 1f 44 00 00 48 8b 87 f0 00 00 00 55 48 89 e5 <48> 8b 80 08 05 00 00 5d |
| RIP [<ffffffff81314790>] blk_get_backing_dev_info+0x10/0x20 |
| RSP <ffff880009f5fe68> |
| CR2: 0000000000000508 |
| ---[ end trace 2487336ceb3de62d ]--- |
| |
| The crash is easily reproducible by running the following command, if an |
| msleep(100) is inserted before the call to func() in iterate_devs(): |
| |
| while :; do head -c1 /dev/nullb0; done > /dev/null & while :; do sync; done |
| |
| Fix it by holding the bd_mutex across the func() call and only calling |
| func() if the bdev is opened. |
| |
| Fixes: 5c0d6b60a0ba ("vfs: Create function for iterating over block devices") |
| Reported-and-tested-by: Wei Fang <fangwei1@huawei.com> |
| Signed-off-by: Rabin Vincent <rabinv@axis.com> |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| Reviewed-by: Christoph Hellwig <hch@lst.de> |
| Signed-off-by: Jens Axboe <axboe@fb.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/block_dev.c | 7 ++++++- |
| 1 file changed, 6 insertions(+), 1 deletion(-) |
| |
| --- a/fs/block_dev.c |
| +++ b/fs/block_dev.c |
| @@ -1885,6 +1885,7 @@ void iterate_bdevs(void (*func)(struct b |
| spin_lock(&blockdev_superblock->s_inode_list_lock); |
| list_for_each_entry(inode, &blockdev_superblock->s_inodes, i_sb_list) { |
| struct address_space *mapping = inode->i_mapping; |
| + struct block_device *bdev; |
| |
| spin_lock(&inode->i_lock); |
| if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW) || |
| @@ -1905,8 +1906,12 @@ void iterate_bdevs(void (*func)(struct b |
| */ |
| iput(old_inode); |
| old_inode = inode; |
| + bdev = I_BDEV(inode); |
| |
| - func(I_BDEV(inode), arg); |
| + mutex_lock(&bdev->bd_mutex); |
| + if (bdev->bd_openers) |
| + func(bdev, arg); |
| + mutex_unlock(&bdev->bd_mutex); |
| |
| spin_lock(&blockdev_superblock->s_inode_list_lock); |
| } |