Commit 2eabc5ec authored by Takashi Iwai's avatar Takashi Iwai
Browse files

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



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: default avatar <syzbot+9437020c82413d00222d@syzkaller.appspotmail.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent feb68902
Loading
Loading
Loading
Loading
+2 −8
Original line number Diff line number Diff line
@@ -1897,20 +1897,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);
+8 −5
Original line number Diff line number Diff line
@@ -632,20 +632,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;
}

/*
+3 −2
Original line number Diff line number Diff line
@@ -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