mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-04-02 04:14:14 +00:00
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: Dmitry Vyukov <dvyukov@google.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
f3d83317a6
commit
4842e98f26
1 changed files with 20 additions and 13 deletions
|
@ -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 -
|
/* allocate a new queue -
|
||||||
* return queue index value or negative value for error
|
* 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)
|
if (q == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
q->info_flags = info_flags;
|
q->info_flags = info_flags;
|
||||||
|
queue_use(q, client, 1);
|
||||||
if (queue_list_add(q) < 0) {
|
if (queue_list_add(q) < 0) {
|
||||||
queue_delete(q);
|
queue_delete(q);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
snd_seq_queue_use(q->queue, client, 1); /* use this queue */
|
|
||||||
return q->queue;
|
return q->queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,19 +504,9 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* use or unuse this queue */
|
||||||
/* use or unuse this queue -
|
static void queue_use(struct snd_seq_queue *queue, int client, int use)
|
||||||
* 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);
|
|
||||||
if (use) {
|
if (use) {
|
||||||
if (!test_and_set_bit(client, queue->clients_bitmap))
|
if (!test_and_set_bit(client, queue->clients_bitmap))
|
||||||
queue->clients++;
|
queue->clients++;
|
||||||
|
@ -529,6 +521,21 @@ int snd_seq_queue_use(int queueid, int client, int use)
|
||||||
} else {
|
} else {
|
||||||
snd_seq_timer_close(queue);
|
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);
|
mutex_unlock(&queue->timer_mutex);
|
||||||
queuefree(queue);
|
queuefree(queue);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue