diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2013-10-15 22:17:47 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2013-10-15 22:17:47 -0400 |
commit | e9c6a182649f4259db704ae15a91ac820e63b0ca (patch) | |
tree | 3cfbfc80ce16f6655a8eb823015bd2f34df87dc1 /drivers/md/dm-snap-persistent.c | |
parent | 61e6cfa80de5760bbe406f4e815b7739205754d2 (diff) |
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>
Diffstat (limited to 'drivers/md/dm-snap-persistent.c')
-rw-r--r-- | drivers/md/dm-snap-persistent.c | 18 |
1 files changed, 12 insertions, 6 deletions
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 4caa8e6d59d7..2d2b1b7588d7 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c | |||
@@ -269,6 +269,14 @@ static chunk_t area_location(struct pstore *ps, chunk_t area) | |||
269 | return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area); | 269 | return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area); |
270 | } | 270 | } |
271 | 271 | ||
272 | static void skip_metadata(struct pstore *ps) | ||
273 | { | ||
274 | uint32_t stride = ps->exceptions_per_area + 1; | ||
275 | chunk_t next_free = ps->next_free; | ||
276 | if (sector_div(next_free, stride) == NUM_SNAPSHOT_HDR_CHUNKS) | ||
277 | ps->next_free++; | ||
278 | } | ||
279 | |||
272 | /* | 280 | /* |
273 | * Read or write a metadata area. Remembering to skip the first | 281 | * Read or write a metadata area. Remembering to skip the first |
274 | * chunk which holds the header. | 282 | * chunk which holds the header. |
@@ -502,6 +510,8 @@ static int read_exceptions(struct pstore *ps, | |||
502 | 510 | ||
503 | ps->current_area--; | 511 | ps->current_area--; |
504 | 512 | ||
513 | skip_metadata(ps); | ||
514 | |||
505 | return 0; | 515 | return 0; |
506 | } | 516 | } |
507 | 517 | ||
@@ -616,8 +626,6 @@ static int persistent_prepare_exception(struct dm_exception_store *store, | |||
616 | struct dm_exception *e) | 626 | struct dm_exception *e) |
617 | { | 627 | { |
618 | struct pstore *ps = get_info(store); | 628 | struct pstore *ps = get_info(store); |
619 | uint32_t stride; | ||
620 | chunk_t next_free; | ||
621 | sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev); | 629 | sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev); |
622 | 630 | ||
623 | /* Is there enough room ? */ | 631 | /* Is there enough room ? */ |
@@ -630,10 +638,8 @@ static int persistent_prepare_exception(struct dm_exception_store *store, | |||
630 | * Move onto the next free pending, making sure to take | 638 | * Move onto the next free pending, making sure to take |
631 | * into account the location of the metadata chunks. | 639 | * into account the location of the metadata chunks. |
632 | */ | 640 | */ |
633 | stride = (ps->exceptions_per_area + 1); | 641 | ps->next_free++; |
634 | next_free = ++ps->next_free; | 642 | skip_metadata(ps); |
635 | if (sector_div(next_free, stride) == 1) | ||
636 | ps->next_free++; | ||
637 | 643 | ||
638 | atomic_inc(&ps->pending_count); | 644 | atomic_inc(&ps->pending_count); |
639 | return 0; | 645 | return 0; |