Commit 71105998 authored by Takashi Iwai's avatar Takashi Iwai

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: default avatarMichael23 Yu <ycqzsy@gmail.com>
Suggested-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 124751d5
...@@ -1259,6 +1259,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg) ...@@ -1259,6 +1259,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
struct snd_seq_port_info *info = arg; struct snd_seq_port_info *info = arg;
struct snd_seq_client_port *port; struct snd_seq_client_port *port;
struct snd_seq_port_callback *callback; struct snd_seq_port_callback *callback;
int port_idx;
/* it is not allowed to create the port for an another client */ /* it is not allowed to create the port for an another client */
if (info->addr.client != client->number) 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,7 +1270,9 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
return -ENOMEM; return -ENOMEM;
if (client->type == USER_CLIENT && info->kernel) { if (client->type == USER_CLIENT && info->kernel) {
snd_seq_delete_port(client, port->addr.port); port_idx = port->addr.port;
snd_seq_port_unlock(port);
snd_seq_delete_port(client, port_idx);
return -EINVAL; return -EINVAL;
} }
if (client->type == KERNEL_CLIENT) { if (client->type == KERNEL_CLIENT) {
...@@ -1290,6 +1293,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg) ...@@ -1290,6 +1293,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
snd_seq_set_port_info(port, info); snd_seq_set_port_info(port, info);
snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
snd_seq_port_unlock(port);
return 0; return 0;
} }
......
...@@ -122,7 +122,9 @@ static void port_subs_info_init(struct snd_seq_port_subs_info *grp) ...@@ -122,7 +122,9 @@ static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
} }
/* create a port, port number is returned (-1 on failure) */ /* create a port, port number is returned (-1 on failure);
* the caller needs to unref the port via snd_seq_port_unlock() appropriately
*/
struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
int port) int port)
{ {
...@@ -151,6 +153,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, ...@@ -151,6 +153,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
snd_use_lock_init(&new_port->use_lock); snd_use_lock_init(&new_port->use_lock);
port_subs_info_init(&new_port->c_src); port_subs_info_init(&new_port->c_src);
port_subs_info_init(&new_port->c_dest); port_subs_info_init(&new_port->c_dest);
snd_use_lock_use(&new_port->use_lock);
num = port >= 0 ? port : 0; num = port >= 0 ? port : 0;
mutex_lock(&client->ports_mutex); mutex_lock(&client->ports_mutex);
...@@ -165,9 +168,9 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, ...@@ -165,9 +168,9 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
list_add_tail(&new_port->list, &p->list); list_add_tail(&new_port->list, &p->list);
client->num_ports++; client->num_ports++;
new_port->addr.port = num; /* store the port number in the port */ new_port->addr.port = num; /* store the port number in the port */
sprintf(new_port->name, "port-%d", num);
write_unlock_irqrestore(&client->ports_lock, flags); write_unlock_irqrestore(&client->ports_lock, flags);
mutex_unlock(&client->ports_mutex); mutex_unlock(&client->ports_mutex);
sprintf(new_port->name, "port-%d", num);
return new_port; return new_port;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment