Skip to content

Commit

Permalink
ALSA: seq: Avoid concurrent access to queue flags
Browse files Browse the repository at this point in the history
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: [email protected]
Reported-by: [email protected]
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Takashi Iwai <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
tiwai authored and gregkh committed Feb 28, 2020
1 parent 68ff19b commit 2bc3bde
Showing 1 changed file with 16 additions and 4 deletions.
20 changes: 16 additions & 4 deletions sound/core/seq/seq_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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");
Expand Down

0 comments on commit 2bc3bde

Please sign in to comment.