ALSA: seq: Fix race of get-subscription call vs port-delete ioctls

[ Upstream commit 2eabc5ec8a ]

The snd_seq_ioctl_get_subscription() retrieves the port subscriber
information as a pointer, while the object isn't protected, hence it
may be deleted before the actual reference.  This race was spotted by
syzkaller and may lead to a UAF.

The fix is simply copying the data in the lookup function that
performs in the rwsem to protect against the deletion.

Reported-by: syzbot+9437020c82413d00222d@syzkaller.appspotmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Takashi Iwai 2019-04-09 18:04:17 +02:00 committed by Greg Kroah-Hartman
parent b52fd8af8d
commit 731ebeeda5
3 changed files with 13 additions and 15 deletions

View file

@ -1900,20 +1900,14 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
int result;
struct snd_seq_client *sender = NULL;
struct snd_seq_client_port *sport = NULL;
struct snd_seq_subscribers *p;
result = -EINVAL;
if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
goto __end;
if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
goto __end;
p = snd_seq_port_get_subscription(&sport->c_src, &subs->dest);
if (p) {
result = 0;
*subs = p->info;
} else
result = -ENOENT;
result = snd_seq_port_get_subscription(&sport->c_src, &subs->dest,
subs);
__end:
if (sport)
snd_seq_port_unlock(sport);

View file

@ -635,20 +635,23 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
/* get matched subscriber */
struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr)
int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr,
struct snd_seq_port_subscribe *subs)
{
struct snd_seq_subscribers *s, *found = NULL;
struct snd_seq_subscribers *s;
int err = -ENOENT;
down_read(&src_grp->list_mutex);
list_for_each_entry(s, &src_grp->list_head, src_list) {
if (addr_match(dest_addr, &s->info.dest)) {
found = s;
*subs = s->info;
err = 0;
break;
}
}
up_read(&src_grp->list_mutex);
return found;
return err;
}
/*

View file

@ -135,7 +135,8 @@ int snd_seq_port_subscribe(struct snd_seq_client_port *port,
struct snd_seq_port_subscribe *info);
/* get matched subscriber */
struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr);
int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr,
struct snd_seq_port_subscribe *subs);
#endif