MOKB-19-11-2006

Bug details
Title: Linux 2.6.x NTFS __find_get_block_slow() denial of service
Description: The NTFS filesystem module of the Linux 2.6.x kernel fails to properly handle corrupted data structures, leading to an exploitable denial of service condition. This issue is similar to that explained in MOKB-05-11-2006.
Author/Contributor:
References:
Proof of concept or exploit: The following filesystem image can be used to reproduce the bug: MOKB-19-11-2006.img.bz2
Use a loopback device to mount it: bunzip2 MOKB-19-11-2006.img.bz2 && mount -t ntfs -o loop MOKB-19-11-2006.img /media/test && ls /media/test
Debugging information:

The bug has been found using the Linux version of fsfuzzer on a Fedora Core 6 installation, with up to date packages as of 18-11-2006. The architecture used to conduct the tests is IA32/x86, SMP enabled.

[root@fedora ~]# uname -a
Linux fedora 2.6.18-1.2798.fc6 #1 SMP Mon Oct 16 14:37:32 EDT 2006 i686 i686 i386 GNU/Linux
				

Related debugging information and source code:

----------------------- fs/buffer.c (2.6.18)
1692 void unmap_underlying_metadata(struct block_device *bdev, sector_t block)
1693 {
1694         struct buffer_head *old_bh;
1695 
1696         might_sleep();
1697 
1698         old_bh = __find_get_block_slow(bdev, block);
1699         if (old_bh) {
1700                 clear_buffer_dirty(old_bh);
1701                 wait_on_buffer(old_bh);
1702                 clear_buffer_req(old_bh);
1703                 __brelse(old_bh);
1704         }
1705 }
1706 EXPORT_SYMBOL(unmap_underlying_metadata);
----------------------- fs/buffer.c (2.6.18)

----------------------- fs/buffer.c (2.6.18)
1444 /*
1445  * __getblk will locate (and, if necessary, create) the buffer_head
1446  * which corresponds to the passed block_device, block and size. The
1447  * returned buffer has its reference count incremented.
1448  *
1449  * __getblk() cannot fail - it just keeps trying.  If you pass it an
1450  * illegal block number, __getblk() will happily return a buffer_head
1451  * which represents the non-existent block.  Very weird.
1452  *
1453  * __getblk() will lock up the machine if grow_dev_page's try_to_free_buffers()
1454  * attempt is failing.  FIXME, perhaps?
1455  */
1456 struct buffer_head *
1457 __getblk(struct block_device *bdev, sector_t block, int size)
1458 {
1459         struct buffer_head *bh = __find_get_block(bdev, block, size);  <---------- (!)
1460 
1461         might_sleep();
1462         if (bh == NULL)
1463                 bh = __getblk_slow(bdev, block, size);
1464         return bh;
1465 }
----------------------- fs/buffer.c (2.6.18)

----------------------- fs/buffer.c (2.6.18)
1423 /*
1424  * Perform a pagecache lookup for the matching buffer.  If it's there, refresh
1425  * it in the LRU and mark it as accessed.  If it is not present then return
1426  * NULL
1427  */
1428 struct buffer_head *
1429 __find_get_block(struct block_device *bdev, sector_t block, int size)
1430 {
1431         struct buffer_head *bh = lookup_bh_lru(bdev, block, size);
1432 
1433         if (bh == NULL) {
1434                 bh = __find_get_block_slow(bdev, block);				<---------- (!)
1435                 if (bh)
1436                         bh_lru_install(bh);
1437         }
1438         if (bh)
1439                 touch_buffer(bh);
1440         return bh;
1441 }
1442 EXPORT_SYMBOL(__find_get_block);
----------------------- fs/buffer.c (2.6.18)

----------------------- fs/buffer.c (2.6.18)
386 __find_get_block_slow(struct block_device *bdev, sector_t block)
387 {
388         struct inode *bd_inode = bdev->bd_inode;
389         struct address_space *bd_mapping = bd_inode->i_mapping;
390         struct buffer_head *ret = NULL;
391         pgoff_t index;
392         struct buffer_head *bh;
393         struct buffer_head *head;
394         struct page *page;
395         int all_mapped = 1;
396 
397         index = block >> (PAGE_CACHE_SHIFT - bd_inode->i_blkbits);
398         page = find_get_page(bd_mapping, index);
399         if (!page)
400                 goto out;
401 
402         spin_lock(&bd_mapping->private_lock);
403         if (!page_has_buffers(page))
404                 goto out_unlock;
405         head = page_buffers(page);
406         bh = head;
407         do {
408                 if (bh->b_blocknr == block) {
409                         ret = bh;
410                         get_bh(bh);
411                         goto out_unlock;
412                 }
413                 if (!buffer_mapped(bh))
414                         all_mapped = 0;                                     <------
415                 bh = bh->b_this_page;
416         } while (bh != head);
417 
418         /* we might be here because some of the buffers on this page are
419          * not mapped.  This is due to various races between               <------\
420          * file io on the block device and getblk.  It gets dealt with     <-------> 'elsewhere'
421          * elsewhere, don't buffer_error if we had some unmapped buffers   <------/
422          */
423         if (all_mapped) {
424                 printk("__find_get_block_slow() failed. "
425                         "block=%llu, b_blocknr=%llu\n",
426                         (unsigned long long)block,
427                         (unsigned long long)bh->b_blocknr);
428                 printk("b_state=0x%08lx, b_size=%zu\n",
429                         bh->b_state, bh->b_size);
430                 printk("device blocksize: %d\n", 1 << bd_inode->i_blkbits);
431         }
432 out_unlock:
433         spin_unlock(&bd_mapping->private_lock);
434         page_cache_release(page);
435 out:
436         return ret;
437 }
----------------------- fs/buffer.c (2.6.18)

kernel msg buffer:

__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
__find_get_block_slow() failed. block=944892805152, b_blocknr=32
b_state=0x00000020, b_size=512
device blocksize: 512
(...)