aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWenwen Wang <wang6495@umn.edu>2018-10-03 12:43:59 -0400
committerMike Snitzer <snitzer@redhat.com>2018-10-18 11:54:07 -0400
commit800a7340ab7dd667edf95e74d8e4f23a17e87076 (patch)
tree661f1c6576b6862811a74b5431500c1487943b2f
parentbab5d988841e58fec6ae22f486905ddde2d715f4 (diff)
dm ioctl: harden copy_params()'s copy_from_user() from malicious users
In copy_params(), the struct 'dm_ioctl' is first copied from the user space buffer 'user' to 'param_kernel' and the field 'data_size' is checked against 'minimum_data_size' (size of 'struct dm_ioctl' payload up to its 'data' member). If the check fails, an error code EINVAL will be returned. Otherwise, param_kernel->data_size is used to do a second copy, which copies from the same user-space buffer to 'dmi'. After the second copy, only 'dmi->data_size' is checked against 'param_kernel->data_size'. Given that the buffer 'user' resides in the user space, a malicious user-space process can race to change the content in the buffer between the two copies. This way, the attacker can inject inconsistent data into 'dmi' (versus previously validated 'param_kernel'). Fix redundant copying of 'minimum_data_size' from user-space buffer by using the first copy stored in 'param_kernel'. Also remove the 'data_size' check after the second copy because it is now unnecessary. Cc: stable@vger.kernel.org Signed-off-by: Wenwen Wang <wang6495@umn.edu> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--drivers/md/dm-ioctl.c18
1 files changed, 6 insertions, 12 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index b810ea77e6b1..f666778ad237 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1720,8 +1720,7 @@ static void free_params(struct dm_ioctl *param, size_t param_size, int param_fla
1720} 1720}
1721 1721
1722static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel, 1722static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel,
1723 int ioctl_flags, 1723 int ioctl_flags, struct dm_ioctl **param, int *param_flags)
1724 struct dm_ioctl **param, int *param_flags)
1725{ 1724{
1726 struct dm_ioctl *dmi; 1725 struct dm_ioctl *dmi;
1727 int secure_data; 1726 int secure_data;
@@ -1762,18 +1761,13 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kern
1762 1761
1763 *param_flags |= DM_PARAMS_MALLOC; 1762 *param_flags |= DM_PARAMS_MALLOC;
1764 1763
1765 if (copy_from_user(dmi, user, param_kernel->data_size)) 1764 /* Copy from param_kernel (which was already copied from user) */
1766 goto bad; 1765 memcpy(dmi, param_kernel, minimum_data_size);
1767 1766
1768data_copied: 1767 if (copy_from_user(&dmi->data, (char __user *)user + minimum_data_size,
1769 /* 1768 param_kernel->data_size - minimum_data_size))
1770 * Abort if something changed the ioctl data while it was being copied.
1771 */
1772 if (dmi->data_size != param_kernel->data_size) {
1773 DMERR("rejecting ioctl: data size modified while processing parameters");
1774 goto bad; 1769 goto bad;
1775 } 1770data_copied:
1776
1777 /* Wipe the user buffer so we do not return it to userspace */ 1771 /* Wipe the user buffer so we do not return it to userspace */
1778 if (secure_data && clear_user(user, param_kernel->data_size)) 1772 if (secure_data && clear_user(user, param_kernel->data_size))
1779 goto bad; 1773 goto bad;