Skip to content

Commit bb51e66

Browse files
committed
ALSA: seq: Avoid concurrent access to queue flags
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: [email protected] Reported-by: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 0fbb027 commit bb51e66

File tree

1 file changed

+16
-4
lines changed

1 file changed

+16
-4
lines changed

sound/core/seq/seq_queue.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ int snd_seq_queue_check_access(int queueid, int client)
392392
int snd_seq_queue_set_owner(int queueid, int client, int locked)
393393
{
394394
struct snd_seq_queue *q = queueptr(queueid);
395+
unsigned long flags;
395396

396397
if (q == NULL)
397398
return -EINVAL;
@@ -401,8 +402,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked)
401402
return -EPERM;
402403
}
403404

405+
spin_lock_irqsave(&q->owner_lock, flags);
404406
q->locked = locked ? 1 : 0;
405407
q->owner = client;
408+
spin_unlock_irqrestore(&q->owner_lock, flags);
406409
queue_access_unlock(q);
407410
queuefree(q);
408411

@@ -539,15 +542,17 @@ void snd_seq_queue_client_termination(int client)
539542
unsigned long flags;
540543
int i;
541544
struct snd_seq_queue *q;
545+
bool matched;
542546

543547
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
544548
if ((q = queueptr(i)) == NULL)
545549
continue;
546550
spin_lock_irqsave(&q->owner_lock, flags);
547-
if (q->owner == client)
551+
matched = (q->owner == client);
552+
if (matched)
548553
q->klocked = 1;
549554
spin_unlock_irqrestore(&q->owner_lock, flags);
550-
if (q->owner == client) {
555+
if (matched) {
551556
if (q->timer->running)
552557
snd_seq_timer_stop(q->timer);
553558
snd_seq_timer_reset(q->timer);
@@ -739,6 +744,8 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
739744
int i, bpm;
740745
struct snd_seq_queue *q;
741746
struct snd_seq_timer *tmr;
747+
bool locked;
748+
int owner;
742749

743750
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
744751
if ((q = queueptr(i)) == NULL)
@@ -750,9 +757,14 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
750757
else
751758
bpm = 0;
752759

760+
spin_lock_irq(&q->owner_lock);
761+
locked = q->locked;
762+
owner = q->owner;
763+
spin_unlock_irq(&q->owner_lock);
764+
753765
snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
754-
snd_iprintf(buffer, "owned by client : %d\n", q->owner);
755-
snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free");
766+
snd_iprintf(buffer, "owned by client : %d\n", owner);
767+
snd_iprintf(buffer, "lock status : %s\n", locked ? "Locked" : "Free");
756768
snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
757769
snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
758770
snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");

0 commit comments

Comments
 (0)