[PATCH] Fix a race condition between ->i_mapping and iput()
authorOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Thu, 22 Jun 2006 21:47:21 +0000 (14:47 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 22 Jun 2006 22:05:57 +0000 (15:05 -0700)
commit09d967c6f32b35eab15b45862ae16e4f06259d8e
tree9fca9dda390612041f857a33cb746fe1eb28b60a
parent0e5b3781591cc954037c08ef78edf7f1192d38c5
[PATCH] Fix a race condition between ->i_mapping and iput()

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: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/block_dev.c