Netdev List
 help / color / mirror / Atom feed
* [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
@ 2018-08-27 10:38 Xin Long
  2018-08-27 13:08 ` Neil Horman
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Xin Long @ 2018-08-27 10:38 UTC (permalink / raw)
  To: network dev, linux-sctp; +Cc: davem, Marcelo Ricardo Leitner, Neil Horman

As Marcelo noticed, in sctp_transport_get_next, it is iterating over
transports but then also accessing the association directly, without
checking any refcnts before that, which can cause an use-after-free
Read.

So fix it by holding transport before accessing the association. With
that, sctp_transport_hold calls can be removed in the later places.

Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
 net/sctp/proc.c   |  4 ----
 net/sctp/socket.c | 22 +++++++++++++++-------
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index ef5c9a8..4d6f1c8 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -264,8 +264,6 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
 	}
 
 	transport = (struct sctp_transport *)v;
-	if (!sctp_transport_hold(transport))
-		return 0;
 	assoc = transport->asoc;
 	epb = &assoc->base;
 	sk = epb->sk;
@@ -322,8 +320,6 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
 	}
 
 	transport = (struct sctp_transport *)v;
-	if (!sctp_transport_hold(transport))
-		return 0;
 	assoc = transport->asoc;
 
 	list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index e96b15a..aa76586 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -5005,9 +5005,14 @@ struct sctp_transport *sctp_transport_get_next(struct net *net,
 			break;
 		}
 
+		if (!sctp_transport_hold(t))
+			continue;
+
 		if (net_eq(sock_net(t->asoc->base.sk), net) &&
 		    t->asoc->peer.primary_path == t)
 			break;
+
+		sctp_transport_put(t);
 	}
 
 	return t;
@@ -5017,13 +5022,18 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
 					      struct rhashtable_iter *iter,
 					      int pos)
 {
-	void *obj = SEQ_START_TOKEN;
+	struct sctp_transport *t;
 
-	while (pos && (obj = sctp_transport_get_next(net, iter)) &&
-	       !IS_ERR(obj))
-		pos--;
+	if (!pos)
+		return SEQ_START_TOKEN;
 
-	return obj;
+	while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
+		if (!--pos)
+			break;
+		sctp_transport_put(t);
+	}
+
+	return t;
 }
 
 int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
@@ -5082,8 +5092,6 @@ int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
 
 	tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
 	for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
-		if (!sctp_transport_hold(tsp))
-			continue;
 		ret = cb(tsp, p);
 		if (ret)
 			break;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
  2018-08-27 10:38 [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next Xin Long
@ 2018-08-27 13:08 ` Neil Horman
  2018-08-28 16:08   ` Xin Long
  2018-08-27 13:28 ` Marcelo Ricardo Leitner
  2018-08-27 22:13 ` David Miller
  2 siblings, 1 reply; 9+ messages in thread
From: Neil Horman @ 2018-08-27 13:08 UTC (permalink / raw)
  To: Xin Long; +Cc: network dev, linux-sctp, davem, Marcelo Ricardo Leitner

On Mon, Aug 27, 2018 at 06:38:31PM +0800, Xin Long wrote:
> As Marcelo noticed, in sctp_transport_get_next, it is iterating over
> transports but then also accessing the association directly, without
> checking any refcnts before that, which can cause an use-after-free
> Read.
> 
> So fix it by holding transport before accessing the association. With
> that, sctp_transport_hold calls can be removed in the later places.
> 
> Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
> Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
> Signed-off-by: Xin Long <lucien.xin@gmail.com>
> ---
>  net/sctp/proc.c   |  4 ----
>  net/sctp/socket.c | 22 +++++++++++++++-------
>  2 files changed, 15 insertions(+), 11 deletions(-)
> 
> diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> index ef5c9a8..4d6f1c8 100644
> --- a/net/sctp/proc.c
> +++ b/net/sctp/proc.c
> @@ -264,8 +264,6 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
>  	}
>  
>  	transport = (struct sctp_transport *)v;
> -	if (!sctp_transport_hold(transport))
> -		return 0;
>  	assoc = transport->asoc;
>  	epb = &assoc->base;
>  	sk = epb->sk;
> @@ -322,8 +320,6 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
>  	}
>  
>  	transport = (struct sctp_transport *)v;
> -	if (!sctp_transport_hold(transport))
> -		return 0;
>  	assoc = transport->asoc;
>  
>  	list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index e96b15a..aa76586 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -5005,9 +5005,14 @@ struct sctp_transport *sctp_transport_get_next(struct net *net,
>  			break;
>  		}
>  
> +		if (!sctp_transport_hold(t))
> +			continue;
> +
>  		if (net_eq(sock_net(t->asoc->base.sk), net) &&
>  		    t->asoc->peer.primary_path == t)
>  			break;
> +
> +		sctp_transport_put(t);
>  	}
>  
>  	return t;
> @@ -5017,13 +5022,18 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
>  					      struct rhashtable_iter *iter,
>  					      int pos)
>  {
> -	void *obj = SEQ_START_TOKEN;
> +	struct sctp_transport *t;
>  
> -	while (pos && (obj = sctp_transport_get_next(net, iter)) &&
> -	       !IS_ERR(obj))
> -		pos--;
> +	if (!pos)
> +		return SEQ_START_TOKEN;
>  
> -	return obj;
> +	while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
> +		if (!--pos)
> +			break;
> +		sctp_transport_put(t);
> +	}
> +
> +	return t;
>  }
>  
>  int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
> @@ -5082,8 +5092,6 @@ int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
>  
>  	tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
>  	for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
> -		if (!sctp_transport_hold(tsp))
> -			continue;
>  		ret = cb(tsp, p);
>  		if (ret)
>  			break;
> -- 
> 2.1.0
> 
> 
Acked-by: Neil Horman <nhorman@tuxdriver.com>

Additionally, its not germaine to this particular fix, but why are we still
using that pos variable in sctp_transport_get_idx?  With the conversion to
rhashtables, it doesn't seem particularly useful anymore.

Neil

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
  2018-08-27 10:38 [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next Xin Long
  2018-08-27 13:08 ` Neil Horman
@ 2018-08-27 13:28 ` Marcelo Ricardo Leitner
  2018-08-27 22:13 ` David Miller
  2 siblings, 0 replies; 9+ messages in thread
From: Marcelo Ricardo Leitner @ 2018-08-27 13:28 UTC (permalink / raw)
  To: Xin Long; +Cc: network dev, linux-sctp, davem, Neil Horman

On Mon, Aug 27, 2018 at 06:38:31PM +0800, Xin Long wrote:
> As Marcelo noticed, in sctp_transport_get_next, it is iterating over
> transports but then also accessing the association directly, without
> checking any refcnts before that, which can cause an use-after-free
> Read.
> 
> So fix it by holding transport before accessing the association. With
> that, sctp_transport_hold calls can be removed in the later places.
> 
> Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
> Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
> Signed-off-by: Xin Long <lucien.xin@gmail.com>

Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>

> ---
>  net/sctp/proc.c   |  4 ----
>  net/sctp/socket.c | 22 +++++++++++++++-------
>  2 files changed, 15 insertions(+), 11 deletions(-)
> 
> diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> index ef5c9a8..4d6f1c8 100644
> --- a/net/sctp/proc.c
> +++ b/net/sctp/proc.c
> @@ -264,8 +264,6 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
>  	}
>  
>  	transport = (struct sctp_transport *)v;
> -	if (!sctp_transport_hold(transport))
> -		return 0;
>  	assoc = transport->asoc;
>  	epb = &assoc->base;
>  	sk = epb->sk;
> @@ -322,8 +320,6 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
>  	}
>  
>  	transport = (struct sctp_transport *)v;
> -	if (!sctp_transport_hold(transport))
> -		return 0;
>  	assoc = transport->asoc;
>  
>  	list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index e96b15a..aa76586 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -5005,9 +5005,14 @@ struct sctp_transport *sctp_transport_get_next(struct net *net,
>  			break;
>  		}
>  
> +		if (!sctp_transport_hold(t))
> +			continue;
> +
>  		if (net_eq(sock_net(t->asoc->base.sk), net) &&
>  		    t->asoc->peer.primary_path == t)
>  			break;
> +
> +		sctp_transport_put(t);
>  	}
>  
>  	return t;
> @@ -5017,13 +5022,18 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
>  					      struct rhashtable_iter *iter,
>  					      int pos)
>  {
> -	void *obj = SEQ_START_TOKEN;
> +	struct sctp_transport *t;
>  
> -	while (pos && (obj = sctp_transport_get_next(net, iter)) &&
> -	       !IS_ERR(obj))
> -		pos--;
> +	if (!pos)
> +		return SEQ_START_TOKEN;
>  
> -	return obj;
> +	while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
> +		if (!--pos)
> +			break;
> +		sctp_transport_put(t);
> +	}
> +
> +	return t;
>  }
>  
>  int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
> @@ -5082,8 +5092,6 @@ int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
>  
>  	tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
>  	for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
> -		if (!sctp_transport_hold(tsp))
> -			continue;
>  		ret = cb(tsp, p);
>  		if (ret)
>  			break;
> -- 
> 2.1.0
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
  2018-08-27 10:38 [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next Xin Long
  2018-08-27 13:08 ` Neil Horman
  2018-08-27 13:28 ` Marcelo Ricardo Leitner
@ 2018-08-27 22:13 ` David Miller
  2 siblings, 0 replies; 9+ messages in thread
From: David Miller @ 2018-08-27 22:13 UTC (permalink / raw)
  To: lucien.xin; +Cc: netdev, linux-sctp, marcelo.leitner, nhorman

From: Xin Long <lucien.xin@gmail.com>
Date: Mon, 27 Aug 2018 18:38:31 +0800

> As Marcelo noticed, in sctp_transport_get_next, it is iterating over
> transports but then also accessing the association directly, without
> checking any refcnts before that, which can cause an use-after-free
> Read.
> 
> So fix it by holding transport before accessing the association. With
> that, sctp_transport_hold calls can be removed in the later places.
> 
> Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
> Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
> Signed-off-by: Xin Long <lucien.xin@gmail.com>

Applied and queued up for -stable.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
  2018-08-27 13:08 ` Neil Horman
@ 2018-08-28 16:08   ` Xin Long
  2018-08-29 11:35     ` Neil Horman
  0 siblings, 1 reply; 9+ messages in thread
From: Xin Long @ 2018-08-28 16:08 UTC (permalink / raw)
  To: Neil Horman; +Cc: network dev, linux-sctp, davem, Marcelo Ricardo Leitner

On Mon, Aug 27, 2018 at 9:08 PM Neil Horman <nhorman@tuxdriver.com> wrote:
>
> On Mon, Aug 27, 2018 at 06:38:31PM +0800, Xin Long wrote:
> > As Marcelo noticed, in sctp_transport_get_next, it is iterating over
> > transports but then also accessing the association directly, without
> > checking any refcnts before that, which can cause an use-after-free
> > Read.
> >
> > So fix it by holding transport before accessing the association. With
> > that, sctp_transport_hold calls can be removed in the later places.
> >
> > Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
> > Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
> > Signed-off-by: Xin Long <lucien.xin@gmail.com>
> > ---
> >  net/sctp/proc.c   |  4 ----
> >  net/sctp/socket.c | 22 +++++++++++++++-------
> >  2 files changed, 15 insertions(+), 11 deletions(-)
> >
> > diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> > index ef5c9a8..4d6f1c8 100644
> > --- a/net/sctp/proc.c
> > +++ b/net/sctp/proc.c
> > @@ -264,8 +264,6 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
> >       }
> >
> >       transport = (struct sctp_transport *)v;
> > -     if (!sctp_transport_hold(transport))
> > -             return 0;
> >       assoc = transport->asoc;
> >       epb = &assoc->base;
> >       sk = epb->sk;
> > @@ -322,8 +320,6 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
> >       }
> >
> >       transport = (struct sctp_transport *)v;
> > -     if (!sctp_transport_hold(transport))
> > -             return 0;
> >       assoc = transport->asoc;
> >
> >       list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
> > diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> > index e96b15a..aa76586 100644
> > --- a/net/sctp/socket.c
> > +++ b/net/sctp/socket.c
> > @@ -5005,9 +5005,14 @@ struct sctp_transport *sctp_transport_get_next(struct net *net,
> >                       break;
> >               }
> >
> > +             if (!sctp_transport_hold(t))
> > +                     continue;
> > +
> >               if (net_eq(sock_net(t->asoc->base.sk), net) &&
> >                   t->asoc->peer.primary_path == t)
> >                       break;
> > +
> > +             sctp_transport_put(t);
> >       }
> >
> >       return t;
> > @@ -5017,13 +5022,18 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
> >                                             struct rhashtable_iter *iter,
> >                                             int pos)
> >  {
> > -     void *obj = SEQ_START_TOKEN;
> > +     struct sctp_transport *t;
> >
> > -     while (pos && (obj = sctp_transport_get_next(net, iter)) &&
> > -            !IS_ERR(obj))
> > -             pos--;
> > +     if (!pos)
> > +             return SEQ_START_TOKEN;
> >
> > -     return obj;
> > +     while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
> > +             if (!--pos)
> > +                     break;
> > +             sctp_transport_put(t);
> > +     }
> > +
> > +     return t;
> >  }
> >
> >  int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
> > @@ -5082,8 +5092,6 @@ int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
> >
> >       tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
> >       for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
> > -             if (!sctp_transport_hold(tsp))
> > -                     continue;
> >               ret = cb(tsp, p);
> >               if (ret)
> >                       break;
> > --
> > 2.1.0
> >
> >
> Acked-by: Neil Horman <nhorman@tuxdriver.com>
>
> Additionally, its not germaine to this particular fix, but why are we still
> using that pos variable in sctp_transport_get_idx?  With the conversion to
> rhashtables, it doesn't seem particularly useful anymore.
For proc, seems so, hti is saved into seq->private.
But for diag, "hti" in sctp_for_each_transport() is a local variable.
do you think where we can save it?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
  2018-08-28 16:08   ` Xin Long
@ 2018-08-29 11:35     ` Neil Horman
  2018-08-31  7:09       ` Xin Long
  0 siblings, 1 reply; 9+ messages in thread
From: Neil Horman @ 2018-08-29 11:35 UTC (permalink / raw)
  To: Xin Long; +Cc: network dev, linux-sctp, davem, Marcelo Ricardo Leitner

On Wed, Aug 29, 2018 at 12:08:40AM +0800, Xin Long wrote:
> On Mon, Aug 27, 2018 at 9:08 PM Neil Horman <nhorman@tuxdriver.com> wrote:
> >
> > On Mon, Aug 27, 2018 at 06:38:31PM +0800, Xin Long wrote:
> > > As Marcelo noticed, in sctp_transport_get_next, it is iterating over
> > > transports but then also accessing the association directly, without
> > > checking any refcnts before that, which can cause an use-after-free
> > > Read.
> > >
> > > So fix it by holding transport before accessing the association. With
> > > that, sctp_transport_hold calls can be removed in the later places.
> > >
> > > Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
> > > Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
> > > Signed-off-by: Xin Long <lucien.xin@gmail.com>
> > > ---
> > >  net/sctp/proc.c   |  4 ----
> > >  net/sctp/socket.c | 22 +++++++++++++++-------
> > >  2 files changed, 15 insertions(+), 11 deletions(-)
> > >
> > > diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> > > index ef5c9a8..4d6f1c8 100644
> > > --- a/net/sctp/proc.c
> > > +++ b/net/sctp/proc.c
> > > @@ -264,8 +264,6 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
> > >       }
> > >
> > >       transport = (struct sctp_transport *)v;
> > > -     if (!sctp_transport_hold(transport))
> > > -             return 0;
> > >       assoc = transport->asoc;
> > >       epb = &assoc->base;
> > >       sk = epb->sk;
> > > @@ -322,8 +320,6 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
> > >       }
> > >
> > >       transport = (struct sctp_transport *)v;
> > > -     if (!sctp_transport_hold(transport))
> > > -             return 0;
> > >       assoc = transport->asoc;
> > >
> > >       list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
> > > diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> > > index e96b15a..aa76586 100644
> > > --- a/net/sctp/socket.c
> > > +++ b/net/sctp/socket.c
> > > @@ -5005,9 +5005,14 @@ struct sctp_transport *sctp_transport_get_next(struct net *net,
> > >                       break;
> > >               }
> > >
> > > +             if (!sctp_transport_hold(t))
> > > +                     continue;
> > > +
> > >               if (net_eq(sock_net(t->asoc->base.sk), net) &&
> > >                   t->asoc->peer.primary_path == t)
> > >                       break;
> > > +
> > > +             sctp_transport_put(t);
> > >       }
> > >
> > >       return t;
> > > @@ -5017,13 +5022,18 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
> > >                                             struct rhashtable_iter *iter,
> > >                                             int pos)
> > >  {
> > > -     void *obj = SEQ_START_TOKEN;
> > > +     struct sctp_transport *t;
> > >
> > > -     while (pos && (obj = sctp_transport_get_next(net, iter)) &&
> > > -            !IS_ERR(obj))
> > > -             pos--;
> > > +     if (!pos)
> > > +             return SEQ_START_TOKEN;
> > >
> > > -     return obj;
> > > +     while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
> > > +             if (!--pos)
> > > +                     break;
> > > +             sctp_transport_put(t);
> > > +     }
> > > +
> > > +     return t;
> > >  }
> > >
> > >  int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
> > > @@ -5082,8 +5092,6 @@ int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
> > >
> > >       tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
> > >       for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
> > > -             if (!sctp_transport_hold(tsp))
> > > -                     continue;
> > >               ret = cb(tsp, p);
> > >               if (ret)
> > >                       break;
> > > --
> > > 2.1.0
> > >
> > >
> > Acked-by: Neil Horman <nhorman@tuxdriver.com>
> >
> > Additionally, its not germaine to this particular fix, but why are we still
> > using that pos variable in sctp_transport_get_idx?  With the conversion to
> > rhashtables, it doesn't seem particularly useful anymore.
> For proc, seems so, hti is saved into seq->private.
> But for diag, "hti" in sctp_for_each_transport() is a local variable.
> do you think where we can save it?
> 
Sorry, wasn't suggesting that it had to be removed from sctp_for_each_trasnport,
its clearly used as both an input and output there.  All I was sugesting was
that, in sctp_transport_get_idx, the pos variable might no longer be needed
there specifically, as sctp_transprt_get_next should terminate the loop on its
own.  Or is there another purpose for that positional variable I am missing

Neil

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
  2018-08-29 11:35     ` Neil Horman
@ 2018-08-31  7:09       ` Xin Long
  2018-08-31 12:03         ` Neil Horman
  0 siblings, 1 reply; 9+ messages in thread
From: Xin Long @ 2018-08-31  7:09 UTC (permalink / raw)
  To: Neil Horman; +Cc: network dev, linux-sctp, davem, Marcelo Ricardo Leitner

On Wed, Aug 29, 2018 at 7:36 PM Neil Horman <nhorman@tuxdriver.com> wrote:
>
> On Wed, Aug 29, 2018 at 12:08:40AM +0800, Xin Long wrote:
> > On Mon, Aug 27, 2018 at 9:08 PM Neil Horman <nhorman@tuxdriver.com> wrote:
> > >
> > > On Mon, Aug 27, 2018 at 06:38:31PM +0800, Xin Long wrote:
> > > > As Marcelo noticed, in sctp_transport_get_next, it is iterating over
> > > > transports but then also accessing the association directly, without
> > > > checking any refcnts before that, which can cause an use-after-free
> > > > Read.
> > > >
> > > > So fix it by holding transport before accessing the association. With
> > > > that, sctp_transport_hold calls can be removed in the later places.
> > > >
> > > > Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
> > > > Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
> > > > Signed-off-by: Xin Long <lucien.xin@gmail.com>
> > > > ---
> > > >  net/sctp/proc.c   |  4 ----
> > > >  net/sctp/socket.c | 22 +++++++++++++++-------
> > > >  2 files changed, 15 insertions(+), 11 deletions(-)
> > > >
> > > > diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> > > > index ef5c9a8..4d6f1c8 100644
> > > > --- a/net/sctp/proc.c
> > > > +++ b/net/sctp/proc.c
> > > > @@ -264,8 +264,6 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
> > > >       }
> > > >
> > > >       transport = (struct sctp_transport *)v;
> > > > -     if (!sctp_transport_hold(transport))
> > > > -             return 0;
> > > >       assoc = transport->asoc;
> > > >       epb = &assoc->base;
> > > >       sk = epb->sk;
> > > > @@ -322,8 +320,6 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
> > > >       }
> > > >
> > > >       transport = (struct sctp_transport *)v;
> > > > -     if (!sctp_transport_hold(transport))
> > > > -             return 0;
> > > >       assoc = transport->asoc;
> > > >
> > > >       list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
> > > > diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> > > > index e96b15a..aa76586 100644
> > > > --- a/net/sctp/socket.c
> > > > +++ b/net/sctp/socket.c
> > > > @@ -5005,9 +5005,14 @@ struct sctp_transport *sctp_transport_get_next(struct net *net,
> > > >                       break;
> > > >               }
> > > >
> > > > +             if (!sctp_transport_hold(t))
> > > > +                     continue;
> > > > +
> > > >               if (net_eq(sock_net(t->asoc->base.sk), net) &&
> > > >                   t->asoc->peer.primary_path == t)
> > > >                       break;
> > > > +
> > > > +             sctp_transport_put(t);
> > > >       }
> > > >
> > > >       return t;
> > > > @@ -5017,13 +5022,18 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
> > > >                                             struct rhashtable_iter *iter,
> > > >                                             int pos)
> > > >  {
> > > > -     void *obj = SEQ_START_TOKEN;
> > > > +     struct sctp_transport *t;
> > > >
> > > > -     while (pos && (obj = sctp_transport_get_next(net, iter)) &&
> > > > -            !IS_ERR(obj))
> > > > -             pos--;
> > > > +     if (!pos)
> > > > +             return SEQ_START_TOKEN;
> > > >
> > > > -     return obj;
> > > > +     while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
> > > > +             if (!--pos)
> > > > +                     break;
> > > > +             sctp_transport_put(t);
> > > > +     }
> > > > +
> > > > +     return t;
> > > >  }
> > > >
> > > >  int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
> > > > @@ -5082,8 +5092,6 @@ int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
> > > >
> > > >       tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
> > > >       for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
> > > > -             if (!sctp_transport_hold(tsp))
> > > > -                     continue;
> > > >               ret = cb(tsp, p);
> > > >               if (ret)
> > > >                       break;
> > > > --
> > > > 2.1.0
> > > >
> > > >
> > > Acked-by: Neil Horman <nhorman@tuxdriver.com>
> > >
> > > Additionally, its not germaine to this particular fix, but why are we still
> > > using that pos variable in sctp_transport_get_idx?  With the conversion to
> > > rhashtables, it doesn't seem particularly useful anymore.
> > For proc, seems so, hti is saved into seq->private.
> > But for diag, "hti" in sctp_for_each_transport() is a local variable.
> > do you think where we can save it?
> >
> Sorry, wasn't suggesting that it had to be removed from sctp_for_each_trasnport,
> its clearly used as both an input and output there.  All I was sugesting was
> that, in sctp_transport_get_idx, the pos variable might no longer be needed
> there specifically, as sctp_transprt_get_next should terminate the loop on its
> own.  Or is there another purpose for that positional variable I am missing
Yes, Neil, all transports/asocs could not be dumped once as one buffer/rtnl msg
is not big enough for them. when coming into proc/diag again, it needs to start
from the *next* one, and 'pos' is used to save its position.

Without 'pos', it would always start from the first one and dump the duplicate
ones in the next times. Make sense?

>
> Neil
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
  2018-08-31  7:09       ` Xin Long
@ 2018-08-31 12:03         ` Neil Horman
  2018-09-03 13:03           ` Neil Horman
  0 siblings, 1 reply; 9+ messages in thread
From: Neil Horman @ 2018-08-31 12:03 UTC (permalink / raw)
  To: Xin Long; +Cc: network dev, linux-sctp, davem, Marcelo Ricardo Leitner

On Fri, Aug 31, 2018 at 03:09:05PM +0800, Xin Long wrote:
> On Wed, Aug 29, 2018 at 7:36 PM Neil Horman <nhorman@tuxdriver.com> wrote:
> >
> > On Wed, Aug 29, 2018 at 12:08:40AM +0800, Xin Long wrote:
> > > On Mon, Aug 27, 2018 at 9:08 PM Neil Horman <nhorman@tuxdriver.com> wrote:
> > > >
> > > > On Mon, Aug 27, 2018 at 06:38:31PM +0800, Xin Long wrote:
> > > > > As Marcelo noticed, in sctp_transport_get_next, it is iterating over
> > > > > transports but then also accessing the association directly, without
> > > > > checking any refcnts before that, which can cause an use-after-free
> > > > > Read.
> > > > >
> > > > > So fix it by holding transport before accessing the association. With
> > > > > that, sctp_transport_hold calls can be removed in the later places.
> > > > >
> > > > > Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
> > > > > Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
> > > > > Signed-off-by: Xin Long <lucien.xin@gmail.com>
> > > > > ---
> > > > >  net/sctp/proc.c   |  4 ----
> > > > >  net/sctp/socket.c | 22 +++++++++++++++-------
> > > > >  2 files changed, 15 insertions(+), 11 deletions(-)
> > > > >
> > > > > diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> > > > > index ef5c9a8..4d6f1c8 100644
> > > > > --- a/net/sctp/proc.c
> > > > > +++ b/net/sctp/proc.c
> > > > > @@ -264,8 +264,6 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
> > > > >       }
> > > > >
> > > > >       transport = (struct sctp_transport *)v;
> > > > > -     if (!sctp_transport_hold(transport))
> > > > > -             return 0;
> > > > >       assoc = transport->asoc;
> > > > >       epb = &assoc->base;
> > > > >       sk = epb->sk;
> > > > > @@ -322,8 +320,6 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
> > > > >       }
> > > > >
> > > > >       transport = (struct sctp_transport *)v;
> > > > > -     if (!sctp_transport_hold(transport))
> > > > > -             return 0;
> > > > >       assoc = transport->asoc;
> > > > >
> > > > >       list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
> > > > > diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> > > > > index e96b15a..aa76586 100644
> > > > > --- a/net/sctp/socket.c
> > > > > +++ b/net/sctp/socket.c
> > > > > @@ -5005,9 +5005,14 @@ struct sctp_transport *sctp_transport_get_next(struct net *net,
> > > > >                       break;
> > > > >               }
> > > > >
> > > > > +             if (!sctp_transport_hold(t))
> > > > > +                     continue;
> > > > > +
> > > > >               if (net_eq(sock_net(t->asoc->base.sk), net) &&
> > > > >                   t->asoc->peer.primary_path == t)
> > > > >                       break;
> > > > > +
> > > > > +             sctp_transport_put(t);
> > > > >       }
> > > > >
> > > > >       return t;
> > > > > @@ -5017,13 +5022,18 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
> > > > >                                             struct rhashtable_iter *iter,
> > > > >                                             int pos)
> > > > >  {
> > > > > -     void *obj = SEQ_START_TOKEN;
> > > > > +     struct sctp_transport *t;
> > > > >
> > > > > -     while (pos && (obj = sctp_transport_get_next(net, iter)) &&
> > > > > -            !IS_ERR(obj))
> > > > > -             pos--;
> > > > > +     if (!pos)
> > > > > +             return SEQ_START_TOKEN;
> > > > >
> > > > > -     return obj;
> > > > > +     while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
> > > > > +             if (!--pos)
> > > > > +                     break;
> > > > > +             sctp_transport_put(t);
> > > > > +     }
> > > > > +
> > > > > +     return t;
> > > > >  }
> > > > >
> > > > >  int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
> > > > > @@ -5082,8 +5092,6 @@ int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
> > > > >
> > > > >       tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
> > > > >       for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
> > > > > -             if (!sctp_transport_hold(tsp))
> > > > > -                     continue;
> > > > >               ret = cb(tsp, p);
> > > > >               if (ret)
> > > > >                       break;
> > > > > --
> > > > > 2.1.0
> > > > >
> > > > >
> > > > Acked-by: Neil Horman <nhorman@tuxdriver.com>
> > > >
> > > > Additionally, its not germaine to this particular fix, but why are we still
> > > > using that pos variable in sctp_transport_get_idx?  With the conversion to
> > > > rhashtables, it doesn't seem particularly useful anymore.
> > > For proc, seems so, hti is saved into seq->private.
> > > But for diag, "hti" in sctp_for_each_transport() is a local variable.
> > > do you think where we can save it?
> > >
> > Sorry, wasn't suggesting that it had to be removed from sctp_for_each_trasnport,
> > its clearly used as both an input and output there.  All I was sugesting was
> > that, in sctp_transport_get_idx, the pos variable might no longer be needed
> > there specifically, as sctp_transprt_get_next should terminate the loop on its
> > own.  Or is there another purpose for that positional variable I am missing
> Yes, Neil, all transports/asocs could not be dumped once as one buffer/rtnl msg
> is not big enough for them. when coming into proc/diag again, it needs to start
> from the *next* one, and 'pos' is used to save its position.
> 
> Without 'pos', it would always start from the first one and dump the duplicate
> ones in the next times. Make sense?
> 
You're missing what I'm trying to say.  I'm speaking specifically about
sctp_transport_get_idx here.  In that function, pos is passed by value, and has
no bearing on if sctp_transport_get_idx starts at the beginning of the list, or
not, thats in control of the iterator entirely here. If we remove pos from the
parameter list, at worst, the iterator continues until the end of the list,
which I think is fine.

No?
Neil

> >
> > Neil
> >
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next
  2018-08-31 12:03         ` Neil Horman
@ 2018-09-03 13:03           ` Neil Horman
  0 siblings, 0 replies; 9+ messages in thread
From: Neil Horman @ 2018-09-03 13:03 UTC (permalink / raw)
  To: Xin Long; +Cc: network dev, linux-sctp, davem, Marcelo Ricardo Leitner

On Fri, Aug 31, 2018 at 08:03:23AM -0400, Neil Horman wrote:
> On Fri, Aug 31, 2018 at 03:09:05PM +0800, Xin Long wrote:
> > On Wed, Aug 29, 2018 at 7:36 PM Neil Horman <nhorman@tuxdriver.com> wrote:
> > >
> > > On Wed, Aug 29, 2018 at 12:08:40AM +0800, Xin Long wrote:
> > > > On Mon, Aug 27, 2018 at 9:08 PM Neil Horman <nhorman@tuxdriver.com> wrote:
> > > > >
> > > > > On Mon, Aug 27, 2018 at 06:38:31PM +0800, Xin Long wrote:
> > > > > > As Marcelo noticed, in sctp_transport_get_next, it is iterating over
> > > > > > transports but then also accessing the association directly, without
> > > > > > checking any refcnts before that, which can cause an use-after-free
> > > > > > Read.
> > > > > >
> > > > > > So fix it by holding transport before accessing the association. With
> > > > > > that, sctp_transport_hold calls can be removed in the later places.
> > > > > >
> > > > > > Fixes: 626d16f50f39 ("sctp: export some apis or variables for sctp_diag and reuse some for proc")
> > > > > > Reported-by: syzbot+fe62a0c9aa6a85c6de16@syzkaller.appspotmail.com
> > > > > > Signed-off-by: Xin Long <lucien.xin@gmail.com>
> > > > > > ---
> > > > > >  net/sctp/proc.c   |  4 ----
> > > > > >  net/sctp/socket.c | 22 +++++++++++++++-------
> > > > > >  2 files changed, 15 insertions(+), 11 deletions(-)
> > > > > >
> > > > > > diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> > > > > > index ef5c9a8..4d6f1c8 100644
> > > > > > --- a/net/sctp/proc.c
> > > > > > +++ b/net/sctp/proc.c
> > > > > > @@ -264,8 +264,6 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
> > > > > >       }
> > > > > >
> > > > > >       transport = (struct sctp_transport *)v;
> > > > > > -     if (!sctp_transport_hold(transport))
> > > > > > -             return 0;
> > > > > >       assoc = transport->asoc;
> > > > > >       epb = &assoc->base;
> > > > > >       sk = epb->sk;
> > > > > > @@ -322,8 +320,6 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
> > > > > >       }
> > > > > >
> > > > > >       transport = (struct sctp_transport *)v;
> > > > > > -     if (!sctp_transport_hold(transport))
> > > > > > -             return 0;
> > > > > >       assoc = transport->asoc;
> > > > > >
> > > > > >       list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
> > > > > > diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> > > > > > index e96b15a..aa76586 100644
> > > > > > --- a/net/sctp/socket.c
> > > > > > +++ b/net/sctp/socket.c
> > > > > > @@ -5005,9 +5005,14 @@ struct sctp_transport *sctp_transport_get_next(struct net *net,
> > > > > >                       break;
> > > > > >               }
> > > > > >
> > > > > > +             if (!sctp_transport_hold(t))
> > > > > > +                     continue;
> > > > > > +
> > > > > >               if (net_eq(sock_net(t->asoc->base.sk), net) &&
> > > > > >                   t->asoc->peer.primary_path == t)
> > > > > >                       break;
> > > > > > +
> > > > > > +             sctp_transport_put(t);
> > > > > >       }
> > > > > >
> > > > > >       return t;
> > > > > > @@ -5017,13 +5022,18 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
> > > > > >                                             struct rhashtable_iter *iter,
> > > > > >                                             int pos)
> > > > > >  {
> > > > > > -     void *obj = SEQ_START_TOKEN;
> > > > > > +     struct sctp_transport *t;
> > > > > >
> > > > > > -     while (pos && (obj = sctp_transport_get_next(net, iter)) &&
> > > > > > -            !IS_ERR(obj))
> > > > > > -             pos--;
> > > > > > +     if (!pos)
> > > > > > +             return SEQ_START_TOKEN;
> > > > > >
> > > > > > -     return obj;
> > > > > > +     while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
> > > > > > +             if (!--pos)
> > > > > > +                     break;
> > > > > > +             sctp_transport_put(t);
> > > > > > +     }
> > > > > > +
> > > > > > +     return t;
> > > > > >  }
> > > > > >
> > > > > >  int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
> > > > > > @@ -5082,8 +5092,6 @@ int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
> > > > > >
> > > > > >       tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
> > > > > >       for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
> > > > > > -             if (!sctp_transport_hold(tsp))
> > > > > > -                     continue;
> > > > > >               ret = cb(tsp, p);
> > > > > >               if (ret)
> > > > > >                       break;
> > > > > > --
> > > > > > 2.1.0
> > > > > >
> > > > > >
> > > > > Acked-by: Neil Horman <nhorman@tuxdriver.com>
> > > > >
> > > > > Additionally, its not germaine to this particular fix, but why are we still
> > > > > using that pos variable in sctp_transport_get_idx?  With the conversion to
> > > > > rhashtables, it doesn't seem particularly useful anymore.
> > > > For proc, seems so, hti is saved into seq->private.
> > > > But for diag, "hti" in sctp_for_each_transport() is a local variable.
> > > > do you think where we can save it?
> > > >
> > > Sorry, wasn't suggesting that it had to be removed from sctp_for_each_trasnport,
> > > its clearly used as both an input and output there.  All I was sugesting was
> > > that, in sctp_transport_get_idx, the pos variable might no longer be needed
> > > there specifically, as sctp_transprt_get_next should terminate the loop on its
> > > own.  Or is there another purpose for that positional variable I am missing
> > Yes, Neil, all transports/asocs could not be dumped once as one buffer/rtnl msg
> > is not big enough for them. when coming into proc/diag again, it needs to start
> > from the *next* one, and 'pos' is used to save its position.
> > 
> > Without 'pos', it would always start from the first one and dump the duplicate
> > ones in the next times. Make sense?
> > 
> You're missing what I'm trying to say.  I'm speaking specifically about
> sctp_transport_get_idx here.  In that function, pos is passed by value, and has
> no bearing on if sctp_transport_get_idx starts at the beginning of the list, or
> not, thats in control of the iterator entirely here. If we remove pos from the
> parameter list, at worst, the iterator continues until the end of the list,
> which I think is fine.
> 
> No?
> Neil
> 

Sorry, slight correction here, I see what you were trying to say.  You're saying
that the one user of sctp_for_each_transport (sctp_diag_dump), uses the pos
variable to start at a point other than the head of the list, because the
netlink protocol that uses that allows you to index into it that way.  Sorry
about that.

That said, thats....odd.  Its certainly no in keeping with the other _for_each
methods the kernel has.  The for_each construct typically iterates over the
entire list regardless, and leaves filtering up to the caller.  I'd suggest we
do the same, by not requireing a positional argument, and instead checking that
positional argument in the callback.  I'll write a patch for it this week

Neil

> > >
> > > Neil
> > >
> > 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2018-09-03 17:24 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-08-27 10:38 [PATCH net] sctp: hold transport before accessing its asoc in sctp_transport_get_next Xin Long
2018-08-27 13:08 ` Neil Horman
2018-08-28 16:08   ` Xin Long
2018-08-29 11:35     ` Neil Horman
2018-08-31  7:09       ` Xin Long
2018-08-31 12:03         ` Neil Horman
2018-09-03 13:03           ` Neil Horman
2018-08-27 13:28 ` Marcelo Ricardo Leitner
2018-08-27 22:13 ` David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox