forked from Minki/linux
dm snapshot: fix data corruption
This patch fixes a particular type of data corruption that has been encountered when loading a snapshot's metadata from disk. When we allocate a new chunk in persistent_prepare, we increment ps->next_free and we make sure that it doesn't point to a metadata area by further incrementing it if necessary. When we load metadata from disk on device activation, ps->next_free is positioned after the last used data chunk. However, if this last used data chunk is followed by a metadata area, ps->next_free is positioned erroneously to the metadata area. A newly-allocated chunk is placed at the same location as the metadata area, resulting in data or metadata corruption. This patch changes the code so that ps->next_free skips the metadata area when metadata are loaded in function read_exceptions. The patch also moves a piece of code from persistent_prepare_exception to a separate function skip_metadata to avoid code duplication. CVE-2013-4299 Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Cc: stable@vger.kernel.org Cc: Mike Snitzer <snitzer@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
This commit is contained in:
parent
61e6cfa80d
commit
e9c6a18264
@ -269,6 +269,14 @@ static chunk_t area_location(struct pstore *ps, chunk_t area)
|
||||
return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area);
|
||||
}
|
||||
|
||||
static void skip_metadata(struct pstore *ps)
|
||||
{
|
||||
uint32_t stride = ps->exceptions_per_area + 1;
|
||||
chunk_t next_free = ps->next_free;
|
||||
if (sector_div(next_free, stride) == NUM_SNAPSHOT_HDR_CHUNKS)
|
||||
ps->next_free++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read or write a metadata area. Remembering to skip the first
|
||||
* chunk which holds the header.
|
||||
@ -502,6 +510,8 @@ static int read_exceptions(struct pstore *ps,
|
||||
|
||||
ps->current_area--;
|
||||
|
||||
skip_metadata(ps);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -616,8 +626,6 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
|
||||
struct dm_exception *e)
|
||||
{
|
||||
struct pstore *ps = get_info(store);
|
||||
uint32_t stride;
|
||||
chunk_t next_free;
|
||||
sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
|
||||
|
||||
/* Is there enough room ? */
|
||||
@ -630,10 +638,8 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
|
||||
* Move onto the next free pending, making sure to take
|
||||
* into account the location of the metadata chunks.
|
||||
*/
|
||||
stride = (ps->exceptions_per_area + 1);
|
||||
next_free = ++ps->next_free;
|
||||
if (sector_div(next_free, stride) == 1)
|
||||
ps->next_free++;
|
||||
ps->next_free++;
|
||||
skip_metadata(ps);
|
||||
|
||||
atomic_inc(&ps->pending_count);
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user