aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2017-10-09 05:09:20 -0400
committerTakashi Iwai <tiwai@suse.de>2017-10-11 03:58:18 -0400
commit71105998845fb012937332fe2e806d443c09e026 (patch)
treeef3e01e939b553e41d7f411a875775888e78b2e2
parent124751d5e63c823092060074bd0abaae61aaa9c4 (diff)
ALSA: seq: Fix use-after-free at creating a port
There is a potential race window opened at creating and deleting a port via ioctl, as spotted by fuzzing. snd_seq_create_port() creates a port object and returns its pointer, but it doesn't take the refcount, thus it can be deleted immediately by another thread. Meanwhile, snd_seq_ioctl_create_port() still calls the function snd_seq_system_client_ev_port_start() with the created port object that is being deleted, and this triggers use-after-free like: BUG: KASAN: use-after-free in snd_seq_ioctl_create_port+0x504/0x630 [snd_seq] at addr ffff8801f2241cb1 ============================================================================= BUG kmalloc-512 (Tainted: G B ): kasan: bad access detected ----------------------------------------------------------------------------- INFO: Allocated in snd_seq_create_port+0x94/0x9b0 [snd_seq] age=1 cpu=3 pid=4511 ___slab_alloc+0x425/0x460 __slab_alloc+0x20/0x40 kmem_cache_alloc_trace+0x150/0x190 snd_seq_create_port+0x94/0x9b0 [snd_seq] snd_seq_ioctl_create_port+0xd1/0x630 [snd_seq] snd_seq_do_ioctl+0x11c/0x190 [snd_seq] snd_seq_ioctl+0x40/0x80 [snd_seq] do_vfs_ioctl+0x54b/0xda0 SyS_ioctl+0x79/0x90 entry_SYSCALL_64_fastpath+0x16/0x75 INFO: Freed in port_delete+0x136/0x1a0 [snd_seq] age=1 cpu=2 pid=4717 __slab_free+0x204/0x310 kfree+0x15f/0x180 port_delete+0x136/0x1a0 [snd_seq] snd_seq_delete_port+0x235/0x350 [snd_seq] snd_seq_ioctl_delete_port+0xc8/0x180 [snd_seq] snd_seq_do_ioctl+0x11c/0x190 [snd_seq] snd_seq_ioctl+0x40/0x80 [snd_seq] do_vfs_ioctl+0x54b/0xda0 SyS_ioctl+0x79/0x90 entry_SYSCALL_64_fastpath+0x16/0x75 Call Trace: [<ffffffff81b03781>] dump_stack+0x63/0x82 [<ffffffff81531b3b>] print_trailer+0xfb/0x160 [<ffffffff81536db4>] object_err+0x34/0x40 [<ffffffff815392d3>] kasan_report.part.2+0x223/0x520 [<ffffffffa07aadf4>] ? snd_seq_ioctl_create_port+0x504/0x630 [snd_seq] [<ffffffff815395fe>] __asan_report_load1_noabort+0x2e/0x30 [<ffffffffa07aadf4>] snd_seq_ioctl_create_port+0x504/0x630 [snd_seq] [<ffffffffa07aa8f0>] ? snd_seq_ioctl_delete_port+0x180/0x180 [snd_seq] [<ffffffff8136be50>] ? taskstats_exit+0xbc0/0xbc0 [<ffffffffa07abc5c>] snd_seq_do_ioctl+0x11c/0x190 [snd_seq] [<ffffffffa07abd10>] snd_seq_ioctl+0x40/0x80 [snd_seq] [<ffffffff8136d433>] ? acct_account_cputime+0x63/0x80 [<ffffffff815b515b>] do_vfs_ioctl+0x54b/0xda0 ..... We may fix this in a few different ways, and in this patch, it's fixed simply by taking the refcount properly at snd_seq_create_port() and letting the caller unref the object after use. Also, there is another potential use-after-free by sprintf() call in snd_seq_create_port(), and this is moved inside the lock. This fix covers CVE-2017-15265. Reported-and-tested-by: Michael23 Yu <ycqzsy@gmail.com> Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/core/seq/seq_clientmgr.c6
-rw-r--r--sound/core/seq/seq_ports.c7
2 files changed, 10 insertions, 3 deletions
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index ea2d0ae85bd3..6c9cba2166d9 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1259,6 +1259,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
1259 struct snd_seq_port_info *info = arg; 1259 struct snd_seq_port_info *info = arg;
1260 struct snd_seq_client_port *port; 1260 struct snd_seq_client_port *port;
1261 struct snd_seq_port_callback *callback; 1261 struct snd_seq_port_callback *callback;
1262 int port_idx;
1262 1263
1263 /* it is not allowed to create the port for an another client */ 1264 /* it is not allowed to create the port for an another client */
1264 if (info->addr.client != client->number) 1265 if (info->addr.client != client->number)
@@ -1269,7 +1270,9 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
1269 return -ENOMEM; 1270 return -ENOMEM;
1270 1271
1271 if (client->type == USER_CLIENT && info->kernel) { 1272 if (client->type == USER_CLIENT && info->kernel) {
1272 snd_seq_delete_port(client, port->addr.port); 1273 port_idx = port->addr.port;
1274 snd_seq_port_unlock(port);
1275 snd_seq_delete_port(client, port_idx);
1273 return -EINVAL; 1276 return -EINVAL;
1274 } 1277 }
1275 if (client->type == KERNEL_CLIENT) { 1278 if (client->type == KERNEL_CLIENT) {
@@ -1290,6 +1293,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
1290 1293
1291 snd_seq_set_port_info(port, info); 1294 snd_seq_set_port_info(port, info);
1292 snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); 1295 snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
1296 snd_seq_port_unlock(port);
1293 1297
1294 return 0; 1298 return 0;
1295} 1299}
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 0a7020c82bfc..d21ece9f8d73 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -122,7 +122,9 @@ static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
122} 122}
123 123
124 124
125/* create a port, port number is returned (-1 on failure) */ 125/* create a port, port number is returned (-1 on failure);
126 * the caller needs to unref the port via snd_seq_port_unlock() appropriately
127 */
126struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, 128struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
127 int port) 129 int port)
128{ 130{
@@ -151,6 +153,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
151 snd_use_lock_init(&new_port->use_lock); 153 snd_use_lock_init(&new_port->use_lock);
152 port_subs_info_init(&new_port->c_src); 154 port_subs_info_init(&new_port->c_src);
153 port_subs_info_init(&new_port->c_dest); 155 port_subs_info_init(&new_port->c_dest);
156 snd_use_lock_use(&new_port->use_lock);
154 157
155 num = port >= 0 ? port : 0; 158 num = port >= 0 ? port : 0;
156 mutex_lock(&client->ports_mutex); 159 mutex_lock(&client->ports_mutex);
@@ -165,9 +168,9 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
165 list_add_tail(&new_port->list, &p->list); 168 list_add_tail(&new_port->list, &p->list);
166 client->num_ports++; 169 client->num_ports++;
167 new_port->addr.port = num; /* store the port number in the port */ 170 new_port->addr.port = num; /* store the port number in the port */
171 sprintf(new_port->name, "port-%d", num);
168 write_unlock_irqrestore(&client->ports_lock, flags); 172 write_unlock_irqrestore(&client->ports_lock, flags);
169 mutex_unlock(&client->ports_mutex); 173 mutex_unlock(&client->ports_mutex);
170 sprintf(new_port->name, "port-%d", num);
171 174
172 return new_port; 175 return new_port;
173} 176}