| From 50e9ffb1996a5d11ff5040a266585bad4ceeca0a Mon Sep 17 00:00:00 2001 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Thu, 26 Jul 2018 14:27:59 +0200 |
| Subject: ALSA: virmidi: Fix too long output trigger loop |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit 50e9ffb1996a5d11ff5040a266585bad4ceeca0a upstream. |
| |
| The virmidi output trigger tries to parse the all available bytes and |
| process sequencer events as much as possible. In a normal situation, |
| this is supposed to be relatively short, but a program may give a huge |
| buffer and it'll take a long time in a single spin lock, which may |
| eventually lead to a soft lockup. |
| |
| This patch simply adds a workaround, a cond_resched() call in the loop |
| if applicable. A better solution would be to move the event processor |
| into a work, but let's put a duct-tape quickly at first. |
| |
| Reported-and-tested-by: Dae R. Jeong <threeearcat@gmail.com> |
| Reported-by: syzbot+619d9f40141d826b097e@syzkaller.appspotmail.com |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| sound/core/seq/seq_virmidi.c | 10 ++++++++++ |
| 1 file changed, 10 insertions(+) |
| |
| --- a/sound/core/seq/seq_virmidi.c |
| +++ b/sound/core/seq/seq_virmidi.c |
| @@ -163,6 +163,7 @@ static void snd_virmidi_output_trigger(s |
| int count, res; |
| unsigned char buf[32], *pbuf; |
| unsigned long flags; |
| + bool check_resched = !in_atomic(); |
| |
| if (up) { |
| vmidi->trigger = 1; |
| @@ -200,6 +201,15 @@ static void snd_virmidi_output_trigger(s |
| vmidi->event.type = SNDRV_SEQ_EVENT_NONE; |
| } |
| } |
| + if (!check_resched) |
| + continue; |
| + /* do temporary unlock & cond_resched() for avoiding |
| + * CPU soft lockup, which may happen via a write from |
| + * a huge rawmidi buffer |
| + */ |
| + spin_unlock_irqrestore(&substream->runtime->lock, flags); |
| + cond_resched(); |
| + spin_lock_irqsave(&substream->runtime->lock, flags); |
| } |
| out: |
| spin_unlock_irqrestore(&substream->runtime->lock, flags); |