Skip to content

Commit 3ed4ce9

Browse files
tiwaigregkh
authored andcommitted
ALSA: seq: More protection for concurrent write and ioctl races
commit 7bd80091567789f1c0cb70eb4737aac8bcd2b6b9 upstream. This patch is an attempt for further hardening against races between the concurrent write and ioctls. The previous fix d15d662e89fc ("ALSA: seq: Fix racy pool initializations") covered the race of the pool initialization at writer and the pool resize ioctl by the client->ioctl_mutex (CVE-2018-1000004). However, basically this mutex should be applied more widely to the whole write operation for avoiding the unexpected pool operations by another thread. The only change outside snd_seq_write() is the additional mutex argument to helper functions, so that we can unlock / relock the given mutex temporarily during schedule() call for blocking write. Fixes: d15d662e89fc ("ALSA: seq: Fix racy pool initializations") Reported-by: 范龙飞 <[email protected]> Reported-by: Nicolai Stange <[email protected]> Reviewed-and-tested-by: Nicolai Stange <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6eebd4d commit 3ed4ce9

File tree

4 files changed

+24
-13
lines changed

4 files changed

+24
-13
lines changed

sound/core/seq/seq_clientmgr.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
919919
static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
920920
struct snd_seq_event *event,
921921
struct file *file, int blocking,
922-
int atomic, int hop)
922+
int atomic, int hop,
923+
struct mutex *mutexp)
923924
{
924925
struct snd_seq_event_cell *cell;
925926
int err;
@@ -957,7 +958,8 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
957958
return -ENXIO; /* queue is not allocated */
958959

959960
/* allocate an event cell */
960-
err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
961+
err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic,
962+
file, mutexp);
961963
if (err < 0)
962964
return err;
963965

@@ -1026,12 +1028,11 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
10261028
return -ENXIO;
10271029

10281030
/* allocate the pool now if the pool is not allocated yet */
1031+
mutex_lock(&client->ioctl_mutex);
10291032
if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
1030-
mutex_lock(&client->ioctl_mutex);
10311033
err = snd_seq_pool_init(client->pool);
1032-
mutex_unlock(&client->ioctl_mutex);
10331034
if (err < 0)
1034-
return -ENOMEM;
1035+
goto out;
10351036
}
10361037

10371038
/* only process whole events */
@@ -1082,7 +1083,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
10821083
/* ok, enqueue it */
10831084
err = snd_seq_client_enqueue_event(client, &event, file,
10841085
!(file->f_flags & O_NONBLOCK),
1085-
0, 0);
1086+
0, 0, &client->ioctl_mutex);
10861087
if (err < 0)
10871088
break;
10881089

@@ -1093,6 +1094,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
10931094
written += len;
10941095
}
10951096

1097+
out:
1098+
mutex_unlock(&client->ioctl_mutex);
10961099
return written ? written : err;
10971100
}
10981101

@@ -2355,7 +2358,8 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
23552358
if (! cptr->accept_output)
23562359
result = -EPERM;
23572360
else /* send it */
2358-
result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
2361+
result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
2362+
atomic, hop, NULL);
23592363

23602364
snd_seq_client_unlock(cptr);
23612365
return result;

sound/core/seq/seq_fifo.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
123123
return -EINVAL;
124124

125125
snd_use_lock_use(&f->use_lock);
126-
err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
126+
err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
127127
if (err < 0) {
128128
if ((err == -ENOMEM) || (err == -EAGAIN))
129129
atomic_inc(&f->overflow);

sound/core/seq/seq_memory.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
221221
*/
222222
static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
223223
struct snd_seq_event_cell **cellp,
224-
int nonblock, struct file *file)
224+
int nonblock, struct file *file,
225+
struct mutex *mutexp)
225226
{
226227
struct snd_seq_event_cell *cell;
227228
unsigned long flags;
@@ -245,7 +246,11 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
245246
set_current_state(TASK_INTERRUPTIBLE);
246247
add_wait_queue(&pool->output_sleep, &wait);
247248
spin_unlock_irq(&pool->lock);
249+
if (mutexp)
250+
mutex_unlock(mutexp);
248251
schedule();
252+
if (mutexp)
253+
mutex_lock(mutexp);
249254
spin_lock_irq(&pool->lock);
250255
remove_wait_queue(&pool->output_sleep, &wait);
251256
/* interrupted? */
@@ -288,7 +293,7 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
288293
*/
289294
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
290295
struct snd_seq_event_cell **cellp, int nonblock,
291-
struct file *file)
296+
struct file *file, struct mutex *mutexp)
292297
{
293298
int ncells, err;
294299
unsigned int extlen;
@@ -305,7 +310,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
305310
if (ncells >= pool->total_elements)
306311
return -ENOMEM;
307312

308-
err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
313+
err = snd_seq_cell_alloc(pool, &cell, nonblock, file, mutexp);
309314
if (err < 0)
310315
return err;
311316

@@ -331,7 +336,8 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
331336
int size = sizeof(struct snd_seq_event);
332337
if (len < size)
333338
size = len;
334-
err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
339+
err = snd_seq_cell_alloc(pool, &tmp, nonblock, file,
340+
mutexp);
335341
if (err < 0)
336342
goto __error;
337343
if (cell->event.data.ext.ptr == NULL)

sound/core/seq/seq_memory.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ struct snd_seq_pool {
6666
void snd_seq_cell_free(struct snd_seq_event_cell *cell);
6767

6868
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
69-
struct snd_seq_event_cell **cellp, int nonblock, struct file *file);
69+
struct snd_seq_event_cell **cellp, int nonblock,
70+
struct file *file, struct mutex *mutexp);
7071

7172
/* return number of unused (free) cells */
7273
static inline int snd_seq_unused_cells(struct snd_seq_pool *pool)

0 commit comments

Comments
 (0)