aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-snap-persistent.c
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2013-10-15 22:17:47 -0400
committerAlasdair G Kergon <agk@redhat.com>2013-10-15 22:17:47 -0400
commite9c6a182649f4259db704ae15a91ac820e63b0ca (patch)
tree3cfbfc80ce16f6655a8eb823015bd2f34df87dc1 /drivers/md/dm-snap-persistent.c
parent61e6cfa80de5760bbe406f4e815b7739205754d2 (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.c18
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
272static 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;