aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r--drivers/md/dm-ioctl.c52
1 files changed, 34 insertions, 18 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 9ae11b2994f8..7eb0682d574f 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1560,7 +1560,8 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user)
1560 return r; 1560 return r;
1561} 1561}
1562 1562
1563#define DM_PARAMS_VMALLOC 0x0001 /* Params alloced with vmalloc not kmalloc */ 1563#define DM_PARAMS_KMALLOC 0x0001 /* Params alloced with kmalloc */
1564#define DM_PARAMS_VMALLOC 0x0002 /* Params alloced with vmalloc */
1564#define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */ 1565#define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */
1565 1566
1566static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags) 1567static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags)
@@ -1568,66 +1569,80 @@ static void free_params(struct dm_ioctl *param, size_t param_size, int param_fla
1568 if (param_flags & DM_WIPE_BUFFER) 1569 if (param_flags & DM_WIPE_BUFFER)
1569 memset(param, 0, param_size); 1570 memset(param, 0, param_size);
1570 1571
1572 if (param_flags & DM_PARAMS_KMALLOC)
1573 kfree(param);
1571 if (param_flags & DM_PARAMS_VMALLOC) 1574 if (param_flags & DM_PARAMS_VMALLOC)
1572 vfree(param); 1575 vfree(param);
1573 else
1574 kfree(param);
1575} 1576}
1576 1577
1577static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param, int *param_flags) 1578static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel,
1579 int ioctl_flags,
1580 struct dm_ioctl **param, int *param_flags)
1578{ 1581{
1579 struct dm_ioctl tmp, *dmi; 1582 struct dm_ioctl *dmi;
1580 int secure_data; 1583 int secure_data;
1584 const size_t minimum_data_size = sizeof(*param_kernel) - sizeof(param_kernel->data);
1581 1585
1582 if (copy_from_user(&tmp, user, sizeof(tmp) - sizeof(tmp.data))) 1586 if (copy_from_user(param_kernel, user, minimum_data_size))
1583 return -EFAULT; 1587 return -EFAULT;
1584 1588
1585 if (tmp.data_size < (sizeof(tmp) - sizeof(tmp.data))) 1589 if (param_kernel->data_size < minimum_data_size)
1586 return -EINVAL; 1590 return -EINVAL;
1587 1591
1588 secure_data = tmp.flags & DM_SECURE_DATA_FLAG; 1592 secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG;
1589 1593
1590 *param_flags = secure_data ? DM_WIPE_BUFFER : 0; 1594 *param_flags = secure_data ? DM_WIPE_BUFFER : 0;
1591 1595
1596 if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) {
1597 dmi = param_kernel;
1598 dmi->data_size = minimum_data_size;
1599 goto data_copied;
1600 }
1601
1592 /* 1602 /*
1593 * Try to avoid low memory issues when a device is suspended. 1603 * Try to avoid low memory issues when a device is suspended.
1594 * Use kmalloc() rather than vmalloc() when we can. 1604 * Use kmalloc() rather than vmalloc() when we can.
1595 */ 1605 */
1596 dmi = NULL; 1606 dmi = NULL;
1597 if (tmp.data_size <= KMALLOC_MAX_SIZE) 1607 if (param_kernel->data_size <= KMALLOC_MAX_SIZE) {
1598 dmi = kmalloc(tmp.data_size, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); 1608 dmi = kmalloc(param_kernel->data_size, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
1609 if (dmi)
1610 *param_flags |= DM_PARAMS_KMALLOC;
1611 }
1599 1612
1600 if (!dmi) { 1613 if (!dmi) {
1601 dmi = __vmalloc(tmp.data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH, PAGE_KERNEL); 1614 dmi = __vmalloc(param_kernel->data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH, PAGE_KERNEL);
1602 *param_flags |= DM_PARAMS_VMALLOC; 1615 if (dmi)
1616 *param_flags |= DM_PARAMS_VMALLOC;
1603 } 1617 }
1604 1618
1605 if (!dmi) { 1619 if (!dmi) {
1606 if (secure_data && clear_user(user, tmp.data_size)) 1620 if (secure_data && clear_user(user, param_kernel->data_size))
1607 return -EFAULT; 1621 return -EFAULT;
1608 return -ENOMEM; 1622 return -ENOMEM;
1609 } 1623 }
1610 1624
1611 if (copy_from_user(dmi, user, tmp.data_size)) 1625 if (copy_from_user(dmi, user, param_kernel->data_size))
1612 goto bad; 1626 goto bad;
1613 1627
1628data_copied:
1614 /* 1629 /*
1615 * Abort if something changed the ioctl data while it was being copied. 1630 * Abort if something changed the ioctl data while it was being copied.
1616 */ 1631 */
1617 if (dmi->data_size != tmp.data_size) { 1632 if (dmi->data_size != param_kernel->data_size) {
1618 DMERR("rejecting ioctl: data size modified while processing parameters"); 1633 DMERR("rejecting ioctl: data size modified while processing parameters");
1619 goto bad; 1634 goto bad;
1620 } 1635 }
1621 1636
1622 /* Wipe the user buffer so we do not return it to userspace */ 1637 /* Wipe the user buffer so we do not return it to userspace */
1623 if (secure_data && clear_user(user, tmp.data_size)) 1638 if (secure_data && clear_user(user, param_kernel->data_size))
1624 goto bad; 1639 goto bad;
1625 1640
1626 *param = dmi; 1641 *param = dmi;
1627 return 0; 1642 return 0;
1628 1643
1629bad: 1644bad:
1630 free_params(dmi, tmp.data_size, *param_flags); 1645 free_params(dmi, param_kernel->data_size, *param_flags);
1631 1646
1632 return -EFAULT; 1647 return -EFAULT;
1633} 1648}
@@ -1671,6 +1686,7 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
1671 struct dm_ioctl *uninitialized_var(param); 1686 struct dm_ioctl *uninitialized_var(param);
1672 ioctl_fn fn = NULL; 1687 ioctl_fn fn = NULL;
1673 size_t input_param_size; 1688 size_t input_param_size;
1689 struct dm_ioctl param_kernel;
1674 1690
1675 /* only root can play with this */ 1691 /* only root can play with this */
1676 if (!capable(CAP_SYS_ADMIN)) 1692 if (!capable(CAP_SYS_ADMIN))
@@ -1704,7 +1720,7 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
1704 /* 1720 /*
1705 * Copy the parameters into kernel space. 1721 * Copy the parameters into kernel space.
1706 */ 1722 */
1707 r = copy_params(user, &param, &param_flags); 1723 r = copy_params(user, &param_kernel, ioctl_flags, &param, &param_flags);
1708 1724
1709 if (r) 1725 if (r)
1710 return r; 1726 return r;