• T
    fix wrong iput on d_inode introduced by e6bc45d6 · 50338b88
    Török Edwin 提交于
    Git bisection shows that commit e6bc45d6 causes
    BUG_ONs under high I/O load:
    
    kernel BUG at fs/inode.c:1368!
    [ 2862.501007] Call Trace:
    [ 2862.501007]  [<ffffffff811691d8>] d_kill+0xf8/0x140
    [ 2862.501007]  [<ffffffff81169c19>] dput+0xc9/0x190
    [ 2862.501007]  [<ffffffff8115577f>] fput+0x15f/0x210
    [ 2862.501007]  [<ffffffff81152171>] filp_close+0x61/0x90
    [ 2862.501007]  [<ffffffff81152251>] sys_close+0xb1/0x110
    [ 2862.501007]  [<ffffffff814c14fb>] system_call_fastpath+0x16/0x1b
    
    A reliable way to reproduce this bug is:
    Login to KDE, run 'rsnapshot sync', and apt-get install openjdk-6-jdk,
    and apt-get remove openjdk-6-jdk.
    
    The buggy part of the patch is this:
    	struct inode *inode = NULL;
    .....
    -               if (nd.last.name[nd.last.len])
    -                       goto slashes;
                    inode = dentry->d_inode;
    -               if (inode)
    -                       ihold(inode);
    +               if (nd.last.name[nd.last.len] || !inode)
    +                       goto slashes;
    +               ihold(inode)
    ...
    	if (inode)
    		iput(inode);	/* truncate the inode here */
    
    If nd.last.name[nd.last.len] is nonzero (and thus goto slashes branch is taken),
    and dentry->d_inode is non-NULL, then this code now does an additional iput on
    the inode, which is wrong.
    
    Fix this by only setting the inode variable if nd.last.name[nd.last.len] is 0.
    
    Reference: https://lkml.org/lkml/2011/6/15/50Reported-by: NNorbert Preining <preining@logic.at>
    Reported-by: NTörök Edwin <edwintorok@gmail.com>
    Cc: "Theodore Ts'o" <tytso@mit.edu>
    Cc: Al Viro <viro@zeniv.linux.org.uk>
    Signed-off-by: NTörök Edwin <edwintorok@gmail.com>
    Signed-off-by: NAl Viro <viro@zeniv.linux.org.uk>
    50338b88
namei.c 81.2 KB