• O
    [PATCH] Fix a race condition between ->i_mapping and iput() · 09d967c6
    OGAWA Hirofumi 提交于
    This race became a cause of oops, and can reproduce by the following.
    
        while true; do
    	dd if=/dev/zero of=/dev/.static/dev/hdg1 bs=512 count=1000 & sync
        done
    
    This race condition was between __sync_single_inode() and iput().
    
              cpu0 (fs's inode)                 cpu1 (bdev's inode)
              -----------------                 -------------------
                                           close("/dev/hda2")
                                           [...]
    __sync_single_inode()
       /* copy the bdev's ->i_mapping */
       mapping = inode->i_mapping;
    
                                           generic_forget_inode()
                                              bdev_clear_inode()
    					     /* restre the fs's ->i_mapping */
    				             inode->i_mapping = &inode->i_data;
    				          /* bdev's inode was freed */
                                              destroy_inode(inode);
    
       if (wait) {
          /* dereference a freed bdev's mapping->host */
          filemap_fdatawait(mapping);  /* Oops */
    
    Since __sync_single_inode() is only taking a ref-count of fs's inode, the
    another process can be close() and freeing the bdev's inode while writing
    fs's inode.  So, __sync_signle_inode() accesses the freed ->i_mapping,
    oops.
    
    This patch takes a ref-count on the bdev's inode for the fs's inode before
    setting a ->i_mapping, and the clear_inode() of the fs's inode does iput() on
    the bdev's inode.  So if the fs's inode is still living, bdev's inode
    shouldn't be freed.
    Signed-off-by: NOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
    Signed-off-by: NAndrew Morton <akpm@osdl.org>
    Signed-off-by: NLinus Torvalds <torvalds@osdl.org>
    09d967c6
block_dev.c 29.3 KB