Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 531a64e4 authored by Bernard Metzler's avatar Bernard Metzler Committed by Doug Ledford
Browse files

RDMA/siw: Fix IPv6 addr_list locking



Walking the address list of an inet6_dev requires
appropriate locking. Since the called function
siw_listen_address() may sleep, we have to use
rtnl_lock() instead of read_lock_bh().

Also introduces sanity checks if we got a device
from in_dev_get() or in6_dev_get().

Reported-by: default avatarBart Van Assche <bvanassche@acm.org>
Fixes: 6c52fdc2 ("rdma/siw: connection management")
Signed-off-by: default avatarBernard Metzler <bmt@zurich.ibm.com>
Link: https://lore.kernel.org/r/20190828130355.22830-1-bmt@zurich.ibm.com


Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent c536277e
Loading
Loading
Loading
Loading
+20 −11
Original line number Diff line number Diff line
@@ -1962,6 +1962,10 @@ int siw_create_listen(struct iw_cm_id *id, int backlog)
		struct sockaddr_in s_laddr, *s_raddr;
		const struct in_ifaddr *ifa;

		if (!in_dev) {
			rv = -ENODEV;
			goto out;
		}
		memcpy(&s_laddr, &id->local_addr, sizeof(s_laddr));
		s_raddr = (struct sockaddr_in *)&id->remote_addr;

@@ -1991,22 +1995,27 @@ int siw_create_listen(struct iw_cm_id *id, int backlog)
		struct sockaddr_in6 *s_laddr = &to_sockaddr_in6(id->local_addr),
			*s_raddr = &to_sockaddr_in6(id->remote_addr);

		if (!in6_dev) {
			rv = -ENODEV;
			goto out;
		}
		siw_dbg(id->device,
			"laddr %pI6:%d, raddr %pI6:%d\n",
			&s_laddr->sin6_addr, ntohs(s_laddr->sin6_port),
			&s_raddr->sin6_addr, ntohs(s_raddr->sin6_port));

		read_lock_bh(&in6_dev->lock);
		rtnl_lock();
		list_for_each_entry(ifp, &in6_dev->addr_list, if_list) {
			struct sockaddr_in6 bind_addr;

			if (ifp->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
				continue;
			if (ipv6_addr_any(&s_laddr->sin6_addr) ||
			    ipv6_addr_equal(&s_laddr->sin6_addr, &ifp->addr)) {
				bind_addr.sin6_family = AF_INET6;
				bind_addr.sin6_port = s_laddr->sin6_port;
				bind_addr.sin6_flowinfo = 0;
				bind_addr.sin6_addr = ifp->addr;
				bind_addr.sin6_scope_id = dev->ifindex;
				struct sockaddr_in6 bind_addr  = {
					.sin6_family = AF_INET6,
					.sin6_port = s_laddr->sin6_port,
					.sin6_flowinfo = 0,
					.sin6_addr = ifp->addr,
					.sin6_scope_id = dev->ifindex };

				rv = siw_listen_address(id, backlog,
						(struct sockaddr *)&bind_addr,
@@ -2015,12 +2024,12 @@ int siw_create_listen(struct iw_cm_id *id, int backlog)
					listeners++;
			}
		}
		read_unlock_bh(&in6_dev->lock);

		rtnl_unlock();
		in6_dev_put(in6_dev);
	} else {
		return -EAFNOSUPPORT;
		rv = -EAFNOSUPPORT;
	}
out:
	if (listeners)
		rv = 0;
	else if (!rv)