diff options
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r-- | drivers/md/dm-ioctl.c | 64 |
1 files changed, 43 insertions, 21 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index afd95986d099..0666b5d14b88 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
@@ -1543,7 +1543,21 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user) | |||
1543 | return r; | 1543 | return r; |
1544 | } | 1544 | } |
1545 | 1545 | ||
1546 | static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param) | 1546 | #define DM_PARAMS_VMALLOC 0x0001 /* Params alloced with vmalloc not kmalloc */ |
1547 | #define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */ | ||
1548 | |||
1549 | static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags) | ||
1550 | { | ||
1551 | if (param_flags & DM_WIPE_BUFFER) | ||
1552 | memset(param, 0, param_size); | ||
1553 | |||
1554 | if (param_flags & DM_PARAMS_VMALLOC) | ||
1555 | vfree(param); | ||
1556 | else | ||
1557 | kfree(param); | ||
1558 | } | ||
1559 | |||
1560 | static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param, int *param_flags) | ||
1547 | { | 1561 | { |
1548 | struct dm_ioctl tmp, *dmi; | 1562 | struct dm_ioctl tmp, *dmi; |
1549 | int secure_data; | 1563 | int secure_data; |
@@ -1556,7 +1570,21 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param) | |||
1556 | 1570 | ||
1557 | secure_data = tmp.flags & DM_SECURE_DATA_FLAG; | 1571 | secure_data = tmp.flags & DM_SECURE_DATA_FLAG; |
1558 | 1572 | ||
1559 | dmi = vmalloc(tmp.data_size); | 1573 | *param_flags = secure_data ? DM_WIPE_BUFFER : 0; |
1574 | |||
1575 | /* | ||
1576 | * Try to avoid low memory issues when a device is suspended. | ||
1577 | * Use kmalloc() rather than vmalloc() when we can. | ||
1578 | */ | ||
1579 | dmi = NULL; | ||
1580 | if (tmp.data_size <= KMALLOC_MAX_SIZE) | ||
1581 | dmi = kmalloc(tmp.data_size, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); | ||
1582 | |||
1583 | if (!dmi) { | ||
1584 | dmi = __vmalloc(tmp.data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH, PAGE_KERNEL); | ||
1585 | *param_flags |= DM_PARAMS_VMALLOC; | ||
1586 | } | ||
1587 | |||
1560 | if (!dmi) { | 1588 | if (!dmi) { |
1561 | if (secure_data && clear_user(user, tmp.data_size)) | 1589 | if (secure_data && clear_user(user, tmp.data_size)) |
1562 | return -EFAULT; | 1590 | return -EFAULT; |
@@ -1566,6 +1594,14 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param) | |||
1566 | if (copy_from_user(dmi, user, tmp.data_size)) | 1594 | if (copy_from_user(dmi, user, tmp.data_size)) |
1567 | goto bad; | 1595 | goto bad; |
1568 | 1596 | ||
1597 | /* | ||
1598 | * Abort if something changed the ioctl data while it was being copied. | ||
1599 | */ | ||
1600 | if (dmi->data_size != tmp.data_size) { | ||
1601 | DMERR("rejecting ioctl: data size modified while processing parameters"); | ||
1602 | goto bad; | ||
1603 | } | ||
1604 | |||
1569 | /* Wipe the user buffer so we do not return it to userspace */ | 1605 | /* Wipe the user buffer so we do not return it to userspace */ |
1570 | if (secure_data && clear_user(user, tmp.data_size)) | 1606 | if (secure_data && clear_user(user, tmp.data_size)) |
1571 | goto bad; | 1607 | goto bad; |
@@ -1574,9 +1610,8 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param) | |||
1574 | return 0; | 1610 | return 0; |
1575 | 1611 | ||
1576 | bad: | 1612 | bad: |
1577 | if (secure_data) | 1613 | free_params(dmi, tmp.data_size, *param_flags); |
1578 | memset(dmi, 0, tmp.data_size); | 1614 | |
1579 | vfree(dmi); | ||
1580 | return -EFAULT; | 1615 | return -EFAULT; |
1581 | } | 1616 | } |
1582 | 1617 | ||
@@ -1613,7 +1648,7 @@ static int validate_params(uint cmd, struct dm_ioctl *param) | |||
1613 | static int ctl_ioctl(uint command, struct dm_ioctl __user *user) | 1648 | static int ctl_ioctl(uint command, struct dm_ioctl __user *user) |
1614 | { | 1649 | { |
1615 | int r = 0; | 1650 | int r = 0; |
1616 | int wipe_buffer; | 1651 | int param_flags; |
1617 | unsigned int cmd; | 1652 | unsigned int cmd; |
1618 | struct dm_ioctl *uninitialized_var(param); | 1653 | struct dm_ioctl *uninitialized_var(param); |
1619 | ioctl_fn fn = NULL; | 1654 | ioctl_fn fn = NULL; |
@@ -1649,24 +1684,14 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user) | |||
1649 | } | 1684 | } |
1650 | 1685 | ||
1651 | /* | 1686 | /* |
1652 | * Trying to avoid low memory issues when a device is | ||
1653 | * suspended. | ||
1654 | */ | ||
1655 | current->flags |= PF_MEMALLOC; | ||
1656 | |||
1657 | /* | ||
1658 | * Copy the parameters into kernel space. | 1687 | * Copy the parameters into kernel space. |
1659 | */ | 1688 | */ |
1660 | r = copy_params(user, ¶m); | 1689 | r = copy_params(user, ¶m, ¶m_flags); |
1661 | |||
1662 | current->flags &= ~PF_MEMALLOC; | ||
1663 | 1690 | ||
1664 | if (r) | 1691 | if (r) |
1665 | return r; | 1692 | return r; |
1666 | 1693 | ||
1667 | input_param_size = param->data_size; | 1694 | input_param_size = param->data_size; |
1668 | wipe_buffer = param->flags & DM_SECURE_DATA_FLAG; | ||
1669 | |||
1670 | r = validate_params(cmd, param); | 1695 | r = validate_params(cmd, param); |
1671 | if (r) | 1696 | if (r) |
1672 | goto out; | 1697 | goto out; |
@@ -1681,10 +1706,7 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user) | |||
1681 | r = -EFAULT; | 1706 | r = -EFAULT; |
1682 | 1707 | ||
1683 | out: | 1708 | out: |
1684 | if (wipe_buffer) | 1709 | free_params(param, input_param_size, param_flags); |
1685 | memset(param, 0, input_param_size); | ||
1686 | |||
1687 | vfree(param); | ||
1688 | return r; | 1710 | return r; |
1689 | } | 1711 | } |
1690 | 1712 | ||