| From bc26d4d06e337ade069f33d3f4377593b24e6e36 Mon Sep 17 00:00:00 2001 |
| From: Alexey Khoroshilov <khoroshilov@ispras.ru> |
| Date: Sat, 18 Apr 2015 02:53:25 +0300 |
| Subject: sound/oss: fix deadlock in sequencer_ioctl(SNDCTL_SEQ_OUTOFBAND) |
| |
| From: Alexey Khoroshilov <khoroshilov@ispras.ru> |
| |
| commit bc26d4d06e337ade069f33d3f4377593b24e6e36 upstream. |
| |
| A deadlock can be initiated by userspace via ioctl(SNDCTL_SEQ_OUTOFBAND) |
| on /dev/sequencer with TMR_ECHO midi event. |
| |
| In this case the control flow is: |
| sound_ioctl() |
| -> case SND_DEV_SEQ: |
| case SND_DEV_SEQ2: |
| sequencer_ioctl() |
| -> case SNDCTL_SEQ_OUTOFBAND: |
| spin_lock_irqsave(&lock,flags); |
| play_event(); |
| -> case EV_TIMING: |
| seq_timing_event() |
| -> case TMR_ECHO: |
| seq_copy_to_input() |
| -> spin_lock_irqsave(&lock,flags); |
| |
| It seems that spin_lock_irqsave() around play_event() is not necessary, |
| because the only other call location in seq_startplay() makes the call |
| without acquiring spinlock. |
| |
| So, the patch just removes spinlocks around play_event(). |
| By the way, it removes unreachable code in seq_timing_event(), |
| since (seq_mode == SEQ_2) case is handled in the beginning. |
| |
| Compile tested only. |
| |
| Found by Linux Driver Verification project (linuxtesting.org). |
| |
| Signed-off-by: Alexey Khoroshilov <khoroshilov@ispras.ru> |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Cc: Willy Tarreau <w@1wt.eu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| sound/oss/sequencer.c | 12 ++---------- |
| 1 file changed, 2 insertions(+), 10 deletions(-) |
| |
| --- a/sound/oss/sequencer.c |
| +++ b/sound/oss/sequencer.c |
| @@ -681,13 +681,8 @@ static int seq_timing_event(unsigned cha |
| break; |
| |
| case TMR_ECHO: |
| - if (seq_mode == SEQ_2) |
| - seq_copy_to_input(event_rec, 8); |
| - else |
| - { |
| - parm = (parm << 8 | SEQ_ECHO); |
| - seq_copy_to_input((unsigned char *) &parm, 4); |
| - } |
| + parm = (parm << 8 | SEQ_ECHO); |
| + seq_copy_to_input((unsigned char *) &parm, 4); |
| break; |
| |
| default:; |
| @@ -1324,7 +1319,6 @@ int sequencer_ioctl(int dev, struct file |
| int mode = translate_mode(file); |
| struct synth_info inf; |
| struct seq_event_rec event_rec; |
| - unsigned long flags; |
| int __user *p = arg; |
| |
| orig_dev = dev = dev >> 4; |
| @@ -1479,9 +1473,7 @@ int sequencer_ioctl(int dev, struct file |
| case SNDCTL_SEQ_OUTOFBAND: |
| if (copy_from_user(&event_rec, arg, sizeof(event_rec))) |
| return -EFAULT; |
| - spin_lock_irqsave(&lock,flags); |
| play_event(event_rec.arr); |
| - spin_unlock_irqrestore(&lock,flags); |
| return 0; |
| |
| case SNDCTL_MIDI_INFO: |