• Brian Foster's avatar
    xfs: swap leaf buffer into path struct atomically during path shift · 7df1c170
    Brian Foster authored
    The node directory lookup code uses a state structure that tracks the
    path of buffers used to search for the hash of a filename through the
    leaf blocks. When the lookup encounters a block that ends with the
    requested hash, but the entry has not yet been found, it must shift over
    to the next block and continue looking for the entry (i.e., duplicate
    hashes could continue over into the next block). This shift mechanism
    involves walking back up and down the state structure, replacing buffers
    at the appropriate btree levels as necessary.
    
    When a buffer is replaced, the old buffer is released and the new buffer
    read into the active slot in the path structure. Because the buffer is
    read directly into the path slot, a buffer read failure can result in
    setting a NULL buffer pointer in an active slot. This throws off the
    state cleanup code in xfs_dir2_node_lookup(), which expects to release a
    buffer from each active slot. Instead, a BUG occurs due to a NULL
    pointer dereference:
    
      BUG: unable to handle kernel NULL pointer dereference at 00000000000001e8
      IP: [<ffffffffa0585063>] xfs_trans_brelse+0x2a3/0x3c0 [xfs]
      ...
      RIP: 0010:[<ffffffffa0585063>]  [<ffffffffa0585063>] xfs_trans_brelse+0x2a3/0x3c0 [xfs]
      ...
      Call Trace:
       [<ffffffffa05250c6>] xfs_dir2_node_lookup+0xa6/0x2c0 [xfs]
       [<ffffffffa0519f7c>] xfs_dir_lookup+0x1ac/0x1c0 [xfs]
       [<ffffffffa055d0e1>] xfs_lookup+0x91/0x290 [xfs]
       [<ffffffffa05580b3>] xfs_vn_lookup+0x73/0xb0 [xfs]
       [<ffffffff8122de8d>] lookup_real+0x1d/0x50
       [<ffffffff8123330e>] path_openat+0x91e/0x1490
       [<ffffffff81235079>] do_filp_open+0x89/0x100
       ...
    
    This has been reproduced via a parallel fsstress and filesystem shutdown
    workload in a loop. The shutdown triggers the read error in the
    aforementioned codepath and causes the BUG in xfs_dir2_node_lookup().
    
    Update xfs_da3_path_shift() to update the active path slot atomically
    with respect to the caller when a buffer is replaced. This ensures that
    the caller always sees the old or new buffer in the slot and prevents
    the NULL pointer dereference.
    Signed-off-by: default avatarBrian Foster <bfoster@redhat.com>
    Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
    Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
    7df1c170
xfs_da_btree.c 69.7 KB