diff options
author | Nicolas Schichan <nschichan@freebox.fr> | 2014-01-15 10:58:52 -0500 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2014-01-15 16:55:00 -0500 |
commit | cb335f88eb35af712d1f4171642d0487f7bb2e7e (patch) | |
tree | fba070397b84f40067bf5d12aded18672eea3ebf /drivers/md/md.c | |
parent | 830778a180f268ac106f072b8aad793a79088c87 (diff) |
md: check command validity early in md_ioctl().
Verify that the cmd parameter passed to md_ioctl() is valid before
doing anything.
This fixes mddev->hold_active being set to 0 when an invalid ioctl
command is passed to md_ioctl() before the array has been configured.
Clearing mddev->hold_active in that case can lead to a livelock
situation when an invalid ioctl number is given to md_ioctl() by a
process when the mddev is currently being opened by another process:
Process 1 Process 2
--------- ---------
md_alloc()
mddev_find()
-> returns a new mddev with
hold_active == UNTIL_IOCTL
add_disk()
-> sends KOBJ_ADD uevent
(sees KOBJ_ADD uevent for device)
md_open()
md_ioctl(INVALID_IOCTL)
-> returns ENODEV and clears
mddev->hold_active
md_release()
md_put()
-> deletes the mddev as
hold_active is 0
md_open()
mddev_find()
-> returns a newly
allocated mddev with
mddev->gendisk == NULL
-> returns with ERESTARTSYS
(kernel restarts the open syscall)
Signed-off-by: Nicolas Schichan <nschichan@freebox.fr>
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers/md/md.c')
-rw-r--r-- | drivers/md/md.c | 29 |
1 files changed, 29 insertions, 0 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index a20b7806de7a..b890d3fb0e02 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c | |||
@@ -6356,6 +6356,32 @@ static int md_getgeo(struct block_device *bdev, struct hd_geometry *geo) | |||
6356 | return 0; | 6356 | return 0; |
6357 | } | 6357 | } |
6358 | 6358 | ||
6359 | static inline bool md_ioctl_valid(unsigned int cmd) | ||
6360 | { | ||
6361 | switch (cmd) { | ||
6362 | case ADD_NEW_DISK: | ||
6363 | case BLKROSET: | ||
6364 | case GET_ARRAY_INFO: | ||
6365 | case GET_BITMAP_FILE: | ||
6366 | case GET_DISK_INFO: | ||
6367 | case HOT_ADD_DISK: | ||
6368 | case HOT_REMOVE_DISK: | ||
6369 | case PRINT_RAID_DEBUG: | ||
6370 | case RAID_AUTORUN: | ||
6371 | case RAID_VERSION: | ||
6372 | case RESTART_ARRAY_RW: | ||
6373 | case RUN_ARRAY: | ||
6374 | case SET_ARRAY_INFO: | ||
6375 | case SET_BITMAP_FILE: | ||
6376 | case SET_DISK_FAULTY: | ||
6377 | case STOP_ARRAY: | ||
6378 | case STOP_ARRAY_RO: | ||
6379 | return true; | ||
6380 | default: | ||
6381 | return false; | ||
6382 | } | ||
6383 | } | ||
6384 | |||
6359 | static int md_ioctl(struct block_device *bdev, fmode_t mode, | 6385 | static int md_ioctl(struct block_device *bdev, fmode_t mode, |
6360 | unsigned int cmd, unsigned long arg) | 6386 | unsigned int cmd, unsigned long arg) |
6361 | { | 6387 | { |
@@ -6364,6 +6390,9 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, | |||
6364 | struct mddev *mddev = NULL; | 6390 | struct mddev *mddev = NULL; |
6365 | int ro; | 6391 | int ro; |
6366 | 6392 | ||
6393 | if (!md_ioctl_valid(cmd)) | ||
6394 | return -ENOTTY; | ||
6395 | |||
6367 | switch (cmd) { | 6396 | switch (cmd) { |
6368 | case RAID_VERSION: | 6397 | case RAID_VERSION: |
6369 | case GET_ARRAY_INFO: | 6398 | case GET_ARRAY_INFO: |