Merge git://git.infradead.org/battery-2.6
[pandora-kernel.git] / drivers / md / dm-ioctl.c
index 6d12775..4cacdad 100644 (file)
@@ -1501,14 +1501,10 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user)
        return r;
 }
 
-static void free_params(struct dm_ioctl *param)
-{
-       vfree(param);
-}
-
 static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param)
 {
        struct dm_ioctl tmp, *dmi;
+       int secure_data;
 
        if (copy_from_user(&tmp, user, sizeof(tmp) - sizeof(tmp.data)))
                return -EFAULT;
@@ -1516,17 +1512,30 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param)
        if (tmp.data_size < (sizeof(tmp) - sizeof(tmp.data)))
                return -EINVAL;
 
+       secure_data = tmp.flags & DM_SECURE_DATA_FLAG;
+
        dmi = vmalloc(tmp.data_size);
-       if (!dmi)
+       if (!dmi) {
+               if (secure_data && clear_user(user, tmp.data_size))
+                       return -EFAULT;
                return -ENOMEM;
-
-       if (copy_from_user(dmi, user, tmp.data_size)) {
-               vfree(dmi);
-               return -EFAULT;
        }
 
+       if (copy_from_user(dmi, user, tmp.data_size))
+               goto bad;
+
+       /* Wipe the user buffer so we do not return it to userspace */
+       if (secure_data && clear_user(user, tmp.data_size))
+               goto bad;
+
        *param = dmi;
        return 0;
+
+bad:
+       if (secure_data)
+               memset(dmi, 0, tmp.data_size);
+       vfree(dmi);
+       return -EFAULT;
 }
 
 static int validate_params(uint cmd, struct dm_ioctl *param)
@@ -1534,6 +1543,7 @@ static int validate_params(uint cmd, struct dm_ioctl *param)
        /* Always clear this flag */
        param->flags &= ~DM_BUFFER_FULL_FLAG;
        param->flags &= ~DM_UEVENT_GENERATED_FLAG;
+       param->flags &= ~DM_SECURE_DATA_FLAG;
 
        /* Ignores parameters */
        if (cmd == DM_REMOVE_ALL_CMD ||
@@ -1561,10 +1571,11 @@ static int validate_params(uint cmd, struct dm_ioctl *param)
 static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
 {
        int r = 0;
+       int wipe_buffer;
        unsigned int cmd;
        struct dm_ioctl *uninitialized_var(param);
        ioctl_fn fn = NULL;
-       size_t param_size;
+       size_t input_param_size;
 
        /* only root can play with this */
        if (!capable(CAP_SYS_ADMIN))
@@ -1611,13 +1622,15 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
        if (r)
                return r;
 
+       input_param_size = param->data_size;
+       wipe_buffer = param->flags & DM_SECURE_DATA_FLAG;
+
        r = validate_params(cmd, param);
        if (r)
                goto out;
 
-       param_size = param->data_size;
        param->data_size = sizeof(*param);
-       r = fn(param, param_size);
+       r = fn(param, input_param_size);
 
        /*
         * Copy the results back to userland.
@@ -1625,8 +1638,11 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
        if (!r && copy_to_user(user, param, param->data_size))
                r = -EFAULT;
 
- out:
-       free_params(param);
+out:
+       if (wipe_buffer)
+               memset(param, 0, input_param_size);
+
+       vfree(param);
        return r;
 }