diff options
author | Jiri Kosina <jkosina@suse.cz> | 2016-02-01 05:19:17 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2016-02-01 05:19:17 -0500 |
commit | a0c80efe5956ccce9fe7ae5c78542578c07bc20a (patch) | |
tree | a712e934052d1d0d017f95c8ac249756284100a8 /drivers/block | |
parent | aa0818c6ee8d8e4772725a43550823347bc1ad30 (diff) |
floppy: fix lock_fdc() signal handling
floppy_revalidate() doesn't perform any error handling on lock_fdc()
result. lock_fdc() might actually be interrupted by a signal (it waits for
fdc becoming non-busy interruptibly). In such case, floppy_revalidate()
proceeds as if it had claimed the lock, but it fact it doesn't.
In case of multiple threads trying to open("/dev/fdX"), this leads to
serious corruptions all over the place, because all of a sudden there is
no critical section protection (that'd otherwise be guaranteed by locked
fd) whatsoever.
While at this, fix the fact that the 'interruptible' parameter to
lock_fdc() doesn't make any sense whatsoever, because we always wait
interruptibly anyway.
Most of the lock_fdc() callsites do properly handle error (and propagate
EINTR), but floppy_revalidate() and floppy_check_events() don't. Fix this.
Spotted by 'syzkaller' tool.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Tested-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/floppy.c | 33 |
1 files changed, 18 insertions, 15 deletions
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index fa9bb742df6e..c1aacca88c8f 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c | |||
@@ -866,7 +866,7 @@ static void set_fdc(int drive) | |||
866 | } | 866 | } |
867 | 867 | ||
868 | /* locks the driver */ | 868 | /* locks the driver */ |
869 | static int lock_fdc(int drive, bool interruptible) | 869 | static int lock_fdc(int drive) |
870 | { | 870 | { |
871 | if (WARN(atomic_read(&usage_count) == 0, | 871 | if (WARN(atomic_read(&usage_count) == 0, |
872 | "Trying to lock fdc while usage count=0\n")) | 872 | "Trying to lock fdc while usage count=0\n")) |
@@ -2173,7 +2173,7 @@ static int do_format(int drive, struct format_descr *tmp_format_req) | |||
2173 | { | 2173 | { |
2174 | int ret; | 2174 | int ret; |
2175 | 2175 | ||
2176 | if (lock_fdc(drive, true)) | 2176 | if (lock_fdc(drive)) |
2177 | return -EINTR; | 2177 | return -EINTR; |
2178 | 2178 | ||
2179 | set_floppy(drive); | 2179 | set_floppy(drive); |
@@ -2960,7 +2960,7 @@ static int user_reset_fdc(int drive, int arg, bool interruptible) | |||
2960 | { | 2960 | { |
2961 | int ret; | 2961 | int ret; |
2962 | 2962 | ||
2963 | if (lock_fdc(drive, interruptible)) | 2963 | if (lock_fdc(drive)) |
2964 | return -EINTR; | 2964 | return -EINTR; |
2965 | 2965 | ||
2966 | if (arg == FD_RESET_ALWAYS) | 2966 | if (arg == FD_RESET_ALWAYS) |
@@ -3243,7 +3243,7 @@ static int set_geometry(unsigned int cmd, struct floppy_struct *g, | |||
3243 | if (!capable(CAP_SYS_ADMIN)) | 3243 | if (!capable(CAP_SYS_ADMIN)) |
3244 | return -EPERM; | 3244 | return -EPERM; |
3245 | mutex_lock(&open_lock); | 3245 | mutex_lock(&open_lock); |
3246 | if (lock_fdc(drive, true)) { | 3246 | if (lock_fdc(drive)) { |
3247 | mutex_unlock(&open_lock); | 3247 | mutex_unlock(&open_lock); |
3248 | return -EINTR; | 3248 | return -EINTR; |
3249 | } | 3249 | } |
@@ -3263,7 +3263,7 @@ static int set_geometry(unsigned int cmd, struct floppy_struct *g, | |||
3263 | } else { | 3263 | } else { |
3264 | int oldStretch; | 3264 | int oldStretch; |
3265 | 3265 | ||
3266 | if (lock_fdc(drive, true)) | 3266 | if (lock_fdc(drive)) |
3267 | return -EINTR; | 3267 | return -EINTR; |
3268 | if (cmd != FDDEFPRM) { | 3268 | if (cmd != FDDEFPRM) { |
3269 | /* notice a disk change immediately, else | 3269 | /* notice a disk change immediately, else |
@@ -3349,7 +3349,7 @@ static int get_floppy_geometry(int drive, int type, struct floppy_struct **g) | |||
3349 | if (type) | 3349 | if (type) |
3350 | *g = &floppy_type[type]; | 3350 | *g = &floppy_type[type]; |
3351 | else { | 3351 | else { |
3352 | if (lock_fdc(drive, false)) | 3352 | if (lock_fdc(drive)) |
3353 | return -EINTR; | 3353 | return -EINTR; |
3354 | if (poll_drive(false, 0) == -EINTR) | 3354 | if (poll_drive(false, 0) == -EINTR) |
3355 | return -EINTR; | 3355 | return -EINTR; |
@@ -3433,7 +3433,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int | |||
3433 | if (UDRS->fd_ref != 1) | 3433 | if (UDRS->fd_ref != 1) |
3434 | /* somebody else has this drive open */ | 3434 | /* somebody else has this drive open */ |
3435 | return -EBUSY; | 3435 | return -EBUSY; |
3436 | if (lock_fdc(drive, true)) | 3436 | if (lock_fdc(drive)) |
3437 | return -EINTR; | 3437 | return -EINTR; |
3438 | 3438 | ||
3439 | /* do the actual eject. Fails on | 3439 | /* do the actual eject. Fails on |
@@ -3445,7 +3445,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int | |||
3445 | process_fd_request(); | 3445 | process_fd_request(); |
3446 | return ret; | 3446 | return ret; |
3447 | case FDCLRPRM: | 3447 | case FDCLRPRM: |
3448 | if (lock_fdc(drive, true)) | 3448 | if (lock_fdc(drive)) |
3449 | return -EINTR; | 3449 | return -EINTR; |
3450 | current_type[drive] = NULL; | 3450 | current_type[drive] = NULL; |
3451 | floppy_sizes[drive] = MAX_DISK_SIZE << 1; | 3451 | floppy_sizes[drive] = MAX_DISK_SIZE << 1; |
@@ -3467,7 +3467,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int | |||
3467 | UDP->flags &= ~FTD_MSG; | 3467 | UDP->flags &= ~FTD_MSG; |
3468 | return 0; | 3468 | return 0; |
3469 | case FDFMTBEG: | 3469 | case FDFMTBEG: |
3470 | if (lock_fdc(drive, true)) | 3470 | if (lock_fdc(drive)) |
3471 | return -EINTR; | 3471 | return -EINTR; |
3472 | if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) | 3472 | if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) |
3473 | return -EINTR; | 3473 | return -EINTR; |
@@ -3484,7 +3484,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int | |||
3484 | return do_format(drive, &inparam.f); | 3484 | return do_format(drive, &inparam.f); |
3485 | case FDFMTEND: | 3485 | case FDFMTEND: |
3486 | case FDFLUSH: | 3486 | case FDFLUSH: |
3487 | if (lock_fdc(drive, true)) | 3487 | if (lock_fdc(drive)) |
3488 | return -EINTR; | 3488 | return -EINTR; |
3489 | return invalidate_drive(bdev); | 3489 | return invalidate_drive(bdev); |
3490 | case FDSETEMSGTRESH: | 3490 | case FDSETEMSGTRESH: |
@@ -3507,7 +3507,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int | |||
3507 | outparam = UDP; | 3507 | outparam = UDP; |
3508 | break; | 3508 | break; |
3509 | case FDPOLLDRVSTAT: | 3509 | case FDPOLLDRVSTAT: |
3510 | if (lock_fdc(drive, true)) | 3510 | if (lock_fdc(drive)) |
3511 | return -EINTR; | 3511 | return -EINTR; |
3512 | if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) | 3512 | if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) |
3513 | return -EINTR; | 3513 | return -EINTR; |
@@ -3530,7 +3530,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int | |||
3530 | case FDRAWCMD: | 3530 | case FDRAWCMD: |
3531 | if (type) | 3531 | if (type) |
3532 | return -EINVAL; | 3532 | return -EINVAL; |
3533 | if (lock_fdc(drive, true)) | 3533 | if (lock_fdc(drive)) |
3534 | return -EINTR; | 3534 | return -EINTR; |
3535 | set_floppy(drive); | 3535 | set_floppy(drive); |
3536 | i = raw_cmd_ioctl(cmd, (void __user *)param); | 3536 | i = raw_cmd_ioctl(cmd, (void __user *)param); |
@@ -3539,7 +3539,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int | |||
3539 | process_fd_request(); | 3539 | process_fd_request(); |
3540 | return i; | 3540 | return i; |
3541 | case FDTWADDLE: | 3541 | case FDTWADDLE: |
3542 | if (lock_fdc(drive, true)) | 3542 | if (lock_fdc(drive)) |
3543 | return -EINTR; | 3543 | return -EINTR; |
3544 | twaddle(); | 3544 | twaddle(); |
3545 | process_fd_request(); | 3545 | process_fd_request(); |
@@ -3747,7 +3747,8 @@ static unsigned int floppy_check_events(struct gendisk *disk, | |||
3747 | return DISK_EVENT_MEDIA_CHANGE; | 3747 | return DISK_EVENT_MEDIA_CHANGE; |
3748 | 3748 | ||
3749 | if (time_after(jiffies, UDRS->last_checked + UDP->checkfreq)) { | 3749 | if (time_after(jiffies, UDRS->last_checked + UDP->checkfreq)) { |
3750 | lock_fdc(drive, false); | 3750 | if (lock_fdc(drive)) |
3751 | return -EINTR; | ||
3751 | poll_drive(false, 0); | 3752 | poll_drive(false, 0); |
3752 | process_fd_request(); | 3753 | process_fd_request(); |
3753 | } | 3754 | } |
@@ -3845,7 +3846,9 @@ static int floppy_revalidate(struct gendisk *disk) | |||
3845 | "VFS: revalidate called on non-open device.\n")) | 3846 | "VFS: revalidate called on non-open device.\n")) |
3846 | return -EFAULT; | 3847 | return -EFAULT; |
3847 | 3848 | ||
3848 | lock_fdc(drive, false); | 3849 | res = lock_fdc(drive); |
3850 | if (res) | ||
3851 | return res; | ||
3849 | cf = (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || | 3852 | cf = (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || |
3850 | test_bit(FD_VERIFY_BIT, &UDRS->flags)); | 3853 | test_bit(FD_VERIFY_BIT, &UDRS->flags)); |
3851 | if (!(cf || test_bit(drive, &fake_change) || drive_no_geom(drive))) { | 3854 | if (!(cf || test_bit(drive, &fake_change) || drive_no_geom(drive))) { |