]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ALSA: seq: Fix kernel heap address leak in bounce_error_event()
authorHanQuan <eilaimemedsnaimel@gmail.com>
Fri, 12 Jun 2026 10:32:22 +0000 (10:32 +0000)
committerTakashi Iwai <tiwai@suse.de>
Fri, 12 Jun 2026 11:26:31 +0000 (13:26 +0200)
The comment above bounce_error_event() documents that user clients
should receive SNDRV_SEQ_EVENT_BOUNCE with the original event embedded
as variable-length data, while kernel clients should receive
SNDRV_SEQ_EVENT_KERNEL_ERROR with a quoted kernel pointer.

However, the implementation unconditionally uses
SNDRV_SEQ_EVENT_KERNEL_ERROR with data.quote.event set to the raw
struct snd_seq_event pointer for all clients.  When a bounce error
event is delivered to a USER_CLIENT via snd_seq_read(), the kernel
heap address in data.quote.event is exposed to userspace through
copy_to_user() in the fixed-length branch.

This is a distinct leak path from the one addressed by commit
705dd6dcbc0e ("ALSA: seq: Clear variable event pointer on read"),
which sanitizes data.ext.ptr in the variable-length branch of
snd_seq_read().  The bounce_error_event() leak uses fixed-length
events that take the else branch where no sanitization occurs.

Differentiate the bounce event by client type.  For USER_CLIENT,
send SNDRV_SEQ_EVENT_BOUNCE with SNDRV_SEQ_EVENT_LENGTH_VARIABLE
and data.ext pointing to the original event.  The variable-length
path in snd_seq_event_dup() copies the event data into chained
cells, and snd_seq_expand_var_event() copies only the content --
never the pointer -- to userspace.  For KERNEL_CLIENT, keep the
existing SNDRV_SEQ_EVENT_KERNEL_ERROR behavior with the quoted
pointer.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: HanQuan <eilaimemedsnaimel@gmail.com>
Link: https://patch.msgid.link/20260612103222.2528305-1-eilaimemedsnaimel@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/core/seq/seq_clientmgr.c

index 4dda8268018ec0ef8b41631f3e1656bac0f49a35..0ac6d92058d9a45ad5272d512e0379e01baf1989 100644 (file)
@@ -538,16 +538,38 @@ static int bounce_error_event(struct snd_seq_client *client,
 
        /* set up quoted error */
        memset(&bounce_ev, 0, sizeof(bounce_ev));
-       bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
-       bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+
+       if (client->type == USER_CLIENT) {
+               /*
+                * For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the
+                * original event embedded as variable-length data.  This
+                * avoids exposing data.quote.event (a kernel pointer) to
+                * userspace.  The variable-length path in snd_seq_event_dup()
+                * copies the event data from data.ext.ptr into chained cells,
+                * and snd_seq_expand_var_event() copies only the data content
+                * -- never the pointer -- to userspace.
+                */
+               bounce_ev.type = SNDRV_SEQ_EVENT_BOUNCE;
+               bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+               bounce_ev.data.ext.len = sizeof(struct snd_seq_event);
+               bounce_ev.data.ext.ptr = (char *)event;
+       } else {
+               /*
+                * For kernel clients, quote the event pointer directly.
+                * Kernel consumers can safely dereference the pointer.
+                */
+               bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
+               bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+               bounce_ev.data.quote.origin = event->dest;
+               bounce_ev.data.quote.event = event;
+               bounce_ev.data.quote.value = -err; /* use positive value */
+       }
+
        bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
        bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
        bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
        bounce_ev.dest.client = client->number;
        bounce_ev.dest.port = event->source.port;
-       bounce_ev.data.quote.origin = event->dest;
-       bounce_ev.data.quote.event = event;
-       bounce_ev.data.quote.value = -err; /* use positive value */
        result = snd_seq_deliver_single_event(NULL, &bounce_ev, atomic, hop + 1);
        if (result < 0) {
                client->event_lost++;