diff mbox series

[v2] RDMA/siw: Fix IPv6 addr_list locking

Message ID 20190827164955.9249-1-bmt@zurich.ibm.com (mailing list archive)
State Superseded
Headers show
Series [v2] RDMA/siw: Fix IPv6 addr_list locking | expand

Commit Message

Bernard Metzler Aug. 27, 2019, 4:49 p.m. UTC
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().
- skipping IPv6 addresses flagged IFA_F_TENTATIVE
  or IFA_F_DEPRECATED

Reported-by: Bart Van Assche <bvanassche@acm.org>
Fixes: 6c52fdc244b5 ("rdma/siw: connection management")
Signed-off-by: Bernard Metzler <bmt@zurich.ibm.com>
---
 drivers/infiniband/sw/siw/siw_cm.c | 33 +++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 12 deletions(-)

Comments

Jason Gunthorpe Aug. 27, 2019, 5 p.m. UTC | #1
On Tue, Aug 27, 2019 at 06:49:55PM +0200, Bernard Metzler wrote:
> 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().
> - skipping IPv6 addresses flagged IFA_F_TENTATIVE
>   or IFA_F_DEPRECATED
> 
> Reported-by: Bart Van Assche <bvanassche@acm.org>
> Fixes: 6c52fdc244b5 ("rdma/siw: connection management")
> Signed-off-by: Bernard Metzler <bmt@zurich.ibm.com>
>  drivers/infiniband/sw/siw/siw_cm.c | 33 +++++++++++++++++++-----------
>  1 file changed, 21 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c
> index 1db5ad3d9580..c145b4ff4556 100644
> +++ b/drivers/infiniband/sw/siw/siw_cm.c
> @@ -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);
> -		list_for_each_entry(ifp, &in6_dev->addr_list, if_list) {
> -			struct sockaddr_in6 bind_addr;
> -
> +		rtnl_lock();
> +		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {

If not holding RCU then don't use the rcu list iterator..

Jason
Bernard Metzler Aug. 27, 2019, 10:01 p.m. UTC | #2
-----"Jason Gunthorpe" <jgg@ziepe.ca> wrote: -----

>To: "Bernard Metzler" <bmt@zurich.ibm.com>
>From: "Jason Gunthorpe" <jgg@ziepe.ca>
>Date: 08/27/2019 07:28PM
>Cc: linux-rdma@vger.kernel.org, bvanassche@acm.org,
>dledford@redhat.com
>Subject: [EXTERNAL] Re: [PATCH v2] RDMA/siw: Fix IPv6 addr_list
>locking
>
>On Tue, Aug 27, 2019 at 06:49:55PM +0200, Bernard Metzler wrote:
>> 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().
>> - skipping IPv6 addresses flagged IFA_F_TENTATIVE
>>   or IFA_F_DEPRECATED
>> 
>> Reported-by: Bart Van Assche <bvanassche@acm.org>
>> Fixes: 6c52fdc244b5 ("rdma/siw: connection management")
>> Signed-off-by: Bernard Metzler <bmt@zurich.ibm.com>
>>  drivers/infiniband/sw/siw/siw_cm.c | 33
>+++++++++++++++++++-----------
>>  1 file changed, 21 insertions(+), 12 deletions(-)
>> 
>> diff --git a/drivers/infiniband/sw/siw/siw_cm.c
>b/drivers/infiniband/sw/siw/siw_cm.c
>> index 1db5ad3d9580..c145b4ff4556 100644
>> +++ b/drivers/infiniband/sw/siw/siw_cm.c
>> @@ -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);
>> -		list_for_each_entry(ifp, &in6_dev->addr_list, if_list) {
>> -			struct sockaddr_in6 bind_addr;
>> -
>> +		rtnl_lock();
>> +		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
>
>If not holding RCU then don't use the rcu list iterator..
>

absolutely!
diff mbox series

Patch

diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c
index 1db5ad3d9580..c145b4ff4556 100644
--- a/drivers/infiniband/sw/siw/siw_cm.c
+++ b/drivers/infiniband/sw/siw/siw_cm.c
@@ -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);
-		list_for_each_entry(ifp, &in6_dev->addr_list, if_list) {
-			struct sockaddr_in6 bind_addr;
-
+		rtnl_lock();
+		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+			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)