Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] Bug in error recovery in fs/buffer.c::__block_prepare_write()

fs/buffer.c::__block_prepare_write() has broken error recovery. It calls
the get_block() callback with "create = 1" and if that succeeds it
immediately clears buffer_new on the just allocated buffer (which has
buffer_new set).

The bug is that if an error occurs and get_block() returns != 0, we break
from this loop and go into recovery code. This code has this comment:

/* Error case: */
/*
* Zero out any newly allocated blocks to avoid exposing stale
* data. If BH_New is set, we know that the block was newly
* allocated in the above loop.
*/

So the intent is obviously good in that it wants to clear just allocated
and hence not zeroed buffers. However the code recognises allocated
buffers by checking for buffer_new being set.

Unfortunately __block_prepare_write() as discussed above already cleared
buffer_new on all allocated buffers thus no buffers will be cleared during
error recovery and old data will be leaked.

The simplest way I can see to fix this is to make the current recovery code
work by _not_ clearing buffer_new after calling get_block() in
__block_prepare_write().

We cannot safely allow buffer_new buffers to "leak out" of
__block_prepare_write(), thus we simply do a quick loop over the buffers
clearing buffer_new on each of them if it is set just before returning
"success" from __block_prepare_write().

Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Anton Altaparmakov and committed by
Linus Torvalds
152becd2 9a59f452

+8 -4
+8 -4
fs/buffer.c
··· 1926 1926 if (err) 1927 1927 break; 1928 1928 if (buffer_new(bh)) { 1929 - clear_buffer_new(bh); 1930 1929 unmap_underlying_metadata(bh->b_bdev, 1931 1930 bh->b_blocknr); 1932 1931 if (PageUptodate(page)) { ··· 1967 1968 if (!buffer_uptodate(*wait_bh)) 1968 1969 err = -EIO; 1969 1970 } 1970 - if (!err) 1971 - return err; 1972 - 1971 + if (!err) { 1972 + bh = head; 1973 + do { 1974 + if (buffer_new(bh)) 1975 + clear_buffer_new(bh); 1976 + } while ((bh = bh->b_this_page) != head); 1977 + return 0; 1978 + } 1973 1979 /* Error case: */ 1974 1980 /* 1975 1981 * Zero out any newly allocated blocks to avoid exposing stale