aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXin Long <lucien.xin@gmail.com>2016-10-31 08:32:33 -0400
committerDavid S. Miller <davem@davemloft.net>2016-10-31 16:20:33 -0400
commitdae399d7fdee84d8f5227a9711d95bb4e9a05d4e (patch)
tree5ea1f8d710959c462452efaa4fffc8d7d1f0cbb7
parent7c17fcc726903ffed1716351efdc617e752533ed (diff)
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. Signed-off-by: Xin Long <lucien.xin@gmail.com> Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/sctp/sctp.h2
-rw-r--r--net/sctp/input.c32
-rw-r--r--net/sctp/ipv6.c2
3 files changed, 18 insertions, 18 deletions
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index 87a7f42e7639..31acc3f4f132 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -152,7 +152,7 @@ void sctp_unhash_endpoint(struct sctp_endpoint *);
152struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *, 152struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
153 struct sctphdr *, struct sctp_association **, 153 struct sctphdr *, struct sctp_association **,
154 struct sctp_transport **); 154 struct sctp_transport **);
155void sctp_err_finish(struct sock *, struct sctp_association *); 155void sctp_err_finish(struct sock *, struct sctp_transport *);
156void sctp_icmp_frag_needed(struct sock *, struct sctp_association *, 156void sctp_icmp_frag_needed(struct sock *, struct sctp_association *,
157 struct sctp_transport *t, __u32 pmtu); 157 struct sctp_transport *t, __u32 pmtu);
158void sctp_icmp_redirect(struct sock *, struct sctp_transport *, 158void sctp_icmp_redirect(struct sock *, struct sctp_transport *,
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 8e0bc58eec20..a01a56ec8b8c 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -181,9 +181,10 @@ int sctp_rcv(struct sk_buff *skb)
181 * bound to another interface, via SO_BINDTODEVICE, treat it as OOTB 181 * bound to another interface, via SO_BINDTODEVICE, treat it as OOTB
182 */ 182 */
183 if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb))) { 183 if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb))) {
184 if (asoc) { 184 if (transport) {
185 sctp_association_put(asoc); 185 sctp_transport_put(transport);
186 asoc = NULL; 186 asoc = NULL;
187 transport = NULL;
187 } else { 188 } else {
188 sctp_endpoint_put(ep); 189 sctp_endpoint_put(ep);
189 ep = NULL; 190 ep = NULL;
@@ -269,8 +270,8 @@ int sctp_rcv(struct sk_buff *skb)
269 bh_unlock_sock(sk); 270 bh_unlock_sock(sk);
270 271
271 /* Release the asoc/ep ref we took in the lookup calls. */ 272 /* Release the asoc/ep ref we took in the lookup calls. */
272 if (asoc) 273 if (transport)
273 sctp_association_put(asoc); 274 sctp_transport_put(transport);
274 else 275 else
275 sctp_endpoint_put(ep); 276 sctp_endpoint_put(ep);
276 277
@@ -283,8 +284,8 @@ discard_it:
283 284
284discard_release: 285discard_release:
285 /* Release the asoc/ep ref we took in the lookup calls. */ 286 /* Release the asoc/ep ref we took in the lookup calls. */
286 if (asoc) 287 if (transport)
287 sctp_association_put(asoc); 288 sctp_transport_put(transport);
288 else 289 else
289 sctp_endpoint_put(ep); 290 sctp_endpoint_put(ep);
290 291
@@ -300,6 +301,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
300{ 301{
301 struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; 302 struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
302 struct sctp_inq *inqueue = &chunk->rcvr->inqueue; 303 struct sctp_inq *inqueue = &chunk->rcvr->inqueue;
304 struct sctp_transport *t = chunk->transport;
303 struct sctp_ep_common *rcvr = NULL; 305 struct sctp_ep_common *rcvr = NULL;
304 int backloged = 0; 306 int backloged = 0;
305 307
@@ -351,7 +353,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
351done: 353done:
352 /* Release the refs we took in sctp_add_backlog */ 354 /* Release the refs we took in sctp_add_backlog */
353 if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type) 355 if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
354 sctp_association_put(sctp_assoc(rcvr)); 356 sctp_transport_put(t);
355 else if (SCTP_EP_TYPE_SOCKET == rcvr->type) 357 else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
356 sctp_endpoint_put(sctp_ep(rcvr)); 358 sctp_endpoint_put(sctp_ep(rcvr));
357 else 359 else
@@ -363,6 +365,7 @@ done:
363static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb) 365static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb)
364{ 366{
365 struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; 367 struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
368 struct sctp_transport *t = chunk->transport;
366 struct sctp_ep_common *rcvr = chunk->rcvr; 369 struct sctp_ep_common *rcvr = chunk->rcvr;
367 int ret; 370 int ret;
368 371
@@ -373,7 +376,7 @@ static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb)
373 * from us 376 * from us
374 */ 377 */
375 if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type) 378 if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
376 sctp_association_hold(sctp_assoc(rcvr)); 379 sctp_transport_hold(t);
377 else if (SCTP_EP_TYPE_SOCKET == rcvr->type) 380 else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
378 sctp_endpoint_hold(sctp_ep(rcvr)); 381 sctp_endpoint_hold(sctp_ep(rcvr));
379 else 382 else
@@ -537,15 +540,15 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb,
537 return sk; 540 return sk;
538 541
539out: 542out:
540 sctp_association_put(asoc); 543 sctp_transport_put(transport);
541 return NULL; 544 return NULL;
542} 545}
543 546
544/* Common cleanup code for icmp/icmpv6 error handler. */ 547/* Common cleanup code for icmp/icmpv6 error handler. */
545void sctp_err_finish(struct sock *sk, struct sctp_association *asoc) 548void sctp_err_finish(struct sock *sk, struct sctp_transport *t)
546{ 549{
547 bh_unlock_sock(sk); 550 bh_unlock_sock(sk);
548 sctp_association_put(asoc); 551 sctp_transport_put(t);
549} 552}
550 553
551/* 554/*
@@ -641,7 +644,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
641 } 644 }
642 645
643out_unlock: 646out_unlock:
644 sctp_err_finish(sk, asoc); 647 sctp_err_finish(sk, transport);
645} 648}
646 649
647/* 650/*
@@ -952,11 +955,8 @@ static struct sctp_association *__sctp_lookup_association(
952 goto out; 955 goto out;
953 956
954 asoc = t->asoc; 957 asoc = t->asoc;
955 sctp_association_hold(asoc);
956 *pt = t; 958 *pt = t;
957 959
958 sctp_transport_put(t);
959
960out: 960out:
961 return asoc; 961 return asoc;
962} 962}
@@ -986,7 +986,7 @@ int sctp_has_association(struct net *net,
986 struct sctp_transport *transport; 986 struct sctp_transport *transport;
987 987
988 if ((asoc = sctp_lookup_association(net, laddr, paddr, &transport))) { 988 if ((asoc = sctp_lookup_association(net, laddr, paddr, &transport))) {
989 sctp_association_put(asoc); 989 sctp_transport_put(transport);
990 return 1; 990 return 1;
991 } 991 }
992 992
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index f473779e8b1c..176af3080a2b 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -198,7 +198,7 @@ static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
198 } 198 }
199 199
200out_unlock: 200out_unlock:
201 sctp_err_finish(sk, asoc); 201 sctp_err_finish(sk, transport);
202out: 202out:
203 if (likely(idev != NULL)) 203 if (likely(idev != NULL))
204 in6_dev_put(idev); 204 in6_dev_put(idev);