• NeilBrown's avatar
    Fix read/truncate race · a32ea1e1
    NeilBrown authored
    do_generic_mapping_read currently samples the i_size at the start and doesn't
    do so again unless it needs to call ->readpage to load a page.  After
    ->readpage it has to re-sample i_size as a truncate may have caused that page
    to be filled with zeros, and the read() call should not see these.
    
    However there are other activities that might cause ->readpage to be called on
    a page between the time that do_generic_mapping_read samples i_size and when
    it finds that it has an uptodate page.  These include at least read-ahead and
    possibly another thread performing a read.
    
    So do_generic_mapping_read must sample i_size *after* it has an uptodate page.
     Thus the current sampling at the start and after a read can be replaced with
    a sampling before the copy-out.
    
    The same change applied to __generic_file_splice_read.
    
    Note that this fixes any race with truncate_complete_page, but does not fix a
    possible race with truncate_partial_page.  If a partial truncate happens after
    do_generic_mapping_read samples i_size and before the copy_out, the nuls that
    truncate_partial_page place in the page could be copied out incorrectly.
    
    I think the best fix for that is to *not* zero out parts of the page in
    truncate_partial_page, but rather to zero out the tail of a page when
    increasing i_size.
    Signed-off-by: default avatarNeil Brown <neilb@suse.de>
    Cc: Jens Axboe <jens.axboe@oracle.com>
    Acked-by: default avatarNick Piggin <npiggin@suse.de>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    a32ea1e1
filemap.c 63.4 KB