diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-02-08 06:35:39 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2017-02-08 06:42:37 -0500 |
commit | 4842e98f26dd80be3623c4714a244ba52ea096a8 (patch) | |
tree | 08e56d4db3d9f4d720fbaf3091d31ff219c5b58e | |
parent | f3d83317a69e7d658e7c83e24f8b31ac533c39e3 (diff) |
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>
-rw-r--r-- | sound/core/seq/seq_queue.c | 33 |
1 files changed, 20 insertions, 13 deletions
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 0bec02e89d51..450c5187eecb 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c | |||
@@ -181,6 +181,8 @@ void __exit snd_seq_queues_delete(void) | |||
181 | } | 181 | } |
182 | } | 182 | } |
183 | 183 | ||
184 | static void queue_use(struct snd_seq_queue *queue, int client, int use); | ||
185 | |||
184 | /* allocate a new queue - | 186 | /* allocate a new queue - |
185 | * return queue index value or negative value for error | 187 | * return queue index value or negative value for error |
186 | */ | 188 | */ |
@@ -192,11 +194,11 @@ int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags) | |||
192 | if (q == NULL) | 194 | if (q == NULL) |
193 | return -ENOMEM; | 195 | return -ENOMEM; |
194 | q->info_flags = info_flags; | 196 | q->info_flags = info_flags; |
197 | queue_use(q, client, 1); | ||
195 | if (queue_list_add(q) < 0) { | 198 | if (queue_list_add(q) < 0) { |
196 | queue_delete(q); | 199 | queue_delete(q); |
197 | return -ENOMEM; | 200 | return -ENOMEM; |
198 | } | 201 | } |
199 | snd_seq_queue_use(q->queue, client, 1); /* use this queue */ | ||
200 | return q->queue; | 202 | return q->queue; |
201 | } | 203 | } |
202 | 204 | ||
@@ -502,19 +504,9 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client, | |||
502 | return result; | 504 | return result; |
503 | } | 505 | } |
504 | 506 | ||
505 | 507 | /* use or unuse this queue */ | |
506 | /* use or unuse this queue - | 508 | static void queue_use(struct snd_seq_queue *queue, int client, int use) |
507 | * if it is the first client, starts the timer. | ||
508 | * if it is not longer used by any clients, stop the timer. | ||
509 | */ | ||
510 | int snd_seq_queue_use(int queueid, int client, int use) | ||
511 | { | 509 | { |
512 | struct snd_seq_queue *queue; | ||
513 | |||
514 | queue = queueptr(queueid); | ||
515 | if (queue == NULL) | ||
516 | return -EINVAL; | ||
517 | mutex_lock(&queue->timer_mutex); | ||
518 | if (use) { | 510 | if (use) { |
519 | if (!test_and_set_bit(client, queue->clients_bitmap)) | 511 | if (!test_and_set_bit(client, queue->clients_bitmap)) |
520 | queue->clients++; | 512 | queue->clients++; |
@@ -529,6 +521,21 @@ int snd_seq_queue_use(int queueid, int client, int use) | |||
529 | } else { | 521 | } else { |
530 | snd_seq_timer_close(queue); | 522 | snd_seq_timer_close(queue); |
531 | } | 523 | } |
524 | } | ||
525 | |||
526 | /* use or unuse this queue - | ||
527 | * if it is the first client, starts the timer. | ||
528 | * if it is not longer used by any clients, stop the timer. | ||
529 | */ | ||
530 | int snd_seq_queue_use(int queueid, int client, int use) | ||
531 | { | ||
532 | struct snd_seq_queue *queue; | ||
533 | |||
534 | queue = queueptr(queueid); | ||
535 | if (queue == NULL) | ||
536 | return -EINVAL; | ||
537 | mutex_lock(&queue->timer_mutex); | ||
538 | queue_use(queue, client, use); | ||
532 | mutex_unlock(&queue->timer_mutex); | 539 | mutex_unlock(&queue->timer_mutex); |
533 | queuefree(queue); | 540 | queuefree(queue); |
534 | return 0; | 541 | return 0; |