Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4842e98f authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: seq: Fix race at creating a queue



When a sequencer queue is created in snd_seq_queue_alloc(),it adds the
new queue element to the public list before referencing it.  Thus the
queue might be deleted before the call of snd_seq_queue_use(), and it
results in the use-after-free error, as spotted by syzkaller.

The fix is to reference the queue object at the right time.

Reported-by: default avatarDmitry Vyukov <dvyukov@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent f3d83317
Loading
Loading
Loading
Loading
+20 −13
Original line number Diff line number Diff line
@@ -181,6 +181,8 @@ void __exit snd_seq_queues_delete(void)
	}
}

static void queue_use(struct snd_seq_queue *queue, int client, int use);

/* allocate a new queue -
 * return queue index value or negative value for error
 */
@@ -192,11 +194,11 @@ int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
	if (q == NULL)
		return -ENOMEM;
	q->info_flags = info_flags;
	queue_use(q, client, 1);
	if (queue_list_add(q) < 0) {
		queue_delete(q);
		return -ENOMEM;
	}
	snd_seq_queue_use(q->queue, client, 1); /* use this queue */
	return q->queue;
}

@@ -502,19 +504,9 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client,
	return result;
}


/* use or unuse this queue -
 * if it is the first client, starts the timer.
 * if it is not longer used by any clients, stop the timer.
 */
int snd_seq_queue_use(int queueid, int client, int use)
/* use or unuse this queue */
static void queue_use(struct snd_seq_queue *queue, int client, int use)
{
	struct snd_seq_queue *queue;

	queue = queueptr(queueid);
	if (queue == NULL)
		return -EINVAL;
	mutex_lock(&queue->timer_mutex);
	if (use) {
		if (!test_and_set_bit(client, queue->clients_bitmap))
			queue->clients++;
@@ -529,6 +521,21 @@ int snd_seq_queue_use(int queueid, int client, int use)
	} else {
		snd_seq_timer_close(queue);
	}
}

/* use or unuse this queue -
 * if it is the first client, starts the timer.
 * if it is not longer used by any clients, stop the timer.
 */
int snd_seq_queue_use(int queueid, int client, int use)
{
	struct snd_seq_queue *queue;

	queue = queueptr(queueid);
	if (queue == NULL)
		return -EINVAL;
	mutex_lock(&queue->timer_mutex);
	queue_use(queue, client, use);
	mutex_unlock(&queue->timer_mutex);
	queuefree(queue);
	return 0;