sctp: a bunch of fixes by holding transport
Subsystems (3)
Xin Long
maintainerv2 cover sctp: a bunch of fixes by holding transport
There are several places where it holds assoc after getting transport by searching from transport rhashtable, it may cause use-after-free issue.
This patchset is to fix them by holding transport instead.
v1->v2:
Fix the changelog of patch 2/3
Xin Long (3):
sctp: hold transport instead of assoc in sctp_diag
sctp: return back transport in __sctp_rcv_init_lookup
sctp: hold transport instead of assoc when lookup assoc in rx path
include/net/sctp/sctp.h | 2 +- net/sctp/input.c | 35 +++++++++++++++++------------------ net/sctp/ipv6.c | 2 +- net/sctp/socket.c | 5 +---- 4 files changed, 20 insertions(+), 24 deletions(-)
Signature · 1 line
2.1.0
Xin Long
maintainerPATCH v2 1/3 sctp: hold transport instead of assoc in sctp_diag
In sctp_transport_lookup_process(), Commit 1cceda784980 ("sctp: fix the issue sctp_diag uses lock_sock in rcu_read_lock") moved cb() out of rcu lock, but it put transport and hold assoc instead, and ignore that cb() still uses transport. It may cause a use-after-free issue.
This patch is to hold transport instead of assoc there.
--- net/sctp/socket.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
net/sctp/socket.c +1 -4
@@ -4480,12 +4480,9 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),if (!transport || !sctp_transport_hold (transport)) goto out; -sctp_association_hold (transport->asoc); -sctp_transport_put (transport); -rcu_read_unlock (); err =cb (transport, p); -sctp_association_put (transport->asoc); +sctp_transport_put (transport); out:return err;
Signature · 1 line
2.1.0
Xin Long
maintainerPATCH v2 2/3 sctp: return back transport in __sctp_rcv_init_lookup
Prior to this patch, it used a local variable to save the transport that is looked up by __sctp_lookup_association(), and didn't return it back. But in sctp_rcv, it is used to initialize chunk->transport. So when hitting this, even if it found the transport, it was still initializing chunk->transport with null instead.
This patch is to return the transport back through transport pointer that is from __sctp_rcv_lookup_harder().
--- net/sctp/input.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
net/sctp/input.c +1 -2
@@ -1021,7 +1021,6 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct net *net,struct sctphdr *sh =sctp_hdr (skb);union sctp_params params;sctp_init_chunk_t *init; -struct sctp_transport *transport;struct sctp_af *af;/*
@@ -1052,7 +1051,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct net *net, af->from_addr_param (paddr, params.addr, sh->source,0 ); - asoc =__sctp_lookup_association (net, laddr, paddr, &transport); + asoc =__sctp_lookup_association (net, laddr, paddr, transportp);if (asoc)return asoc; }
Signature · 1 line
2.1.0
Xin Long
maintainerPATCH v2 3/3 sctp: hold transport instead of assoc when lookup assoc in rx path
Prior to this patch, in rx path, before calling lock_sock, it needed to hold assoc when got it by __sctp_lookup_association, in case other place would free/put assoc.
But in __sctp_lookup_association, it lookup and hold transport, then got assoc by transport->assoc, then hold assoc and put transport. It means it didn't hold transport, yet it was returned and later on directly assigned to chunk->transport.
Without the protection of sock lock, the transport may be freed/put by other places, which would cause a use-after-free issue.
This patch is to fix this issue by holding transport instead of assoc. As holding transport can make sure to access assoc is also safe, and actually it looks up assoc by searching transport rhashtable, to hold transport here makes more sense.
Note that the function will be renamed later on on another patch.
--- include/net/sctp/sctp.h | 2 +- net/sctp/input.c | 32 ++++++++++++++++---------------- net/sctp/ipv6.c | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-)
include/net/sctp/sctp.h +1 -1
@@ -152,7 +152,7 @@ void sctp_unhash_endpoint(struct sctp_endpoint *);struct sock *sctp_err_lookup (struct net *net,int family,struct sk_buff *,struct sctphdr *,struct sctp_association **,struct sctp_transport **); -void sctp_err_finish (struct sock *,struct sctp_association *); +void sctp_err_finish (struct sock *,struct sctp_transport *);void sctp_icmp_frag_needed (struct sock *,struct sctp_association *,struct sctp_transport *t,__u32 pmtu); void sctp_icmp_redirect(struct sock *, struct sctp_transport *,
net/sctp/input.c +16 -16
@@ -181,9 +181,10 @@ int sctp_rcv(struct sk_buff *skb)* bound to another interface, via SO_BINDTODEVICE, treat it as OOTB */ if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif (skb))) { -if (asoc) { -sctp_association_put (asoc); +if (transport) { +sctp_transport_put (transport); asoc =NULL ; + transport =NULL ; }else {sctp_endpoint_put (ep); ep =NULL ;
@@ -269,8 +270,8 @@ int sctp_rcv(struct sk_buff *skb)bh_unlock_sock (sk);/* Release the asoc/ep ref we took in the lookup calls. */ -if (asoc) -sctp_association_put (asoc); +if (transport) +sctp_transport_put (transport);else sctp_endpoint_put (ep);
@@ -283,8 +284,8 @@ int sctp_rcv(struct sk_buff *skb) discard_release:/* Release the asoc/ep ref we took in the lookup calls. */ -if (asoc) -sctp_association_put (asoc); +if (transport) +sctp_transport_put (transport);else sctp_endpoint_put (ep);
@@ -300,6 +301,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) {struct sctp_chunk *chunk =SCTP_INPUT_CB (skb)->chunk;struct sctp_inq *inqueue = &chunk->rcvr->inqueue; +struct sctp_transport *t = chunk->transport;struct sctp_ep_common *rcvr =NULL ;int backloged =0 ;
@@ -351,7 +353,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) done:/* Release the refs we took in sctp_add_backlog */ if (SCTP_EP_TYPE_ASSOCIATION = rcvr->type) -sctp_association_put (sctp_assoc (rcvr)); +sctp_transport_put (t);else if (SCTP_EP_TYPE_SOCKET = rcvr->type)sctp_endpoint_put (sctp_ep (rcvr));else
@@ -363,6 +365,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)static int sctp_add_backlog (struct sock *sk,struct sk_buff *skb) {struct sctp_chunk *chunk =SCTP_INPUT_CB (skb)->chunk; +struct sctp_transport *t = chunk->transport;struct sctp_ep_common *rcvr = chunk->rcvr;int ret;
@@ -373,7 +376,7 @@ static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb)* from us */ if (SCTP_EP_TYPE_ASSOCIATION = rcvr->type) -sctp_association_hold (sctp_assoc (rcvr)); +sctp_transport_hold (t);else if (SCTP_EP_TYPE_SOCKET = rcvr->type)sctp_endpoint_hold (sctp_ep (rcvr));else
@@ -537,15 +540,15 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb,return sk; out: -sctp_association_put (asoc); +sctp_transport_put (transport);return NULL ; }/* Common cleanup code for icmp/icmpv6 error handler. */ -void sctp_err_finish (struct sock *sk,struct sctp_association *asoc) +void sctp_err_finish (struct sock *sk,struct sctp_transport *t) {bh_unlock_sock (sk); -sctp_association_put (asoc); +sctp_transport_put (t); }/*
@@ -641,7 +644,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) } out_unlock: -sctp_err_finish (sk, asoc); +sctp_err_finish (sk, transport); }/*
@@ -952,11 +955,8 @@ static struct sctp_association *__sctp_lookup_association( goto out; asoc = t->asoc; -sctp_association_hold (asoc); *pt = t; -sctp_transport_put (t); - out:return asoc; }
@@ -986,7 +986,7 @@ int sctp_has_association(struct net *net,struct sctp_transport *transport;if ((asoc =sctp_lookup_association (net, laddr, paddr, &transport))) { -sctp_association_put (asoc); +sctp_transport_put (transport);return 1 ; }
net/sctp/ipv6.c +1 -1
@@ -198,7 +198,7 @@ static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, } out_unlock: -sctp_err_finish (sk, asoc); +sctp_err_finish (sk, transport); out:if (likely (idev !=NULL ))in6_dev_put (idev);
Signature · 1 line
2.1.0
Re: [PATCHv2 net 3/3] sctp: hold transport instead of assoc when lookup assoc in rx path
Quoted 12 lines · Xin Long
Prior to this patch, in rx path, before calling
lock_sock, it needed to hold assoc when got it by__sctp_lookup_association, in case other place would free/put assoc.But in
__sctp_lookup_association, it lookup and hold transport, then got assoc bytransport->assoc, then hold assoc and put transport. It means it didn't hold transport, yet it was returned and later on directly assigned tochunk->transport.Without the protection of sock lock, the transport may be freed/put by other places, which would cause a use-after-free issue.
Quoted 8 lines
This patch is to fix this issue by holding transport instead of assoc. As holding transport can make sure to access assoc is also safe, and actually it looks up assoc by searching transport rhashtable, to hold transport here makes more sense.
Note that the function will be renamed later on on another patch.
Signed-off-by Xin Long <[email protected]>
Thanks
Quoted 162 lines
--- include/net/sctp/sctp.h | 2 +- net/sctp/input.c | 32 ++++++++++++++++---------------- net/sctp/ipv6.c | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-)include/net/sctp/sctp.h +1
-1@@ -152,7 +152,7 @@ void sctp_unhash_endpoint(struct sctp_endpoint *);struct sock *sctp_err_lookup (struct net *net,int family,struct sk_buff *,struct sctphdr *,struct sctp_association **,struct sctp_transport **); -void sctp_err_finish (struct sock *,struct sctp_association *); +void sctp_err_finish (struct sock *,struct sctp_transport *);void sctp_icmp_frag_needed (struct sock *,struct sctp_association *,struct sctp_transport *t,__u32 pmtu); void sctp_icmp_redirect(struct sock *, struct sctp_transport *,net/sctp/input.c +16
-16@@ -181,9 +181,10 @@ int sctp_rcv(struct sk_buff *skb)* bound to another interface, via SO_BINDTODEVICE, treat it as OOTB */ if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif (skb))) { -if (asoc) { -sctp_association_put (asoc); +if (transport) { +sctp_transport_put (transport); asoc =NULL ; + transport =NULL ; }else {sctp_endpoint_put (ep); ep =NULL ;@@ -269,8 +270,8 @@ int sctp_rcv(struct sk_buff *skb)bh_unlock_sock (sk);/* Release the asoc/ep ref we took in the lookup calls. */ -if (asoc) -sctp_association_put (asoc); +if (transport) +sctp_transport_put (transport);else sctp_endpoint_put (ep);@@ -283,8 +284,8 @@ int sctp_rcv(struct sk_buff *skb) discard_release:/* Release the asoc/ep ref we took in the lookup calls. */ -if (asoc) -sctp_association_put (asoc); +if (transport) +sctp_transport_put (transport);else sctp_endpoint_put (ep);@@ -300,6 +301,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) {struct sctp_chunk *chunk =SCTP_INPUT_CB (skb)->chunk;struct sctp_inq *inqueue = &chunk->rcvr->inqueue; +struct sctp_transport *t = chunk->transport;struct sctp_ep_common *rcvr =NULL ;int backloged =0 ;@@ -351,7 +353,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) done:/* Release the refs we took in sctp_add_backlog */ if (SCTP_EP_TYPE_ASSOCIATION = rcvr->type) -sctp_association_put (sctp_assoc (rcvr)); +sctp_transport_put (t);else if (SCTP_EP_TYPE_SOCKET = rcvr->type)sctp_endpoint_put (sctp_ep (rcvr));else @@ -363,6 +365,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)static int sctp_add_backlog (struct sock *sk,struct sk_buff *skb) {struct sctp_chunk *chunk =SCTP_INPUT_CB (skb)->chunk; +struct sctp_transport *t = chunk->transport;struct sctp_ep_common *rcvr = chunk->rcvr;int ret;@@ -373,7 +376,7 @@ static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb)* from us */ if (SCTP_EP_TYPE_ASSOCIATION = rcvr->type) -sctp_association_hold (sctp_assoc (rcvr)); +sctp_transport_hold (t);else if (SCTP_EP_TYPE_SOCKET = rcvr->type)sctp_endpoint_hold (sctp_ep (rcvr));else @@ -537,15 +540,15 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb,return sk; out: -sctp_association_put (asoc); +sctp_transport_put (transport);return NULL ; }/* Common cleanup code for icmp/icmpv6 error handler. */ -void sctp_err_finish (struct sock *sk,struct sctp_association *asoc) +void sctp_err_finish (struct sock *sk,struct sctp_transport *t) {bh_unlock_sock (sk); -sctp_association_put (asoc); +sctp_transport_put (t); }/* @@ -641,7 +644,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) } out_unlock: -sctp_err_finish (sk, asoc); +sctp_err_finish (sk, transport); }/* @@ -952,11 +955,8 @@ static struct sctp_association *__sctp_lookup_association( goto out; asoc = t->asoc; -sctp_association_hold (asoc); *pt = t; -sctp_transport_put (t); - out:return asoc; }@@ -986,7 +986,7 @@ int sctp_has_association(struct net *net,struct sctp_transport *transport;if ((asoc =sctp_lookup_association (net, laddr, paddr, &transport))) { -sctp_association_put (asoc); +sctp_transport_put (transport);return 1 ; }net/sctp/ipv6.c +1
-1@@ -198,7 +198,7 @@ static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, } out_unlock: -sctp_err_finish (sk, asoc); +sctp_err_finish (sk, transport); out:if (likely (idev !=NULL ))in6_dev_put (idev);Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-sctp" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCHv2 net 2/3] sctp: return back transport in __sctp_rcv_init_lookup
In reply to Xin LongQuoted 10 lines · Xin Long
Prior to this patch, it used a local variable to save the transport that is looked up by
__sctp_lookup_association(), and didn't return it back. But insctp_rcv, it is used to initializechunk->transport. So when hitting this, even if it found the transport, it was still initializingchunk->transportwith null instead.This patch is to return the transport back through transport pointer that is from
__sctp_rcv_lookup_harder().Signed-off-by Xin Long <[email protected]>
Quoted 33 lines
--- net/sctp/input.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)net/sctp/input.c +1
-2@@ -1021,7 +1021,6 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct net *net,struct sctphdr *sh =sctp_hdr (skb);union sctp_params params;sctp_init_chunk_t *init; -struct sctp_transport *transport;struct sctp_af *af;/* @@ -1052,7 +1051,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct net *net, af->from_addr_param (paddr, params.addr, sh->source,0 ); - asoc =__sctp_lookup_association (net, laddr, paddr, &transport); + asoc =__sctp_lookup_association (net, laddr, paddr, transportp);if (asoc)return asoc; }Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-sctp" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCHv2 net 1/3] sctp: hold transport instead of assoc in sctp_diag
In reply to Xin LongQuoted 9 lines · Xin Long
In
sctp_transport_lookup_process(), Commit1cceda784980("sctp: fix the issuesctp_diaguseslock_sockinrcu_read_lock") movedcb()out of rcu lock, but it put transport and hold assoc instead, and ignore thatcb()still uses transport. It may cause a use-after-free issue.This patch is to hold transport instead of assoc there.
Fixes 1cceda784980 ("sctp: fix the issue sctp_diag uses lock_sock in rcu_read_lock")Signed-off-by Xin Long <[email protected]>
Quoted 30 lines
--- net/sctp/socket.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)net/sctp/socket.c +1
-4@@ -4480,12 +4480,9 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),if (!transport || !sctp_transport_hold (transport)) goto out; -sctp_association_hold (transport->asoc); -sctp_transport_put (transport); -rcu_read_unlock (); err =cb (transport, p); -sctp_association_put (transport->asoc); +sctp_transport_put (transport); out:return err;Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-sctp" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 13 lines · Xin Long
There are several places where it holds assoc after getting transport by searching from transport rhashtable, it may cause use-after-free issue.
This patchset is to fix them by holding transport instead.
v1->v2:
Fix the changelog of patch 2/3Xin Long (3):
sctp: hold transport instead of assoc insctp_diag
sctp: return back transport in__sctp_rcv_init_lookup
sctp: hold transport instead of assoc when lookup assoc in rx path
Quoted 10 lines
include/net/sctp/sctp.h | 2 +- net/sctp/input.c | 35 +++++++++++++++++------------------ net/sctp/ipv6.c | 2 +- net/sctp/socket.c | 5 +---- 4 files changed, 20 insertions(+), 24 deletions(-)Signature · 1 line
2.1.0
Series
Quoted 13 lines · Xin Long
There are several places where it holds assoc after getting transport by searching from transport rhashtable, it may cause use-after-free issue.
This patchset is to fix them by holding transport instead.
v1->v2:
Fix the changelog of patch 2/3Xin Long (3):
sctp: hold transport instead of assoc insctp_diag
sctp: return back transport in__sctp_rcv_init_lookup
sctp: hold transport instead of assoc when lookup assoc in rx path
Quoted 10 lines
include/net/sctp/sctp.h | 2 +- net/sctp/input.c | 35 +++++++++++++++++------------------ net/sctp/ipv6.c | 2 +- net/sctp/socket.c | 5 +---- 4 files changed, 20 insertions(+), 24 deletions(-)Signature · 1 line
2.1.0
series