summaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-ioctl.c
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2013-03-01 17:45:44 -0500
committerAlasdair G Kergon <agk@redhat.com>2013-03-01 17:45:44 -0500
commitfd7c092e711ebab55b2688d3859d95dfd0301f73 (patch)
tree3cc99f96f4a2de8e22347feb86b0ecd5dd7200d0 /drivers/md/dm-ioctl.c
parent16245bdc9d3e22d1460341a655c8b5288953bc14 (diff)
dm: fix truncated status strings
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. Cc: stable@vger.kernel.org Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r--drivers/md/dm-ioctl.c14
1 files changed, 9 insertions, 5 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 0666b5d14b88..eee353da3742 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1067,6 +1067,7 @@ static void retrieve_status(struct dm_table *table,
1067 num_targets = dm_table_get_num_targets(table); 1067 num_targets = dm_table_get_num_targets(table);
1068 for (i = 0; i < num_targets; i++) { 1068 for (i = 0; i < num_targets; i++) {
1069 struct dm_target *ti = dm_table_get_target(table, i); 1069 struct dm_target *ti = dm_table_get_target(table, i);
1070 size_t l;
1070 1071
1071 remaining = len - (outptr - outbuf); 1072 remaining = len - (outptr - outbuf);
1072 if (remaining <= sizeof(struct dm_target_spec)) { 1073 if (remaining <= sizeof(struct dm_target_spec)) {
@@ -1093,14 +1094,17 @@ static void retrieve_status(struct dm_table *table,
1093 if (ti->type->status) { 1094 if (ti->type->status) {
1094 if (param->flags & DM_NOFLUSH_FLAG) 1095 if (param->flags & DM_NOFLUSH_FLAG)
1095 status_flags |= DM_STATUS_NOFLUSH_FLAG; 1096 status_flags |= DM_STATUS_NOFLUSH_FLAG;
1096 if (ti->type->status(ti, type, status_flags, outptr, remaining)) { 1097 ti->type->status(ti, type, status_flags, outptr, remaining);
1097 param->flags |= DM_BUFFER_FULL_FLAG;
1098 break;
1099 }
1100 } else 1098 } else
1101 outptr[0] = '\0'; 1099 outptr[0] = '\0';
1102 1100
1103 outptr += strlen(outptr) + 1; 1101 l = strlen(outptr) + 1;
1102 if (l == remaining) {
1103 param->flags |= DM_BUFFER_FULL_FLAG;
1104 break;
1105 }
1106
1107 outptr += l;
1104 used = param->data_start + (outptr - outbuf); 1108 used = param->data_start + (outptr - outbuf);
1105 1109
1106 outptr = align_ptr(outptr); 1110 outptr = align_ptr(outptr);