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

Commit ffac8981 authored by Takashi Iwai's avatar Takashi Iwai Committed by Greg Kroah-Hartman
Browse files

ALSA: seq: Avoid concurrent access to queue flags



commit bb51e669fa49feb5904f452b2991b240ef31bc97 upstream.

The queue flags are represented in bit fields and the concurrent
access may result in unexpected results.  Although the current code
should be mostly OK as it's only reading a field while writing other
fields as KCSAN reported, it's safer to cover both with a proper
spinlock protection.

This patch fixes the possible concurrent read by protecting with
q->owner_lock.  Also the queue owner field is protected as well since
it's the field to be protected by the lock itself.

Reported-by: default avatar <syzbot+65c6c92d04304d0a8efc@syzkaller.appspotmail.com>
Reported-by: default avatar <syzbot+e60ddfa48717579799dd@syzkaller.appspotmail.com>
Link: https://lore.kernel.org/r/20200214111316.26939-2-tiwai@suse.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0aa6ce52
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -415,6 +415,7 @@ int snd_seq_queue_check_access(int queueid, int client)
int snd_seq_queue_set_owner(int queueid, int client, int locked)
{
	struct snd_seq_queue *q = queueptr(queueid);
	unsigned long flags;

	if (q == NULL)
		return -EINVAL;
@@ -424,8 +425,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked)
		return -EPERM;
	}

	spin_lock_irqsave(&q->owner_lock, flags);
	q->locked = locked ? 1 : 0;
	q->owner = client;
	spin_unlock_irqrestore(&q->owner_lock, flags);
	queue_access_unlock(q);
	queuefree(q);

@@ -564,15 +567,17 @@ void snd_seq_queue_client_termination(int client)
	unsigned long flags;
	int i;
	struct snd_seq_queue *q;
	bool matched;

	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
		if ((q = queueptr(i)) == NULL)
			continue;
		spin_lock_irqsave(&q->owner_lock, flags);
		if (q->owner == client)
		matched = (q->owner == client);
		if (matched)
			q->klocked = 1;
		spin_unlock_irqrestore(&q->owner_lock, flags);
		if (q->owner == client) {
		if (matched) {
			if (q->timer->running)
				snd_seq_timer_stop(q->timer);
			snd_seq_timer_reset(q->timer);
@@ -764,6 +769,8 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
	int i, bpm;
	struct snd_seq_queue *q;
	struct snd_seq_timer *tmr;
	bool locked;
	int owner;

	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
		if ((q = queueptr(i)) == NULL)
@@ -775,9 +782,14 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
		else
			bpm = 0;

		spin_lock_irq(&q->owner_lock);
		locked = q->locked;
		owner = q->owner;
		spin_unlock_irq(&q->owner_lock);

		snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
		snd_iprintf(buffer, "owned by client    : %d\n", q->owner);
		snd_iprintf(buffer, "lock status        : %s\n", q->locked ? "Locked" : "Free");
		snd_iprintf(buffer, "owned by client    : %d\n", owner);
		snd_iprintf(buffer, "lock status        : %s\n", locked ? "Locked" : "Free");
		snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
		snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
		snd_iprintf(buffer, "timer state        : %s\n", tmr->running ? "Running" : "Stopped");