diff options
| author | Takashi Iwai <tiwai@suse.de> | 2017-02-08 12:35:39 +0100 | 
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2017-02-08 12:42:37 +0100 | 
| 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)  	}  } +static void queue_use(struct snd_seq_queue *queue, int client, int use); +  /* allocate a new queue -   * 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)  		return -ENOMEM;  	q->info_flags = info_flags; +	queue_use(q, client, 1);  	if (queue_list_add(q) < 0) {  		queue_delete(q);  		return -ENOMEM;  	} -	snd_seq_queue_use(q->queue, client, 1); /* use this queue */  	return q->queue;  } @@ -502,19 +504,9 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client,  	return result;  } - -/* 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) +/* use or unuse this queue */ +static void queue_use(struct snd_seq_queue *queue, 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 (!test_and_set_bit(client, queue->clients_bitmap))  			queue->clients++; @@ -529,6 +521,21 @@ int snd_seq_queue_use(int queueid, int client, int use)  	} else {  		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);  	queuefree(queue);  	return 0;  | 
