Re: PROBLEM: potential concurrency bug between do_vfs_ioctl() and do_readv()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue 26-01-21 02:18:03, Gong, Sishuai wrote:
> Hello,
> 
> We have found out why this bug happens. When a kernel thread is executing
> loop_clr_fd(), it will release the loop_ctl_mutex lock for a short period
> of time, before calling __loop_clr_fd(). However, another kernel thread
> may take use of this small gap, open the loop device, read it and cause a
> BLK_STS_IOERR eventually. This bug may lead to error messages on the
> kernel console, as mentioned in the previous email.
> 
> The following interleavings of this bug is shown below:
> 
> Thread 1				Thread 2
> // Execute loop_clr_fd()
> lo->lo_state = Lo_rundown
> mutex_unlock(&loop_ctl_mutex);
> 					// Execute lo_open()
> 					err = mutex_lock_killable(&loop_ctl_mutex);
> 					…
> 					lo = bdev->bd_disk->private_data;
> 					// lo_open return a success
> 					// User makes a ksys_read() request
> 					// loop_queue_rq()
> 					if (lo->lo_state != Lo_bound)
> 										return BLK_STS_IOERR;
> // Execute __loop_clr_fd()
> mutex_lock(&loop_ctl_mutex);
> ...

So you are right a race like this can happen. However generally it is
expected that you will see IO errors when you run loop_clr_fd() while there
may be other processes submitting IO. The particular scenario you've shown
above could possibly be handled by more careful checking in lo_open() but if
we just slightly modify it like:

Thread 1				Thread 2
					// Execute lo_open()
					err = mutex_lock_killable(&loop_ctl_mutex);
					…
					lo = bdev->bd_disk->private_data;
					// lo_open return a success
// Execute loop_clr_fd()
lo->lo_state = Lo_rundown
mutex_unlock(&loop_ctl_mutex);
					// User makes a ksys_read() request
					// loop_queue_rq()
					if (lo->lo_state != Lo_bound)
// Execute __loop_clr_fd()
mutex_lock(&loop_ctl_mutex);

Then it's kind of obvious that no checking in lo_open() can prevent IO
errors when loop device is being torn down. So to summarize the error
messages are expected when loop device is torn down while IO is in
progress...

								Honza


> > On Sep 28, 2020, at 10:44 AM, Gong, Sishuai <sishuai@xxxxxxxxxx> wrote:
> > 
> > Hi,
> > 
> > We found a potential concurrency bug in linux kernel 5.3.11. We are able to reproduce this bug in x86 under specific thread interleavings. This bug causes a blk_update_request I/O error.
> > 
> > ------------------------------------------
> > Kernel console output
> > blk_update_request: I/O error, dev loop0, sector 0 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 0
> > 
> > ------------------------------------------
> > Test input
> > This bug occurs when kernel functions do_vfs_ioctl() and do_readv() are executed with certain parameters in two separate threads and run concurrently.
> > 
> > The test program is generated in Syzkaller’s format as follows:
> > Test 1 [run in thread 1]
> > syz_read_part_table(0x0, 0x1, &(0x7f00000006c0)=[{0x0, 0x0, 0x100}])
> > Test 2 [run in thread 2]
> > r0 = syz_open_dev$loop(&(0x7f0000000000)='/dev/loop#\x00', 0x0, 0x0)
> > readv(r0, &(0x7f0000000340)=[{&(0x7f0000000440)=""/4096, 0x1000}], 0x1)
> > 
> > ------------------------------------------
> > Interleaving
> > Thread 1													Thread 2
> > 														do_readv()
> > 														-vfs_readv()
> > 														--do_iter_read()
> > 														---do_iter_readv_writev()
> > 														----blkdev_read_iter()
> > do_vfs_ioctl()						
> > --vfs_ioctl()	
> > --blkdev_ioctl()
> > ---blkdev_driver_ioctl()				
> > ----loop_set_fd()
> > -----bd_set_size()
> > 															(fs/blk_dev.c:1999)
> > 															loff_t size = i_size_read(bd_inode);
> > 															loff_t pos = iocb->ki_pos;
> > 															if (pos >= size)
> > 																return 0;
> > 															size -= pos;
> > 
> > 														----generic_file_read_iter()
> > 															(mm/filemap.c:2069)	
> > 															page = find_get_page(mapping, index);
> > 							  								if (!page) {
> > 																if (iocb->ki_flags & IOCB_NOWAIT)
> > 																	goto would_block;
> > 															page_cache_sync_readahead(mapping,
> > 
> > 															-----page_cache_sync_readahead()
> > 															------ondemand_readahead()
> > 															…
> > 															-----------...blk_update_request()
> > 															(error)
> > -----loop_sysfs_init()
> > …
> > 
> > ------------------------------------------
> > Analysis
> > We observed that when thread 2 is executed alone without thread 1, i_size_read() at fs/blk_dev.c:1999 returns a size of 0, thus in sequential mode blkdev_read_iter() returns directly at “return 0;” However, when two threads are executed concurrently, thread 1 changes the size of the same inode that thread 2 is concurrently accessing, then thread 2 goes into a different path, eventually causing the blk_update_request I/O error.
> > 
> > 
> > Thanks,
> > Sishuai
> > 
> 
-- 
Jan Kara <jack@xxxxxxxx>
SUSE Labs, CR



[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux