• Jan Kara's avatar
    ext4: fix data corruption with EXT4_GET_BLOCKS_ZERO · 4f8caa60
    Jan Kara authored
    When ext4_map_blocks() is called with EXT4_GET_BLOCKS_ZERO to zero-out
    allocated blocks and these blocks are actually converted from unwritten
    extent the following race can happen:
    
    CPU0					CPU1
    
    page fault				page fault
    ...					...
    ext4_map_blocks()
      ext4_ext_map_blocks()
        ext4_ext_handle_unwritten_extents()
          ext4_ext_convert_to_initialized()
    	- zero out converted extent
    	ext4_zeroout_es()
    	  - inserts extent as initialized in status tree
    
    					ext4_map_blocks()
    					  ext4_es_lookup_extent()
    					    - finds initialized extent
    					write data
      ext4_issue_zeroout()
        - zeroes out new extent overwriting data
    
    This problem can be reproduced by generic/340 for the fallocated case
    for the last block in the file.
    
    Fix the problem by avoiding zeroing out the area we are mapping with
    ext4_map_blocks() in ext4_ext_convert_to_initialized(). It is pointless
    to zero out this area in the first place as the caller asked us to
    convert the area to initialized because he is just going to write data
    there before the transaction finishes. To achieve this we delete the
    special case of zeroing out full extent as that will be handled by the
    cases below zeroing only the part of the extent that needs it. We also
    instruct ext4_split_extent() that the middle of extent being split
    contains data so that ext4_split_extent_at() cannot zero out full extent
    in case of ENOSPC.
    
    CC: stable@vger.kernel.org
    Fixes: 12735f88Signed-off-by: default avatarJan Kara <jack@suse.cz>
    Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
    4f8caa60
extents.c 163 KB