Skip to content

Commit

Permalink
dm snapshot: fix data corruption
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mikulas Patocka authored and kergon committed Oct 16, 2013
1 parent 61e6cfa commit e9c6a18
Showing 1 changed file with 12 additions and 6 deletions.
18 changes: 12 additions & 6 deletions drivers/md/dm-snap-persistent.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -502,6 +510,8 @@ static int read_exceptions(struct pstore *ps,

ps->current_area--;

skip_metadata(ps);

return 0;
}

Expand Down Expand Up @@ -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 ? */
Expand All @@ -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;
Expand Down

0 comments on commit e9c6a18

Please sign in to comment.