| @@ -477,9 +477,13 @@ static int remove_all(struct file *filp, struct dm_ioctl *param, size_t param_si · 476 unmodified lines |
| 477 | 477 | * Round up the ptr to an 8-byte boundary. |
| 478 | 478 | */ |
| 479 | 479 | #define ALIGN_MASK 7 |
| 480 | +static inline size_t align_val(size_t val) |
| 481 | +{ |
| 482 | + return (val + ALIGN_MASK) & ~ALIGN_MASK; |
| 483 | +} |
| 480 | 484 | static 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 | 509 | struct hash_cell *hc; |
| 506 | 510 | size_t len, needed = 0; |
| 507 | 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 | 513 | uint32_t *event_nr; |
| 510 | 514 | |
| 511 | 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 | 521 | for (i = 0; i < NUM_BUCKETS; i++) { |
| 518 | 522 | list_for_each_entry (hc, _name_buckets + i, name_list) { |
| 519 | | − needed += sizeof(struct dm_name_list); |
| 520 | | − needed += strlen(hc->name) + 1; |
| 521 | | − needed += ALIGN_MASK; |
| 522 | | − needed += (sizeof(uint32_t) + ALIGN_MASK) & ~ALIGN_MASK; |
| 523 | + needed += align_val(offsetof(struct dm_name_list, name) + strlen(hc->name) + 1); |
| 524 | + needed += align_val(sizeof(uint32_t)); |
| 523 | 525 | } |
| 524 | 526 | } |
| 525 | 527 | |
| 526 | 528 | /* |
| 527 | 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 | 532 | if (len < needed) { |
| 531 | 533 | param->flags |= DM_BUFFER_FULL_FLAG; |
| 532 | 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 | 551 | strcpy(nl->name, hc->name); |
| 550 | 552 | |
| 551 | 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 | 555 | *event_nr = dm_get_event_nr(hc->md); |
| 554 | 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 | 565 | out: |
| 559 | 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 | 1628 | * which has a variable size, is not used by the function processing |
| 1622 | 1629 | * the ioctl. |
| 1623 | 1630 | */ |
| 1624 | | −#define IOCTL_FLAGS_NO_PARAMS 1 |
| 1631 | +#define IOCTL_FLAGS_NO_PARAMS 1 |
| 1632 | +#define IOCTL_FLAGS_ISSUE_GLOBAL_EVENT 2 |
| 1625 | 1633 | |
| 1626 | 1634 | /*----------------------------------------------------------------- |
| 1627 | 1635 | * 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 | 1643 | ioctl_fn fn; |
| 1636 | 1644 | } _ioctls[] = { |
| 1637 | 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 | 1647 | {DM_LIST_DEVICES_CMD, 0, list_devices}, |
| 1640 | 1648 | |
| 1641 | | − {DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_create}, |
| 1642 | | − {DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_remove}, |
| 1643 | | − {DM_DEV_RENAME_CMD, 0, dev_rename}, |
| 1649 | + {DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create}, |
| 1650 | + {DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove}, |
| 1651 | + {DM_DEV_RENAME_CMD, IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename}, |
| 1644 | 1652 | {DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend}, |
| 1645 | 1653 | {DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status}, |
| 1646 | 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 | 1877 | unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS)) |
| 1870 | 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 | 1884 | * Copy the results back to userland. |
| 1874 | 1885 | */ |