diff options
author | Takashi Iwai <tiwai@suse.de> | 2018-03-05 16:06:09 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2018-03-08 06:05:37 -0500 |
commit | 7bd80091567789f1c0cb70eb4737aac8bcd2b6b9 (patch) | |
tree | f89b49a8c3e7efa67e1983274ffcd60889fdf574 | |
parent | d85739367c6d56e475c281945c68fdb05ca74b4c (diff) |
ALSA: seq: More protection for concurrent write and ioctl races
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: 范龙飞 <long7573@126.com>
Reported-by: Nicolai Stange <nstange@suse.de>
Reviewed-and-tested-by: Nicolai Stange <nstange@suse.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/core/seq/seq_clientmgr.c | 18 | ||||
-rw-r--r-- | sound/core/seq/seq_fifo.c | 2 | ||||
-rw-r--r-- | sound/core/seq/seq_memory.c | 14 | ||||
-rw-r--r-- | sound/core/seq/seq_memory.h | 3 |
4 files changed, 24 insertions, 13 deletions
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index d41ce3ed62ca..1b62421dadd1 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c | |||
@@ -910,7 +910,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop) | |||
910 | static int snd_seq_client_enqueue_event(struct snd_seq_client *client, | 910 | static int snd_seq_client_enqueue_event(struct snd_seq_client *client, |
911 | struct snd_seq_event *event, | 911 | struct snd_seq_event *event, |
912 | struct file *file, int blocking, | 912 | struct file *file, int blocking, |
913 | int atomic, int hop) | 913 | int atomic, int hop, |
914 | struct mutex *mutexp) | ||
914 | { | 915 | { |
915 | struct snd_seq_event_cell *cell; | 916 | struct snd_seq_event_cell *cell; |
916 | int err; | 917 | int err; |
@@ -948,7 +949,8 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client, | |||
948 | return -ENXIO; /* queue is not allocated */ | 949 | return -ENXIO; /* queue is not allocated */ |
949 | 950 | ||
950 | /* allocate an event cell */ | 951 | /* allocate an event cell */ |
951 | err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file); | 952 | err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, |
953 | file, mutexp); | ||
952 | if (err < 0) | 954 | if (err < 0) |
953 | return err; | 955 | return err; |
954 | 956 | ||
@@ -1017,12 +1019,11 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, | |||
1017 | return -ENXIO; | 1019 | return -ENXIO; |
1018 | 1020 | ||
1019 | /* allocate the pool now if the pool is not allocated yet */ | 1021 | /* allocate the pool now if the pool is not allocated yet */ |
1022 | mutex_lock(&client->ioctl_mutex); | ||
1020 | if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { | 1023 | if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { |
1021 | mutex_lock(&client->ioctl_mutex); | ||
1022 | err = snd_seq_pool_init(client->pool); | 1024 | err = snd_seq_pool_init(client->pool); |
1023 | mutex_unlock(&client->ioctl_mutex); | ||
1024 | if (err < 0) | 1025 | if (err < 0) |
1025 | return -ENOMEM; | 1026 | goto out; |
1026 | } | 1027 | } |
1027 | 1028 | ||
1028 | /* only process whole events */ | 1029 | /* only process whole events */ |
@@ -1073,7 +1074,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, | |||
1073 | /* ok, enqueue it */ | 1074 | /* ok, enqueue it */ |
1074 | err = snd_seq_client_enqueue_event(client, &event, file, | 1075 | err = snd_seq_client_enqueue_event(client, &event, file, |
1075 | !(file->f_flags & O_NONBLOCK), | 1076 | !(file->f_flags & O_NONBLOCK), |
1076 | 0, 0); | 1077 | 0, 0, &client->ioctl_mutex); |
1077 | if (err < 0) | 1078 | if (err < 0) |
1078 | break; | 1079 | break; |
1079 | 1080 | ||
@@ -1084,6 +1085,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, | |||
1084 | written += len; | 1085 | written += len; |
1085 | } | 1086 | } |
1086 | 1087 | ||
1088 | out: | ||
1089 | mutex_unlock(&client->ioctl_mutex); | ||
1087 | return written ? written : err; | 1090 | return written ? written : err; |
1088 | } | 1091 | } |
1089 | 1092 | ||
@@ -2263,7 +2266,8 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev, | |||
2263 | if (! cptr->accept_output) | 2266 | if (! cptr->accept_output) |
2264 | result = -EPERM; | 2267 | result = -EPERM; |
2265 | else /* send it */ | 2268 | else /* send it */ |
2266 | result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop); | 2269 | result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, |
2270 | atomic, hop, NULL); | ||
2267 | 2271 | ||
2268 | snd_seq_client_unlock(cptr); | 2272 | snd_seq_client_unlock(cptr); |
2269 | return result; | 2273 | return result; |
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index a8c2822e0198..72c0302a55d2 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c | |||
@@ -125,7 +125,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, | |||
125 | return -EINVAL; | 125 | return -EINVAL; |
126 | 126 | ||
127 | snd_use_lock_use(&f->use_lock); | 127 | snd_use_lock_use(&f->use_lock); |
128 | err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */ | 128 | err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */ |
129 | if (err < 0) { | 129 | if (err < 0) { |
130 | if ((err == -ENOMEM) || (err == -EAGAIN)) | 130 | if ((err == -ENOMEM) || (err == -EAGAIN)) |
131 | atomic_inc(&f->overflow); | 131 | atomic_inc(&f->overflow); |
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index f763682584a8..ab1112e90f88 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c | |||
@@ -220,7 +220,8 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell) | |||
220 | */ | 220 | */ |
221 | static int snd_seq_cell_alloc(struct snd_seq_pool *pool, | 221 | static int snd_seq_cell_alloc(struct snd_seq_pool *pool, |
222 | struct snd_seq_event_cell **cellp, | 222 | struct snd_seq_event_cell **cellp, |
223 | int nonblock, struct file *file) | 223 | int nonblock, struct file *file, |
224 | struct mutex *mutexp) | ||
224 | { | 225 | { |
225 | struct snd_seq_event_cell *cell; | 226 | struct snd_seq_event_cell *cell; |
226 | unsigned long flags; | 227 | unsigned long flags; |
@@ -244,7 +245,11 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool, | |||
244 | set_current_state(TASK_INTERRUPTIBLE); | 245 | set_current_state(TASK_INTERRUPTIBLE); |
245 | add_wait_queue(&pool->output_sleep, &wait); | 246 | add_wait_queue(&pool->output_sleep, &wait); |
246 | spin_unlock_irq(&pool->lock); | 247 | spin_unlock_irq(&pool->lock); |
248 | if (mutexp) | ||
249 | mutex_unlock(mutexp); | ||
247 | schedule(); | 250 | schedule(); |
251 | if (mutexp) | ||
252 | mutex_lock(mutexp); | ||
248 | spin_lock_irq(&pool->lock); | 253 | spin_lock_irq(&pool->lock); |
249 | remove_wait_queue(&pool->output_sleep, &wait); | 254 | remove_wait_queue(&pool->output_sleep, &wait); |
250 | /* interrupted? */ | 255 | /* interrupted? */ |
@@ -287,7 +292,7 @@ __error: | |||
287 | */ | 292 | */ |
288 | int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, | 293 | int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, |
289 | struct snd_seq_event_cell **cellp, int nonblock, | 294 | struct snd_seq_event_cell **cellp, int nonblock, |
290 | struct file *file) | 295 | struct file *file, struct mutex *mutexp) |
291 | { | 296 | { |
292 | int ncells, err; | 297 | int ncells, err; |
293 | unsigned int extlen; | 298 | unsigned int extlen; |
@@ -304,7 +309,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, | |||
304 | if (ncells >= pool->total_elements) | 309 | if (ncells >= pool->total_elements) |
305 | return -ENOMEM; | 310 | return -ENOMEM; |
306 | 311 | ||
307 | err = snd_seq_cell_alloc(pool, &cell, nonblock, file); | 312 | err = snd_seq_cell_alloc(pool, &cell, nonblock, file, mutexp); |
308 | if (err < 0) | 313 | if (err < 0) |
309 | return err; | 314 | return err; |
310 | 315 | ||
@@ -330,7 +335,8 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, | |||
330 | int size = sizeof(struct snd_seq_event); | 335 | int size = sizeof(struct snd_seq_event); |
331 | if (len < size) | 336 | if (len < size) |
332 | size = len; | 337 | size = len; |
333 | err = snd_seq_cell_alloc(pool, &tmp, nonblock, file); | 338 | err = snd_seq_cell_alloc(pool, &tmp, nonblock, file, |
339 | mutexp); | ||
334 | if (err < 0) | 340 | if (err < 0) |
335 | goto __error; | 341 | goto __error; |
336 | if (cell->event.data.ext.ptr == NULL) | 342 | if (cell->event.data.ext.ptr == NULL) |
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h index 32f959c17786..3abe306c394a 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h | |||
@@ -66,7 +66,8 @@ struct snd_seq_pool { | |||
66 | void snd_seq_cell_free(struct snd_seq_event_cell *cell); | 66 | void snd_seq_cell_free(struct snd_seq_event_cell *cell); |
67 | 67 | ||
68 | int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, | 68 | 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); | ||
70 | 71 | ||
71 | /* return number of unused (free) cells */ | 72 | /* return number of unused (free) cells */ |
72 | static inline int snd_seq_unused_cells(struct snd_seq_pool *pool) | 73 | static inline int snd_seq_unused_cells(struct snd_seq_pool *pool) |