History

dm ioctl: fix alignment of event number in the device list

The size of struct dm_name_list is different on 32-bit and 64-bit kernels (so "(nl + 1)" differs between 32-bit and 64-bit kernels). This mismatch caused some harmless difference in padding when using 32-bit or 64-bit kernel. Commit 23d70c5e52dd ("dm ioctl: report event number in DM_LIST_DEVICES") added reporting event number in the output of DM_LIST_DEVICES_CMD. This difference in padding makes it impossible for userspace to determine the location of the event number (the location would be different when running on 32-bit and 64-bit kernels). Fix the padding by using offsetof(struct dm_name_list, name) instead of sizeof(struct dm_name_list) to determine the location of entries. Also, the ioctl version number is incremented to 37 so that userspace can use the version number to determine that the event number is present and correctly located. In addition, a global event is now raised when a DM device is created, removed, renamed or when table is swapped, so that the user can monitor for device changes.

Mikulas Patocka committed 9 years ago

Commit 62e08241 parent e19b2054 files · +35 −17Browse files

Unified · Split

4 files
  1. drivers/md/dm-core.h +1 −0
  2. drivers/md/dm-ioctl.c +24 −13
  3. drivers/md/dm.c +8 −2
  4. include/uapi/linux/dm-ioctl.h +2 −2
drivers/md/dm-core.h+1−0
@@ -149,5 +149,6 @@ static inline bool dm_message_test_buffer_overflow(char *result, unsigned maxlen · 148 unmodified lines
149 149
150extern atomic_t dm_global_event_nr;150extern atomic_t dm_global_event_nr;
151extern wait_queue_head_t dm_global_eventq;151extern wait_queue_head_t dm_global_eventq;
152void dm_issue_global_event(void);
152 153
153#endif154#endif
drivers/md/dm-ioctl.c+24−13
@@ -477,9 +477,13 @@ static int remove_all(struct file *filp, struct dm_ioctl *param, size_t param_si · 476 unmodified lines
477 * Round up the ptr to an 8-byte boundary.477 * Round up the ptr to an 8-byte boundary.
478 */478 */
479#define ALIGN_MASK 7479#define ALIGN_MASK 7
480static inline size_t align_val(size_t val)
481{
482 return (val + ALIGN_MASK) & ~ALIGN_MASK;
483}
480static inline void *align_ptr(void *ptr)484static inline void *align_ptr(void *ptr)
481{485{
482 return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK);486 return (void *)align_val((size_t)ptr);
483}487}
484 488
485/*489/*
@@ -505,7 +509,7 @@ static int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_ · 19 unmodified lines
505 struct hash_cell *hc;509 struct hash_cell *hc;
506 size_t len, needed = 0;510 size_t len, needed = 0;
507 struct gendisk *disk;511 struct gendisk *disk;
508 struct dm_name_list *nl, *old_nl = NULL;512 struct dm_name_list *orig_nl, *nl, *old_nl = NULL;
509 uint32_t *event_nr;513 uint32_t *event_nr;
510 514
511 down_write(&_hash_lock);515 down_write(&_hash_lock);
@@ -516,17 +520,15 @@ static int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_ · 4 unmodified lines
516 */520 */
517 for (i = 0; i < NUM_BUCKETS; i++) {521 for (i = 0; i < NUM_BUCKETS; i++) {
518 list_for_each_entry (hc, _name_buckets + i, name_list) {522 list_for_each_entry (hc, _name_buckets + i, name_list) {
519 needed += sizeof(struct dm_name_list);523 needed += align_val(offsetof(struct dm_name_list, name) + strlen(hc->name) + 1);
520 needed += strlen(hc->name) + 1;524 needed += align_val(sizeof(uint32_t));
521 needed += ALIGN_MASK;
522 needed += (sizeof(uint32_t) + ALIGN_MASK) & ~ALIGN_MASK;
523 }525 }
524 }526 }
525 527
526 /*528 /*
527 * Grab our output buffer.529 * Grab our output buffer.
528 */530 */
529 nl = get_result_buffer(param, param_size, &len);531 nl = orig_nl = get_result_buffer(param, param_size, &len);
530 if (len < needed) {532 if (len < needed) {
531 param->flags |= DM_BUFFER_FULL_FLAG;533 param->flags |= DM_BUFFER_FULL_FLAG;
532 goto out;534 goto out;
@@ -549,11 +551,16 @@ static int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_ · 16 unmodified lines
549 strcpy(nl->name, hc->name);551 strcpy(nl->name, hc->name);
550 552
551 old_nl = nl;553 old_nl = nl;
552 event_nr = align_ptr(((void *) (nl + 1)) + strlen(hc->name) + 1);554 event_nr = align_ptr(nl->name + strlen(hc->name) + 1);
553 *event_nr = dm_get_event_nr(hc->md);555 *event_nr = dm_get_event_nr(hc->md);
554 nl = align_ptr(event_nr + 1);556 nl = align_ptr(event_nr + 1);
555 }557 }
556 }558 }
559 /*
560 * If mismatch happens, security may be compromised due to buffer
561 * overflow, so it's better to crash.
562 */
563 BUG_ON((char *)nl - (char *)orig_nl != needed);
557 564
558 out:565 out:
559 up_write(&_hash_lock);566 up_write(&_hash_lock);
@@ -1621,7 +1628,8 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para · 1061 unmodified lines
1621 * which has a variable size, is not used by the function processing1628 * which has a variable size, is not used by the function processing
1622 * the ioctl.1629 * the ioctl.
1623 */1630 */
1624#define IOCTL_FLAGS_NO_PARAMS 11631#define IOCTL_FLAGS_NO_PARAMS 1
1632#define IOCTL_FLAGS_ISSUE_GLOBAL_EVENT 2
1625 1633
1626/*-----------------------------------------------------------------1634/*-----------------------------------------------------------------
1627 * Implementation of open/close/ioctl on the special char1635 * Implementation of open/close/ioctl on the special char
@@ -1635,12 +1643,12 @@ static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags) · 7 unmodified lines
1635 ioctl_fn fn;1643 ioctl_fn fn;
1636 } _ioctls[] = {1644 } _ioctls[] = {
1637 {DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */1645 {DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */
1638 {DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS, remove_all},1646 {DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, remove_all},
1639 {DM_LIST_DEVICES_CMD, 0, list_devices},1647 {DM_LIST_DEVICES_CMD, 0, list_devices},
1640 1648
1641 {DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_create},1649 {DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create},
1642 {DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_remove},1650 {DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove},
1643 {DM_DEV_RENAME_CMD, 0, dev_rename},1651 {DM_DEV_RENAME_CMD, IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename},
1644 {DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend},1652 {DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend},
1645 {DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status},1653 {DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status},
1646 {DM_DEV_WAIT_CMD, 0, dev_wait},1654 {DM_DEV_WAIT_CMD, 0, dev_wait},
@@ -1869,6 +1877,9 @@ static int ctl_ioctl(struct file *file, uint command, struct dm_ioctl __user *us · 222 unmodified lines
1869 unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS))1877 unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS))
1870 DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd);1878 DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd);
1871 1879
1880 if (!r && ioctl_flags & IOCTL_FLAGS_ISSUE_GLOBAL_EVENT)
1881 dm_issue_global_event();
1882
1872 /*1883 /*
1873 * Copy the results back to userland.1884 * Copy the results back to userland.
1874 */1885 */
drivers/md/dm.c+8−2
@@ -52,6 +52,12 @@ static struct workqueue_struct *deferred_remove_workqueue; · 51 unmodified lines
52atomic_t dm_global_event_nr = ATOMIC_INIT(0);52atomic_t dm_global_event_nr = ATOMIC_INIT(0);
53DECLARE_WAIT_QUEUE_HEAD(dm_global_eventq);53DECLARE_WAIT_QUEUE_HEAD(dm_global_eventq);
54 54
55void dm_issue_global_event(void)
56{
57 atomic_inc(&dm_global_event_nr);
58 wake_up(&dm_global_eventq);
59}
60
55/*61/*
56 * One of these is allocated per bio.62 * One of these is allocated per bio.
57 */63 */
@@ -1865,9 +1871,8 @@ static void event_callback(void *context) · 1807 unmodified lines
1865 dm_send_uevents(&uevents, &disk_to_dev(md->disk)->kobj);1871 dm_send_uevents(&uevents, &disk_to_dev(md->disk)->kobj);
1866 1872
1867 atomic_inc(&md->event_nr);1873 atomic_inc(&md->event_nr);
1868 atomic_inc(&dm_global_event_nr);
1869 wake_up(&md->eventq);1874 wake_up(&md->eventq);
1870 wake_up(&dm_global_eventq);1875 dm_issue_global_event();
1871}1876}
1872 1877
1873/*1878/*
@@ -2283,6 +2288,7 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table) · 409 unmodified lines
2283 }2288 }
2284 2289
2285 map = __bind(md, table, &limits);2290 map = __bind(md, table, &limits);
2291 dm_issue_global_event();
2286 2292
2287out:2293out:
2288 mutex_unlock(&md->suspend_lock);2294 mutex_unlock(&md->suspend_lock);
include/uapi/linux/dm-ioctl.h+2−2
@@ -269,9 +269,9 @@ enum { · 268 unmodified lines
269#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)269#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
270 270
271#define DM_VERSION_MAJOR 4271#define DM_VERSION_MAJOR 4
272#define DM_VERSION_MINOR 36272#define DM_VERSION_MINOR 37
273#define DM_VERSION_PATCHLEVEL 0273#define DM_VERSION_PATCHLEVEL 0
274#define DM_VERSION_EXTRA "-ioctl (2017-06-09)"274#define DM_VERSION_EXTRA "-ioctl (2017-09-20)"
275 275
276/* Status bits */276/* Status bits */
277#define DM_READONLY_FLAG (1 << 0) /* In/Out */277#define DM_READONLY_FLAG (1 << 0) /* In/Out */