• Jan Kara's avatar
    ext4: fix bh->b_state corruption · 6c5cff7f
    Jan Kara authored
    commit ed8ad838 upstream.
    
    ext4 can update bh->b_state non-atomically in _ext4_get_block() and
    ext4_da_get_block_prep(). Usually this is fine since bh is just a
    temporary storage for mapping information on stack but in some cases it
    can be fully living bh attached to a page. In such case non-atomic
    update of bh->b_state can race with an atomic update which then gets
    lost. Usually when we are mapping bh and thus updating bh->b_state
    non-atomically, nobody else touches the bh and so things work out fine
    but there is one case to especially worry about: ext4_finish_bio() uses
    BH_Uptodate_Lock on the first bh in the page to synchronize handling of
    PageWriteback state. So when blocksize < pagesize, we can be atomically
    modifying bh->b_state of a buffer that actually isn't under IO and thus
    can race e.g. with delalloc trying to map that buffer. The result is
    that we can mistakenly set / clear BH_Uptodate_Lock bit resulting in the
    corruption of PageWriteback state or missed unlock of BH_Uptodate_Lock.
    
    Fix the problem by always updating bh->b_state bits atomically.
    Reported-by: default avatarNikolay Borisov <kernel@kyup.com>
    Signed-off-by: default avatarJan Kara <jack@suse.cz>
    Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
    [ luis: backported to 3.16:
      - replaced READ_ONCE() by ACCESS_ONCE() ]
    Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
    Signed-off-by: default avatarKamal Mostafa <kamal@canonical.com>
    6c5cff7f
inode.c 153 KB