| From 132d358b183ac6ad8b3fea32ad5e0663456d18d1 Mon Sep 17 00:00:00 2001 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Tue, 7 Nov 2017 16:05:24 +0100 |
| Subject: ALSA: seq: Fix OSS sysex delivery in OSS emulation |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit 132d358b183ac6ad8b3fea32ad5e0663456d18d1 upstream. |
| |
| The SYSEX event delivery in OSS sequencer emulation assumed that the |
| event is encoded in the variable-length data with the straight |
| buffering. This was the normal behavior in the past, but during the |
| development, the chained buffers were introduced for carrying more |
| data, while the OSS code was left intact. As a result, when a SYSEX |
| event with the chained buffer data is passed to OSS sequencer port, |
| it may end up with the wrong memory access, as if it were having a too |
| large buffer. |
| |
| This patch addresses the bug, by applying the buffer data expansion by |
| the generic snd_seq_dump_var_event() helper function. |
| |
| Reported-by: syzbot <syzkaller@googlegroups.com> |
| Reported-by: Mark Salyzyn <salyzyn@android.com> |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| sound/core/seq/oss/seq_oss_midi.c | 4 +--- |
| sound/core/seq/oss/seq_oss_readq.c | 29 +++++++++++++++++++++++++++++ |
| sound/core/seq/oss/seq_oss_readq.h | 2 ++ |
| 3 files changed, 32 insertions(+), 3 deletions(-) |
| |
| --- a/sound/core/seq/oss/seq_oss_midi.c |
| +++ b/sound/core/seq/oss/seq_oss_midi.c |
| @@ -612,9 +612,7 @@ send_midi_event(struct seq_oss_devinfo * |
| if (!dp->timer->running) |
| len = snd_seq_oss_timer_start(dp->timer); |
| if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { |
| - if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) |
| - snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, |
| - ev->data.ext.ptr, ev->data.ext.len); |
| + snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev); |
| } else { |
| len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev); |
| if (len > 0) |
| --- a/sound/core/seq/oss/seq_oss_readq.c |
| +++ b/sound/core/seq/oss/seq_oss_readq.c |
| @@ -118,6 +118,35 @@ snd_seq_oss_readq_puts(struct seq_oss_re |
| } |
| |
| /* |
| + * put MIDI sysex bytes; the event buffer may be chained, thus it has |
| + * to be expanded via snd_seq_dump_var_event(). |
| + */ |
| +struct readq_sysex_ctx { |
| + struct seq_oss_readq *readq; |
| + int dev; |
| +}; |
| + |
| +static int readq_dump_sysex(void *ptr, void *buf, int count) |
| +{ |
| + struct readq_sysex_ctx *ctx = ptr; |
| + |
| + return snd_seq_oss_readq_puts(ctx->readq, ctx->dev, buf, count); |
| +} |
| + |
| +int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev, |
| + struct snd_seq_event *ev) |
| +{ |
| + struct readq_sysex_ctx ctx = { |
| + .readq = q, |
| + .dev = dev |
| + }; |
| + |
| + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) |
| + return 0; |
| + return snd_seq_dump_var_event(ev, readq_dump_sysex, &ctx); |
| +} |
| + |
| +/* |
| * copy an event to input queue: |
| * return zero if enqueued |
| */ |
| --- a/sound/core/seq/oss/seq_oss_readq.h |
| +++ b/sound/core/seq/oss/seq_oss_readq.h |
| @@ -44,6 +44,8 @@ void snd_seq_oss_readq_delete(struct seq |
| void snd_seq_oss_readq_clear(struct seq_oss_readq *readq); |
| unsigned int snd_seq_oss_readq_poll(struct seq_oss_readq *readq, struct file *file, poll_table *wait); |
| int snd_seq_oss_readq_puts(struct seq_oss_readq *readq, int dev, unsigned char *data, int len); |
| +int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev, |
| + struct snd_seq_event *ev); |
| int snd_seq_oss_readq_put_event(struct seq_oss_readq *readq, union evrec *ev); |
| int snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *readq, unsigned long curt, int seq_mode); |
| int snd_seq_oss_readq_pick(struct seq_oss_readq *q, union evrec *rec); |