Commit d8b8a43e authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Greg Kroah-Hartman

dm: fix truncated status strings

commit fd7c092e upstream.

Avoid returning a truncated table or status string instead of setting
the DM_BUFFER_FULL_FLAG when the last target of a table fills the
buffer.

When processing a table or status request, the function retrieve_status
calls ti->type->status. If ti->type->status returns non-zero,
retrieve_status assumes that the buffer overflowed and sets
DM_BUFFER_FULL_FLAG.

However, targets don't return non-zero values from their status method
on overflow. Most targets returns always zero.

If a buffer overflow happens in a target that is not the last in the
table, it gets noticed during the next iteration of the loop in
retrieve_status; but if a buffer overflow happens in the last target, it
goes unnoticed and erroneously truncated data is returned.

In the current code, the targets behave in the following way:
* dm-crypt returns -ENOMEM if there is not enough space to store the
  key, but it returns 0 on all other overflows.
* dm-thin returns errors from the status method if a disk error happened.
  This is incorrect because retrieve_status doesn't check the error
  code, it assumes that all non-zero values mean buffer overflow.
* all the other targets always return 0.

This patch changes the ti->type->status function to return void (because
most targets don't use the return code). Overflow is detected in
retrieve_status: if the status method fills up the remaining space
completely, it is assumed that buffer overflow happened.
Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9e23d8bd
...@@ -1262,20 +1262,6 @@ static int crypt_decode_key(u8 *key, char *hex, unsigned int size) ...@@ -1262,20 +1262,6 @@ static int crypt_decode_key(u8 *key, char *hex, unsigned int size)
return 0; return 0;
} }
/*
* Encode key into its hex representation
*/
static void crypt_encode_key(char *hex, u8 *key, unsigned int size)
{
unsigned int i;
for (i = 0; i < size; i++) {
sprintf(hex, "%02x", *key);
hex += 2;
key++;
}
}
static void crypt_free_tfms(struct crypt_config *cc, int cpu) static void crypt_free_tfms(struct crypt_config *cc, int cpu)
{ {
struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu); struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu);
...@@ -1741,11 +1727,11 @@ static int crypt_map(struct dm_target *ti, struct bio *bio, ...@@ -1741,11 +1727,11 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
return DM_MAPIO_SUBMITTED; return DM_MAPIO_SUBMITTED;
} }
static int crypt_status(struct dm_target *ti, status_type_t type, static void crypt_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen) char *result, unsigned int maxlen)
{ {
struct crypt_config *cc = ti->private; struct crypt_config *cc = ti->private;
unsigned int sz = 0; unsigned i, sz = 0;
switch (type) { switch (type) {
case STATUSTYPE_INFO: case STATUSTYPE_INFO:
...@@ -1755,17 +1741,11 @@ static int crypt_status(struct dm_target *ti, status_type_t type, ...@@ -1755,17 +1741,11 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
case STATUSTYPE_TABLE: case STATUSTYPE_TABLE:
DMEMIT("%s ", cc->cipher_string); DMEMIT("%s ", cc->cipher_string);
if (cc->key_size > 0) { if (cc->key_size > 0)
if ((maxlen - sz) < ((cc->key_size << 1) + 1)) for (i = 0; i < cc->key_size; i++)
return -ENOMEM; DMEMIT("%02x", cc->key[i]);
else
crypt_encode_key(result + sz, cc->key, cc->key_size); DMEMIT("-");
sz += cc->key_size << 1;
} else {
if (sz >= maxlen)
return -ENOMEM;
result[sz++] = '-';
}
DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset, DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset,
cc->dev->name, (unsigned long long)cc->start); cc->dev->name, (unsigned long long)cc->start);
...@@ -1775,7 +1755,6 @@ static int crypt_status(struct dm_target *ti, status_type_t type, ...@@ -1775,7 +1755,6 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
break; break;
} }
return 0;
} }
static void crypt_postsuspend(struct dm_target *ti) static void crypt_postsuspend(struct dm_target *ti)
......
...@@ -294,7 +294,7 @@ static int delay_map(struct dm_target *ti, struct bio *bio, ...@@ -294,7 +294,7 @@ static int delay_map(struct dm_target *ti, struct bio *bio,
return delay_bio(dc, dc->read_delay, bio); return delay_bio(dc, dc->read_delay, bio);
} }
static int delay_status(struct dm_target *ti, status_type_t type, static void delay_status(struct dm_target *ti, status_type_t type,
char *result, unsigned maxlen) char *result, unsigned maxlen)
{ {
struct delay_c *dc = ti->private; struct delay_c *dc = ti->private;
...@@ -315,8 +315,6 @@ static int delay_status(struct dm_target *ti, status_type_t type, ...@@ -315,8 +315,6 @@ static int delay_status(struct dm_target *ti, status_type_t type,
dc->write_delay); dc->write_delay);
break; break;
} }
return 0;
} }
static int delay_iterate_devices(struct dm_target *ti, static int delay_iterate_devices(struct dm_target *ti,
......
...@@ -332,7 +332,7 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio, ...@@ -332,7 +332,7 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio,
return error; return error;
} }
static int flakey_status(struct dm_target *ti, status_type_t type, static void flakey_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen) char *result, unsigned int maxlen)
{ {
unsigned sz = 0; unsigned sz = 0;
...@@ -363,7 +363,6 @@ static int flakey_status(struct dm_target *ti, status_type_t type, ...@@ -363,7 +363,6 @@ static int flakey_status(struct dm_target *ti, status_type_t type,
break; break;
} }
return 0;
} }
static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg)
......
...@@ -1066,6 +1066,7 @@ static void retrieve_status(struct dm_table *table, ...@@ -1066,6 +1066,7 @@ static void retrieve_status(struct dm_table *table,
num_targets = dm_table_get_num_targets(table); num_targets = dm_table_get_num_targets(table);
for (i = 0; i < num_targets; i++) { for (i = 0; i < num_targets; i++) {
struct dm_target *ti = dm_table_get_target(table, i); struct dm_target *ti = dm_table_get_target(table, i);
size_t l;
remaining = len - (outptr - outbuf); remaining = len - (outptr - outbuf);
if (remaining <= sizeof(struct dm_target_spec)) { if (remaining <= sizeof(struct dm_target_spec)) {
...@@ -1089,15 +1090,18 @@ static void retrieve_status(struct dm_table *table, ...@@ -1089,15 +1090,18 @@ static void retrieve_status(struct dm_table *table,
} }
/* Get the status/table string from the target driver */ /* Get the status/table string from the target driver */
if (ti->type->status) { if (ti->type->status)
if (ti->type->status(ti, type, outptr, remaining)) { ti->type->status(ti, type, outptr, remaining);
else
outptr[0] = '\0';
l = strlen(outptr) + 1;
if (l == remaining) {
param->flags |= DM_BUFFER_FULL_FLAG; param->flags |= DM_BUFFER_FULL_FLAG;
break; break;
} }
} else
outptr[0] = '\0';
outptr += strlen(outptr) + 1; outptr += l;
used = param->data_start + (outptr - outbuf); used = param->data_start + (outptr - outbuf);
outptr = align_ptr(outptr); outptr = align_ptr(outptr);
......
...@@ -95,7 +95,7 @@ static int linear_map(struct dm_target *ti, struct bio *bio, ...@@ -95,7 +95,7 @@ static int linear_map(struct dm_target *ti, struct bio *bio,
return DM_MAPIO_REMAPPED; return DM_MAPIO_REMAPPED;
} }
static int linear_status(struct dm_target *ti, status_type_t type, static void linear_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen) char *result, unsigned int maxlen)
{ {
struct linear_c *lc = (struct linear_c *) ti->private; struct linear_c *lc = (struct linear_c *) ti->private;
...@@ -110,7 +110,6 @@ static int linear_status(struct dm_target *ti, status_type_t type, ...@@ -110,7 +110,6 @@ static int linear_status(struct dm_target *ti, status_type_t type,
(unsigned long long)lc->start); (unsigned long long)lc->start);
break; break;
} }
return 0;
} }
static int linear_ioctl(struct dm_target *ti, unsigned int cmd, static int linear_ioctl(struct dm_target *ti, unsigned int cmd,
......
...@@ -1343,7 +1343,7 @@ static void multipath_resume(struct dm_target *ti) ...@@ -1343,7 +1343,7 @@ static void multipath_resume(struct dm_target *ti)
* [priority selector-name num_ps_args [ps_args]* * [priority selector-name num_ps_args [ps_args]*
* num_paths num_selector_args [path_dev [selector_args]* ]+ ]+ * num_paths num_selector_args [path_dev [selector_args]* ]+ ]+
*/ */
static int multipath_status(struct dm_target *ti, status_type_t type, static void multipath_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen) char *result, unsigned int maxlen)
{ {
int sz = 0; int sz = 0;
...@@ -1447,8 +1447,6 @@ static int multipath_status(struct dm_target *ti, status_type_t type, ...@@ -1447,8 +1447,6 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
} }
spin_unlock_irqrestore(&m->lock, flags); spin_unlock_irqrestore(&m->lock, flags);
return 0;
} }
static int multipath_message(struct dm_target *ti, unsigned argc, char **argv) static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
......
...@@ -1067,7 +1067,7 @@ static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_c ...@@ -1067,7 +1067,7 @@ static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_c
return DM_MAPIO_SUBMITTED; return DM_MAPIO_SUBMITTED;
} }
static int raid_status(struct dm_target *ti, status_type_t type, static void raid_status(struct dm_target *ti, status_type_t type,
char *result, unsigned maxlen) char *result, unsigned maxlen)
{ {
struct raid_set *rs = ti->private; struct raid_set *rs = ti->private;
...@@ -1203,8 +1203,6 @@ static int raid_status(struct dm_target *ti, status_type_t type, ...@@ -1203,8 +1203,6 @@ static int raid_status(struct dm_target *ti, status_type_t type,
DMEMIT(" -"); DMEMIT(" -");
} }
} }
return 0;
} }
static int raid_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) static int raid_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)
......
...@@ -1362,7 +1362,7 @@ static char device_status_char(struct mirror *m) ...@@ -1362,7 +1362,7 @@ static char device_status_char(struct mirror *m)
} }
static int mirror_status(struct dm_target *ti, status_type_t type, static void mirror_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen) char *result, unsigned int maxlen)
{ {
unsigned int m, sz = 0; unsigned int m, sz = 0;
...@@ -1398,8 +1398,6 @@ static int mirror_status(struct dm_target *ti, status_type_t type, ...@@ -1398,8 +1398,6 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
if (ms->features & DM_RAID1_HANDLE_ERRORS) if (ms->features & DM_RAID1_HANDLE_ERRORS)
DMEMIT(" 1 handle_errors"); DMEMIT(" 1 handle_errors");
} }
return 0;
} }
static int mirror_iterate_devices(struct dm_target *ti, static int mirror_iterate_devices(struct dm_target *ti,
......
...@@ -1845,7 +1845,7 @@ static void snapshot_merge_resume(struct dm_target *ti) ...@@ -1845,7 +1845,7 @@ static void snapshot_merge_resume(struct dm_target *ti)
start_merge(s); start_merge(s);
} }
static int snapshot_status(struct dm_target *ti, status_type_t type, static void snapshot_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen) char *result, unsigned int maxlen)
{ {
unsigned sz = 0; unsigned sz = 0;
...@@ -1892,8 +1892,6 @@ static int snapshot_status(struct dm_target *ti, status_type_t type, ...@@ -1892,8 +1892,6 @@ static int snapshot_status(struct dm_target *ti, status_type_t type,
maxlen - sz); maxlen - sz);
break; break;
} }
return 0;
} }
static int snapshot_iterate_devices(struct dm_target *ti, static int snapshot_iterate_devices(struct dm_target *ti,
...@@ -2148,7 +2146,7 @@ static void origin_resume(struct dm_target *ti) ...@@ -2148,7 +2146,7 @@ static void origin_resume(struct dm_target *ti)
ti->split_io = get_origin_minimum_chunksize(dev->bdev); ti->split_io = get_origin_minimum_chunksize(dev->bdev);
} }
static int origin_status(struct dm_target *ti, status_type_t type, char *result, static void origin_status(struct dm_target *ti, status_type_t type, char *result,
unsigned int maxlen) unsigned int maxlen)
{ {
struct dm_dev *dev = ti->private; struct dm_dev *dev = ti->private;
...@@ -2162,8 +2160,6 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result, ...@@ -2162,8 +2160,6 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result,
snprintf(result, maxlen, "%s", dev->name); snprintf(result, maxlen, "%s", dev->name);
break; break;
} }
return 0;
} }
static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm, static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
......
...@@ -302,7 +302,7 @@ static int stripe_map(struct dm_target *ti, struct bio *bio, ...@@ -302,7 +302,7 @@ static int stripe_map(struct dm_target *ti, struct bio *bio,
* *
*/ */
static int stripe_status(struct dm_target *ti, static void stripe_status(struct dm_target *ti,
status_type_t type, char *result, unsigned int maxlen) status_type_t type, char *result, unsigned int maxlen)
{ {
struct stripe_c *sc = (struct stripe_c *) ti->private; struct stripe_c *sc = (struct stripe_c *) ti->private;
...@@ -330,7 +330,6 @@ static int stripe_status(struct dm_target *ti, ...@@ -330,7 +330,6 @@ static int stripe_status(struct dm_target *ti,
(unsigned long long)sc->stripe[i].physical_start); (unsigned long long)sc->stripe[i].physical_start);
break; break;
} }
return 0;
} }
static int stripe_end_io(struct dm_target *ti, struct bio *bio, static int stripe_end_io(struct dm_target *ti, struct bio *bio,
......
...@@ -2325,7 +2325,7 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv) ...@@ -2325,7 +2325,7 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
* <transaction id> <used metadata sectors>/<total metadata sectors> * <transaction id> <used metadata sectors>/<total metadata sectors>
* <used data sectors>/<total data sectors> <held metadata root> * <used data sectors>/<total data sectors> <held metadata root>
*/ */
static int pool_status(struct dm_target *ti, status_type_t type, static void pool_status(struct dm_target *ti, status_type_t type,
char *result, unsigned maxlen) char *result, unsigned maxlen)
{ {
int r, count; int r, count;
...@@ -2343,32 +2343,41 @@ static int pool_status(struct dm_target *ti, status_type_t type, ...@@ -2343,32 +2343,41 @@ static int pool_status(struct dm_target *ti, status_type_t type,
switch (type) { switch (type) {
case STATUSTYPE_INFO: case STATUSTYPE_INFO:
r = dm_pool_get_metadata_transaction_id(pool->pmd, r = dm_pool_get_metadata_transaction_id(pool->pmd, &transaction_id);
&transaction_id); if (r) {
if (r) DMERR("dm_pool_get_metadata_transaction_id returned %d", r);
return r; goto err;
}
r = dm_pool_get_free_metadata_block_count(pool->pmd, r = dm_pool_get_free_metadata_block_count(pool->pmd, &nr_free_blocks_metadata);
&nr_free_blocks_metadata); if (r) {
if (r) DMERR("dm_pool_get_free_metadata_block_count returned %d", r);
return r; goto err;
}
r = dm_pool_get_metadata_dev_size(pool->pmd, &nr_blocks_metadata); r = dm_pool_get_metadata_dev_size(pool->pmd, &nr_blocks_metadata);
if (r) if (r) {
return r; DMERR("dm_pool_get_metadata_dev_size returned %d", r);
goto err;
}
r = dm_pool_get_free_block_count(pool->pmd, r = dm_pool_get_free_block_count(pool->pmd, &nr_free_blocks_data);
&nr_free_blocks_data); if (r) {
if (r) DMERR("dm_pool_get_free_block_count returned %d", r);
return r; goto err;
}
r = dm_pool_get_data_dev_size(pool->pmd, &nr_blocks_data); r = dm_pool_get_data_dev_size(pool->pmd, &nr_blocks_data);
if (r) if (r) {
return r; DMERR("dm_pool_get_data_dev_size returned %d", r);
goto err;
}
r = dm_pool_get_held_metadata_root(pool->pmd, &held_root); r = dm_pool_get_held_metadata_root(pool->pmd, &held_root);
if (r) if (r) {
return r; DMERR("dm_pool_get_metadata_snap returned %d", r);
goto err;
}
DMEMIT("%llu %llu/%llu %llu/%llu ", DMEMIT("%llu %llu/%llu %llu/%llu ",
(unsigned long long)transaction_id, (unsigned long long)transaction_id,
...@@ -2406,8 +2415,10 @@ static int pool_status(struct dm_target *ti, status_type_t type, ...@@ -2406,8 +2415,10 @@ static int pool_status(struct dm_target *ti, status_type_t type,
break; break;
} }
return;
return 0; err:
DMEMIT("Error");
} }
static int pool_iterate_devices(struct dm_target *ti, static int pool_iterate_devices(struct dm_target *ti,
...@@ -2659,7 +2670,7 @@ static void thin_postsuspend(struct dm_target *ti) ...@@ -2659,7 +2670,7 @@ static void thin_postsuspend(struct dm_target *ti)
/* /*
* <nr mapped sectors> <highest mapped sector> * <nr mapped sectors> <highest mapped sector>
*/ */
static int thin_status(struct dm_target *ti, status_type_t type, static void thin_status(struct dm_target *ti, status_type_t type,
char *result, unsigned maxlen) char *result, unsigned maxlen)
{ {
int r; int r;
...@@ -2674,12 +2685,16 @@ static int thin_status(struct dm_target *ti, status_type_t type, ...@@ -2674,12 +2685,16 @@ static int thin_status(struct dm_target *ti, status_type_t type,
switch (type) { switch (type) {
case STATUSTYPE_INFO: case STATUSTYPE_INFO:
r = dm_thin_get_mapped_count(tc->td, &mapped); r = dm_thin_get_mapped_count(tc->td, &mapped);
if (r) if (r) {
return r; DMERR("dm_thin_get_mapped_count returned %d", r);
goto err;
}
r = dm_thin_get_highest_mapped_block(tc->td, &highest); r = dm_thin_get_highest_mapped_block(tc->td, &highest);
if (r < 0) if (r < 0) {
return r; DMERR("dm_thin_get_highest_mapped_block returned %d", r);
goto err;
}
DMEMIT("%llu ", mapped * tc->pool->sectors_per_block); DMEMIT("%llu ", mapped * tc->pool->sectors_per_block);
if (r) if (r)
...@@ -2699,7 +2714,10 @@ static int thin_status(struct dm_target *ti, status_type_t type, ...@@ -2699,7 +2714,10 @@ static int thin_status(struct dm_target *ti, status_type_t type,
} }
} }
return 0; return;
err:
DMEMIT("Error");
} }
static int thin_iterate_devices(struct dm_target *ti, static int thin_iterate_devices(struct dm_target *ti,
......
...@@ -514,7 +514,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio, ...@@ -514,7 +514,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio,
/* /*
* Status: V (valid) or C (corruption found) * Status: V (valid) or C (corruption found)
*/ */
static int verity_status(struct dm_target *ti, status_type_t type, static void verity_status(struct dm_target *ti, status_type_t type,
char *result, unsigned maxlen) char *result, unsigned maxlen)
{ {
struct dm_verity *v = ti->private; struct dm_verity *v = ti->private;
...@@ -546,8 +546,6 @@ static int verity_status(struct dm_target *ti, status_type_t type, ...@@ -546,8 +546,6 @@ static int verity_status(struct dm_target *ti, status_type_t type,
DMEMIT("%02x", v->salt[x]); DMEMIT("%02x", v->salt[x]);
break; break;
} }
return 0;
} }
static int verity_ioctl(struct dm_target *ti, unsigned cmd, static int verity_ioctl(struct dm_target *ti, unsigned cmd,
......
...@@ -72,7 +72,7 @@ typedef void (*dm_postsuspend_fn) (struct dm_target *ti); ...@@ -72,7 +72,7 @@ typedef void (*dm_postsuspend_fn) (struct dm_target *ti);
typedef int (*dm_preresume_fn) (struct dm_target *ti); typedef int (*dm_preresume_fn) (struct dm_target *ti);
typedef void (*dm_resume_fn) (struct dm_target *ti); typedef void (*dm_resume_fn) (struct dm_target *ti);
typedef int (*dm_status_fn) (struct dm_target *ti, status_type_t status_type, typedef void (*dm_status_fn) (struct dm_target *ti, status_type_t status_type,
char *result, unsigned int maxlen); char *result, unsigned int maxlen);
typedef int (*dm_message_fn) (struct dm_target *ti, unsigned argc, char **argv); typedef int (*dm_message_fn) (struct dm_target *ti, unsigned argc, char **argv);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment