Rework validation of chunk size. The previous code did validate chunk size specified by userspace but did not validate on-disk value. So if bad value were read from disk, it would cause system crash. I moved validation to a special function dm_snapshot_set_chunk_size. It is called for both user's value and for value read from disk. Signed-off-by: Mikulas Patocka --- drivers/md/dm-exception-store.c | 8 +++++--- drivers/md/dm-snap.c | 35 ++++++++++++++--------------------- drivers/md/dm-snap.h | 2 ++ 3 files changed, 21 insertions(+), 24 deletions(-) Index: linux-2.6.28-rc5-devel/drivers/md/dm-snap.c =================================================================== --- linux-2.6.28-rc5-devel.orig/drivers/md/dm-snap.c 2008-11-25 16:10:26.000000000 +0100 +++ linux-2.6.28-rc5-devel/drivers/md/dm-snap.c 2008-11-25 16:10:30.000000000 +0100 @@ -495,14 +495,20 @@ static int init_hash_tables(struct dm_sn return 0; } -/* - * Round a number up to the nearest 'size' boundary. size must - * be a power of 2. - */ -static ulong round_up(ulong n, ulong size) +char *dm_snapshot_set_chunk_size(struct dm_snapshot *s, chunk_t chunk_size) { - size--; - return (n + size) & ~size; + if (!is_power_of_2(chunk_size)) { + return "Chunk size is not a power of 2"; + } + if (chunk_size % (bdev_hardsect_size(s->cow->bdev) >> 9)) { + return "Chunk size is not a multiple of device blocksize"; + } + + s->chunk_size = chunk_size; + s->chunk_mask = chunk_size - 1; + s->chunk_shift = ffs(chunk_size) - 1; + + return NULL; } static int set_chunk_size(struct dm_snapshot *s, const char *chunk_size_arg, @@ -522,21 +528,8 @@ static int set_chunk_size(struct dm_snap return 0; } - /* Check chunk_size is a power of 2 */ - if (!is_power_of_2(chunk_size)) { - *error = "Chunk size is not a power of 2"; - return -EINVAL; - } - - /* Validate the chunk size against the device block size */ - if (chunk_size % (bdev_hardsect_size(s->cow->bdev) >> 9)) { - *error = "Chunk size is not a multiple of device blocksize"; + if ((*error = dm_snapshot_set_chunk_size(s, chunk_size))) return -EINVAL; - } - - s->chunk_size = chunk_size; - s->chunk_mask = chunk_size - 1; - s->chunk_shift = ffs(chunk_size) - 1; return 0; } Index: linux-2.6.28-rc5-devel/drivers/md/dm-snap.h =================================================================== --- linux-2.6.28-rc5-devel.orig/drivers/md/dm-snap.h 2008-11-25 16:10:10.000000000 +0100 +++ linux-2.6.28-rc5-devel/drivers/md/dm-snap.h 2008-11-25 16:10:30.000000000 +0100 @@ -208,6 +208,8 @@ static inline sector_t chunk_to_sector(s return chunk << s->chunk_shift; } +char *dm_snapshot_set_chunk_size(struct dm_snapshot *s, chunk_t chunk_size); + static inline int bdev_equal(struct block_device *lhs, struct block_device *rhs) { /* Index: linux-2.6.28-rc5-devel/drivers/md/dm-exception-store.c =================================================================== --- linux-2.6.28-rc5-devel.orig/drivers/md/dm-exception-store.c 2008-11-25 16:10:25.000000000 +0100 +++ linux-2.6.28-rc5-devel/drivers/md/dm-exception-store.c 2008-11-25 16:10:30.000000000 +0100 @@ -273,6 +273,7 @@ static int zero_disk_area(struct pstore static int read_header(struct pstore *ps, int *new_snapshot) { int r; + char *e; struct disk_header *dh; chunk_t chunk_size; int chunk_size_supplied = 1; @@ -331,9 +332,10 @@ static int read_header(struct pstore *ps /* We had a bogus chunk_size. Fix stuff up. */ free_area(ps); - ps->snap->chunk_size = chunk_size; - ps->snap->chunk_mask = chunk_size - 1; - ps->snap->chunk_shift = ffs(chunk_size) - 1; + if ((e = dm_snapshot_set_chunk_size(ps->snap, chunk_size))) { + DMWARN("%s", e); + return -EINVAL; + } r = dm_io_client_resize(sectors_to_pages(ps->snap->chunk_size), ps->io_client);