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

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

 



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





[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux