Instead of doing a prescan to determine the length of buffer required,
checking the supplied buffer is big enough, and then doing a second
scan to fill the output buffer just do a single scan and detect when
the buffer is too short.

This removes any problems that might occur if a 'target' is added
between the scans.

For additional safety only call strlen(tt->name) once and use the
returned length for everything (incuding the copy).
Ensure than all the pad bytes between the entries are zero.

Set param->data_size to the actual size of the data.
It was slightly large because ALIGN_MASK was added in instead of the size
being rounded up.

Signed-off-by: David Laight <[email protected]>
---

This seems to be the only code that uses dm_target_iterate() and the
dm_get_target_type() call is just a linear scan of the same list that
replaces the lock() with MODULE_GET().
It would all be simpler with a loop here that scanned the list,
but the required locking primitives aren't exposed.
Changing that is a larger change that I wanted to do now.

 drivers/md/dm-ioctl.c | 73 +++++++++++++++++--------------------------
 1 file changed, 29 insertions(+), 44 deletions(-)

diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a529174c94cf..6234cb8b86b7 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -54,10 +54,8 @@ struct hash_cell {
 };
 
 struct vers_iter {
-       size_t param_size;
        struct dm_target_versions *vers, *old_vers;
        char *end;
-       uint32_t flags;
 };
 
 
@@ -684,41 +682,38 @@ static int list_devices(struct file *filp, struct 
dm_ioctl *param, size_t param_
        return 0;
 }
 
-static void list_version_get_needed(struct target_type *tt, void *needed_param)
-{
-       size_t *needed = needed_param;
-
-       *needed += sizeof(struct dm_target_versions);
-       *needed += strlen(tt->name) + 1;
-       *needed += ALIGN_MASK;
-}
-
 static void list_version_get_info(struct target_type *tt, void *param)
 {
        struct vers_iter *info = param;
+       struct dm_target_versions *vers = info->vers;
+       size_t name_len = strlen(tt->name);
+
+       if (!vers)
+               return;
 
-       /* Check space - it might have changed since the first iteration */
-       if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > 
info->end) {
-               info->flags = DM_BUFFER_FULL_FLAG;
+       info->old_vers = vers;
+       info->vers = align_ptr((void *)(info->vers + 1) + name_len + 1);
+
+       /* Check space */
+       if ((char *)info->vers > info->end) {
+               info->vers = NULL;
                return;
        }
 
-       if (info->old_vers)
-               info->old_vers->next = (uint32_t) ((void *)info->vers - (void 
*)info->old_vers);
+       /* Zero padding and terminate vers->name[] */
+       ((u64 *)info->vers)[-1] = 0;
 
-       info->vers->version[0] = tt->version[0];
-       info->vers->version[1] = tt->version[1];
-       info->vers->version[2] = tt->version[2];
-       info->vers->next = 0;
-       strcpy(info->vers->name, tt->name);
+       vers->next = (char *)info->vers - (char *)vers;
 
-       info->old_vers = info->vers;
-       info->vers = align_ptr((void *)(info->vers + 1) + strlen(tt->name) + 1);
+       vers->version[0] = tt->version[0];
+       vers->version[1] = tt->version[1];
+       vers->version[2] = tt->version[2];
+       memcpy(vers->name, tt->name, name_len);
 }
 
 static int __list_versions(struct dm_ioctl *param, size_t param_size, const 
char *name)
 {
-       size_t len, needed = 0;
+       size_t len;
        struct dm_target_versions *vers;
        struct vers_iter iter_info;
        struct target_type *tt = NULL;
@@ -729,41 +724,31 @@ static int __list_versions(struct dm_ioctl *param, size_t 
param_size, const char
                        return -EINVAL;
        }
 
-       /*
-        * Loop through all the devices working out how much
-        * space we need.
-        */
-       if (!tt)
-               dm_target_iterate(list_version_get_needed, &needed);
-       else
-               list_version_get_needed(tt, &needed);
-
        /*
         * Grab our output buffer.
         */
        vers = get_result_buffer(param, param_size, &len);
-       if (len < needed) {
-               param->flags |= DM_BUFFER_FULL_FLAG;
-               goto out;
-       }
-       param->data_size = param->data_start + needed;
 
-       iter_info.param_size = param_size;
        iter_info.old_vers = NULL;
        iter_info.vers = vers;
-       iter_info.flags = 0;
-       iter_info.end = (char *)vers + needed;
+       iter_info.end = (char *)vers + len;
 
        /*
-        * Now loop through filling out the names & versions.
+        * Loop through filling out the names & versions.
         */
        if (!tt)
                dm_target_iterate(list_version_get_info, &iter_info);
        else
                list_version_get_info(tt, &iter_info);
-       param->flags |= iter_info.flags;
 
- out:
+       if (iter_info.vers) {
+               if (iter_info.old_vers)
+                       iter_info.old_vers->next = 0;
+               param->data_size = param->data_start + ((char *)iter_info.vers 
- (char *)vers);
+       } else {
+               param->flags |= DM_BUFFER_FULL_FLAG;
+       }
+
        if (tt)
                dm_put_target_type(tt);
        return 0;
-- 
2.39.5


Reply via email to