diff options
| author | Takashi Iwai <tiwai@suse.de> | 2017-02-08 06:35:39 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-02-14 18:25:41 -0500 |
| commit | 5024b2fb8e49091044e450a0548979bb711588d6 (patch) | |
| tree | ef24bbe8e0fa498264157ade3c800860a609ef56 /sound/core/seq | |
| parent | 2e0feb524caccbe9c061997f63b97e4f695e816e (diff) | |
ALSA: seq: Fix race at creating a queue
commit 4842e98f26dd80be3623c4714a244ba52ea096a8 upstream.
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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound/core/seq')
| -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; |
