* [PATCH] ALSA: seq: fix use-after-free of borrowed substream in snd-seq-midi
@ 2026-06-10 20:03 Doruk Tan Ozturk
2026-06-11 7:25 ` Takashi Iwai
0 siblings, 1 reply; 4+ messages in thread
From: Doruk Tan Ozturk @ 2026-06-10 20:03 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai; +Cc: linux-sound, linux-kernel
event_process_midi() borrows the rawmidi output substream via
msynth->output_rfile.output and uses it through dump_midi() ->
snd_rawmidi_kernel_write() -> snd_rawmidi_kernel_write1() without
synchronizing against a concurrent port unsubscribe.
A concurrent UNSUBSCRIBE_PORT on the output connection runs the
unuse callback midisynth_unuse() -> snd_rawmidi_kernel_release() ->
close_substream() -> snd_rawmidi_runtime_free(), freeing
substream->runtime while an in-flight event_input callback is still
inside snd_rawmidi_kernel_write1(). The borrowed substream runtime is
exposed to teardown before the write path takes its own buffer
reference (snd_rawmidi_buffer_ref()), so the early derefs of
substream->runtime / runtime->buffer read freed memory.
The buggy scenario involves two paths, with each column showing the
order within that path:
path A: event_input path path B: last unuse path
1. event_process_midi() reads 1. midisynth_unuse() runs on the
msynth->output_rfile.output. last UNSUBSCRIBE_PORT.
2. snd_rawmidi_kernel_write1() has 2. snd_rawmidi_kernel_release()
not yet pinned runtime. closes the output file.
3. The writer continues using 3. close_substream() frees
the borrowed substream. substream->runtime.
This is the snd-seq-midi sibling of the UMP-bridge race fixed by
commit 60a1969fae62 ("ALSA: seq: Serialize UMP output teardown with
event_input"); mirror its approach here.
Add a per-msynth rwlock for the event_input-visible output file.
Publish a newly opened output file under the write side once it is
fully set up, and hold the read side from the output lookup through
snd_rawmidi_kernel_write() in event_process_midi(). The last unuse
copies and clears the visible output file under the write side, then
drops the lock and drains/releases the saved rawmidi file outside it
(drain/release may sleep). Use IRQ-safe rwlock guards because
event_input can be reached from atomic sequencer delivery.
Reproduced under KASAN: a single subscription to the midisynth output
port (so the subscriber count oscillates 0<->1 and every unsubscribe
frees the runtime via snd_rawmidi_kernel_release) is raced against a
flood of events driving event_process_midi -> snd_rawmidi_kernel_write1.
The narrow window (the runtime is read before snd_rawmidi_buffer_ref())
was widened with an injected delay to land the race deterministically;
the freed object is the kmalloc-192 snd_rawmidi_runtime. With this patch
applied the same forced race shows no use-after-free.
BUG: KASAN: slab-use-after-free in snd_rawmidi_kernel_write1+0x73e/0x800
Read of size 8 at addr ffff88800b04f310 by task seqmidi_uaf2/84
Call Trace:
snd_rawmidi_kernel_write1+0x73e/0x800
__dump_midi+0x70/0x100
dump_var_event+0x290/0x320
event_process_midi+0x1ff/0x310
snd_seq_deliver_single_event+0x1e6/0x670
snd_seq_deliver_event+0x323/0x5f0
snd_seq_client_enqueue_event.constprop.0+0x226/0x400
snd_seq_write+0x2f1/0x530
vfs_write+0x21e/0xd30
ksys_write+0x17c/0x1c0
do_syscall_64+0xf9/0x540
Allocated by task 85:
open_substream+0xc7/0x7a0
rawmidi_open_priv+0x3df/0x660
snd_rawmidi_kernel_open+0x95/0x140
midisynth_use+0xda/0x1f0
check_and_subscribe_port+0x707/0xbd0
snd_seq_ioctl_subscribe_port+0x1f4/0x400
Freed by task 85:
close_substream.part.0+0x1f9/0x790
rawmidi_release_priv+0x1b0/0x240
snd_rawmidi_kernel_release+0x2d/0xb0
__delete_and_unsubscribe_port+0x1b9/0x3c0
snd_seq_ioctl_unsubscribe_port+0x1ee/0x400
The buggy address belongs to the object at ffff88800b04f300
which belongs to the cache kmalloc-192 of size 192
Found by 0sec.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
sound/core/seq/seq_midi.c | 39 ++++++++++++++++++++++++++++++++-------
1 file changed, 32 insertions(+), 7 deletions(-)
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index ca3f5fc30992..c79c628561ce 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -18,6 +18,7 @@ Possible options for midisynth module:
#include <linux/string.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
#include <sound/seq_kernel.h>
@@ -43,6 +44,7 @@ struct seq_midisynth {
int subdevice;
struct snd_rawmidi_file input_rfile;
struct snd_rawmidi_file output_rfile;
+ rwlock_t output_lock; /* protects output_rfile.output access */
int seq_client;
int seq_port;
struct snd_midi_event *parser;
@@ -129,6 +131,13 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
if (snd_BUG_ON(!msynth))
return -EINVAL;
+ /*
+ * Hold the read side across the whole borrowed-substream use so a
+ * concurrent port unsubscribe (midisynth_unuse) cannot release the
+ * rawmidi file and free substream->runtime under us. IRQ-safe because
+ * event_input can be reached from atomic sequencer delivery.
+ */
+ guard(read_lock_irqsave)(&msynth->output_lock);
substream = msynth->output_rfile.output;
if (substream == NULL)
return -ENODEV;
@@ -160,6 +169,7 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
{
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
return -ENOMEM;
+ rwlock_init(&msynth->output_lock);
msynth->card = card;
msynth->device = device;
msynth->subdevice = subdevice;
@@ -215,12 +225,13 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
{
int err;
struct seq_midisynth *msynth = private_data;
+ struct snd_rawmidi_file rfile = {};
struct snd_rawmidi_params params;
/* open midi port */
err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
SNDRV_RAWMIDI_LFLG_OUTPUT,
- &msynth->output_rfile);
+ &rfile);
if (err < 0) {
pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
return err;
@@ -229,12 +240,15 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
params.avail_min = 1;
params.buffer_size = output_buffer_size;
params.no_active_sensing = 1;
- err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms);
+ err = snd_rawmidi_output_params(rfile.output, ¶ms);
if (err < 0) {
- snd_rawmidi_kernel_release(&msynth->output_rfile);
+ snd_rawmidi_kernel_release(&rfile);
return err;
}
snd_midi_event_reset_decode(msynth->parser);
+ /* publish the opened file only after it is fully set up */
+ scoped_guard(write_lock_irqsave, &msynth->output_lock)
+ msynth->output_rfile = rfile;
return 0;
}
@@ -242,11 +256,22 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
{
struct seq_midisynth *msynth = private_data;
-
- if (snd_BUG_ON(!msynth->output_rfile.output))
+ struct snd_rawmidi_file rfile = {};
+
+ /*
+ * Detach the borrowed output file under the write side so any in-flight
+ * event_process_midi() either still sees the live substream (and is
+ * drained out by the read lock) or sees NULL. Then drain and release
+ * outside the lock, since those paths may sleep.
+ */
+ scoped_guard(write_lock_irqsave, &msynth->output_lock) {
+ rfile = msynth->output_rfile;
+ msynth->output_rfile = (struct snd_rawmidi_file){};
+ }
+ if (snd_BUG_ON(!rfile.output))
return -EINVAL;
- snd_rawmidi_drain_output(msynth->output_rfile.output);
- return snd_rawmidi_kernel_release(&msynth->output_rfile);
+ snd_rawmidi_drain_output(rfile.output);
+ return snd_rawmidi_kernel_release(&rfile);
}
/* delete given midi synth port */
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH] ALSA: seq: fix use-after-free of borrowed substream in snd-seq-midi
2026-06-10 20:03 [PATCH] ALSA: seq: fix use-after-free of borrowed substream in snd-seq-midi Doruk Tan Ozturk
@ 2026-06-11 7:25 ` Takashi Iwai
2026-06-15 0:28 ` Doruk Tan Ozturk
0 siblings, 1 reply; 4+ messages in thread
From: Takashi Iwai @ 2026-06-11 7:25 UTC (permalink / raw)
To: Doruk Tan Ozturk; +Cc: Jaroslav Kysela, Takashi Iwai, linux-sound, linux-kernel
On Wed, 10 Jun 2026 22:03:36 +0200,
Doruk Tan Ozturk wrote:
>
> event_process_midi() borrows the rawmidi output substream via
> msynth->output_rfile.output and uses it through dump_midi() ->
> snd_rawmidi_kernel_write() -> snd_rawmidi_kernel_write1() without
> synchronizing against a concurrent port unsubscribe.
>
> A concurrent UNSUBSCRIBE_PORT on the output connection runs the
> unuse callback midisynth_unuse() -> snd_rawmidi_kernel_release() ->
> close_substream() -> snd_rawmidi_runtime_free(), freeing
> substream->runtime while an in-flight event_input callback is still
> inside snd_rawmidi_kernel_write1(). The borrowed substream runtime is
> exposed to teardown before the write path takes its own buffer
> reference (snd_rawmidi_buffer_ref()), so the early derefs of
> substream->runtime / runtime->buffer read freed memory.
>
> The buggy scenario involves two paths, with each column showing the
> order within that path:
>
> path A: event_input path path B: last unuse path
> 1. event_process_midi() reads 1. midisynth_unuse() runs on the
> msynth->output_rfile.output. last UNSUBSCRIBE_PORT.
> 2. snd_rawmidi_kernel_write1() has 2. snd_rawmidi_kernel_release()
> not yet pinned runtime. closes the output file.
> 3. The writer continues using 3. close_substream() frees
> the borrowed substream. substream->runtime.
>
> This is the snd-seq-midi sibling of the UMP-bridge race fixed by
> commit 60a1969fae62 ("ALSA: seq: Serialize UMP output teardown with
> event_input"); mirror its approach here.
>
> Add a per-msynth rwlock for the event_input-visible output file.
> Publish a newly opened output file under the write side once it is
> fully set up, and hold the read side from the output lookup through
> snd_rawmidi_kernel_write() in event_process_midi(). The last unuse
> copies and clears the visible output file under the write side, then
> drops the lock and drains/releases the saved rawmidi file outside it
> (drain/release may sleep). Use IRQ-safe rwlock guards because
> event_input can be reached from atomic sequencer delivery.
>
> Reproduced under KASAN: a single subscription to the midisynth output
> port (so the subscriber count oscillates 0<->1 and every unsubscribe
> frees the runtime via snd_rawmidi_kernel_release) is raced against a
> flood of events driving event_process_midi -> snd_rawmidi_kernel_write1.
> The narrow window (the runtime is read before snd_rawmidi_buffer_ref())
> was widened with an injected delay to land the race deterministically;
> the freed object is the kmalloc-192 snd_rawmidi_runtime. With this patch
> applied the same forced race shows no use-after-free.
>
> BUG: KASAN: slab-use-after-free in snd_rawmidi_kernel_write1+0x73e/0x800
> Read of size 8 at addr ffff88800b04f310 by task seqmidi_uaf2/84
> Call Trace:
> snd_rawmidi_kernel_write1+0x73e/0x800
> __dump_midi+0x70/0x100
> dump_var_event+0x290/0x320
> event_process_midi+0x1ff/0x310
> snd_seq_deliver_single_event+0x1e6/0x670
> snd_seq_deliver_event+0x323/0x5f0
> snd_seq_client_enqueue_event.constprop.0+0x226/0x400
> snd_seq_write+0x2f1/0x530
> vfs_write+0x21e/0xd30
> ksys_write+0x17c/0x1c0
> do_syscall_64+0xf9/0x540
>
> Allocated by task 85:
> open_substream+0xc7/0x7a0
> rawmidi_open_priv+0x3df/0x660
> snd_rawmidi_kernel_open+0x95/0x140
> midisynth_use+0xda/0x1f0
> check_and_subscribe_port+0x707/0xbd0
> snd_seq_ioctl_subscribe_port+0x1f4/0x400
>
> Freed by task 85:
> close_substream.part.0+0x1f9/0x790
> rawmidi_release_priv+0x1b0/0x240
> snd_rawmidi_kernel_release+0x2d/0xb0
> __delete_and_unsubscribe_port+0x1b9/0x3c0
> snd_seq_ioctl_unsubscribe_port+0x1ee/0x400
>
> The buggy address belongs to the object at ffff88800b04f300
> which belongs to the cache kmalloc-192 of size 192
>
> Found by 0sec.
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
There has been already a similar fix queued in sound.git tree for-next
branch:
ef7607ab1c8adc6258fb1b27d08e26aecdc18a58
ALSA: seq: midi: Serialize output teardown with event_input
I believe this already addressed the bug.
Let me know if there is still anything missing.
thanks,
Takashi
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH] ALSA: seq: fix use-after-free of borrowed substream in snd-seq-midi
2026-06-11 7:25 ` Takashi Iwai
@ 2026-06-15 0:28 ` Doruk Tan Ozturk
2026-06-15 7:26 ` Takashi Iwai
0 siblings, 1 reply; 4+ messages in thread
From: Doruk Tan Ozturk @ 2026-06-15 0:28 UTC (permalink / raw)
To: tiwai; +Cc: perex, tiwai, linux-sound, linux-kernel
On Wed, 11 Jun 2026, Takashi Iwai wrote:
> There has been already a similar fix queued in sound.git tree for-next
> branch:
> ef7607ab1c8adc6258fb1b27d08e26aecdc18a58
> ALSA: seq: midi: Serialize output teardown with event_input
>
> I believe this already addressed the bug.
>
> Let me know if there is still anything missing.
Hi Takashi,
Thanks for checking, and you're right -- ef7607ab1c8a ("ALSA: seq: midi:
Serialize output teardown with event_input") addresses the same race my
patch targeted: event_process_midi() borrowing msynth->output_rfile.output
while a concurrent midisynth_unuse() releases the rawmidi file and frees
substream->runtime before snd_rawmidi_kernel_write1() pins the runtime
buffer. That commit serializes the output_rfile publish/clear and pairs the
output snapshot with an in-flight reference, which closes the window, so my
patch is redundant.
Please drop mine; nothing further needed. Thanks!
Doruk
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH] ALSA: seq: fix use-after-free of borrowed substream in snd-seq-midi
2026-06-15 0:28 ` Doruk Tan Ozturk
@ 2026-06-15 7:26 ` Takashi Iwai
0 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2026-06-15 7:26 UTC (permalink / raw)
To: Doruk Tan Ozturk; +Cc: tiwai, perex, tiwai, linux-sound, linux-kernel
On Mon, 15 Jun 2026 02:28:21 +0200,
Doruk Tan Ozturk wrote:
>
> On Wed, 11 Jun 2026, Takashi Iwai wrote:
> > There has been already a similar fix queued in sound.git tree for-next
> > branch:
> > ef7607ab1c8adc6258fb1b27d08e26aecdc18a58
> > ALSA: seq: midi: Serialize output teardown with event_input
> >
> > I believe this already addressed the bug.
> >
> > Let me know if there is still anything missing.
>
> Hi Takashi,
>
> Thanks for checking, and you're right -- ef7607ab1c8a ("ALSA: seq: midi:
> Serialize output teardown with event_input") addresses the same race my
> patch targeted: event_process_midi() borrowing msynth->output_rfile.output
> while a concurrent midisynth_unuse() releases the rawmidi file and frees
> substream->runtime before snd_rawmidi_kernel_write1() pins the runtime
> buffer. That commit serializes the output_rfile publish/clear and pairs the
> output snapshot with an in-flight reference, which closes the window, so my
> patch is redundant.
>
> Please drop mine; nothing further needed. Thanks!
Thanks for confirmation!
Takashi
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-06-15 7:26 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-10 20:03 [PATCH] ALSA: seq: fix use-after-free of borrowed substream in snd-seq-midi Doruk Tan Ozturk
2026-06-11 7:25 ` Takashi Iwai
2026-06-15 0:28 ` Doruk Tan Ozturk
2026-06-15 7:26 ` Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox