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.26-devel/drivers/md/dm-snap.c =================================================================== --- linux-2.6.26-devel.orig/drivers/md/dm-snap.c 2008-07-14 20:05:05.000000000 +0200 +++ linux-2.6.26-devel/drivers/md/dm-snap.c 2008-07-14 20:05:12.000000000 +0200 @@ -484,14 +484,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, @@ -511,21 +517,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.26-devel/drivers/md/dm-snap.h =================================================================== --- linux-2.6.26-devel.orig/drivers/md/dm-snap.h 2008-07-14 20:04:05.000000000 +0200 +++ linux-2.6.26-devel/drivers/md/dm-snap.h 2008-07-14 20:05:12.000000000 +0200 @@ -206,6 +206,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.26-devel/drivers/md/dm-exception-store.c =================================================================== --- linux-2.6.26-devel.orig/drivers/md/dm-exception-store.c 2008-07-14 20:05:01.000000000 +0200 +++ linux-2.6.26-devel/drivers/md/dm-exception-store.c 2008-07-14 20:05:12.000000000 +0200 @@ -237,6 +237,7 @@ static int zero_area(struct pstore *ps, 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; @@ -295,9 +296,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);