diff options
author | Nicolas Schichan <nschichan@freebox.fr> | 2014-01-15 16:58:52 +0100 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2014-01-16 08:55:00 +1100 |
commit | cb335f88eb35af712d1f4171642d0487f7bb2e7e (patch) | |
tree | fba070397b84f40067bf5d12aded18672eea3ebf /drivers/md/md.c | |
parent | 830778a180f268ac106f072b8aad793a79088c87 (diff) | |
download | linux-cb335f88eb35af712d1f4171642d0487f7bb2e7e.tar.gz linux-cb335f88eb35af712d1f4171642d0487f7bb2e7e.tar.bz2 linux-cb335f88eb35af712d1f4171642d0487f7bb2e7e.zip |
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) return 0; } +static inline bool md_ioctl_valid(unsigned int cmd) +{ + switch (cmd) { + case ADD_NEW_DISK: + case BLKROSET: + case GET_ARRAY_INFO: + case GET_BITMAP_FILE: + case GET_DISK_INFO: + case HOT_ADD_DISK: + case HOT_REMOVE_DISK: + case PRINT_RAID_DEBUG: + case RAID_AUTORUN: + case RAID_VERSION: + case RESTART_ARRAY_RW: + case RUN_ARRAY: + case SET_ARRAY_INFO: + case SET_BITMAP_FILE: + case SET_DISK_FAULTY: + case STOP_ARRAY: + case STOP_ARRAY_RO: + return true; + default: + return false; + } +} + static int md_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { @@ -6364,6 +6390,9 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, struct mddev *mddev = NULL; int ro; + if (!md_ioctl_valid(cmd)) + return -ENOTTY; + switch (cmd) { case RAID_VERSION: case GET_ARRAY_INFO: |