diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 3a8f6a0e6eb5..4cacdad2270a 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1504,6 +1504,7 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user) 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; @@ -1511,17 +1512,28 @@ 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)) 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; } @@ -1531,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 || @@ -1558,6 +1571,7 @@ 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; @@ -1602,13 +1616,15 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user) * Copy the parameters into kernel space. */ r = copy_params(user, ¶m); - input_param_size = param->data_size; current->flags &= ~PF_MEMALLOC; 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; @@ -1623,6 +1639,9 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user) r = -EFAULT; out: + if (wipe_buffer) + memset(param, 0, input_param_size); + vfree(param); return r; } diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h index 78bbf47bbb96..3708455ee6c3 100644 --- a/include/linux/dm-ioctl.h +++ b/include/linux/dm-ioctl.h @@ -267,9 +267,9 @@ enum { #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 19 -#define DM_VERSION_PATCHLEVEL 1 -#define DM_VERSION_EXTRA "-ioctl (2011-01-07)" +#define DM_VERSION_MINOR 20 +#define DM_VERSION_PATCHLEVEL 0 +#define DM_VERSION_EXTRA "-ioctl (2011-02-02)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ @@ -328,4 +328,10 @@ enum { */ #define DM_UUID_FLAG (1 << 14) /* In */ +/* + * If set, all buffers are wiped after use. Use when sending + * or requesting sensitive data such as an encryption key. + */ +#define DM_SECURE_DATA_FLAG (1 << 15) /* In */ + #endif /* _LINUX_DM_IOCTL_H */