Commit dae399d7 authored by Xin Long's avatar Xin Long Committed by David S. Miller

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: default avatarXin Long <lucien.xin@gmail.com>
Acked-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7c17fcc7
...@@ -152,7 +152,7 @@ void sctp_unhash_endpoint(struct sctp_endpoint *); ...@@ -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 sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
struct sctphdr *, struct sctp_association **, struct sctphdr *, struct sctp_association **,
struct sctp_transport **); 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 *, void sctp_icmp_frag_needed(struct sock *, struct sctp_association *,
struct sctp_transport *t, __u32 pmtu); struct sctp_transport *t, __u32 pmtu);
void sctp_icmp_redirect(struct sock *, struct sctp_transport *, void sctp_icmp_redirect(struct sock *, struct sctp_transport *,
......
...@@ -181,9 +181,10 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -181,9 +181,10 @@ int sctp_rcv(struct sk_buff *skb)
* bound to another interface, via SO_BINDTODEVICE, treat it as OOTB * 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 (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb))) {
if (asoc) { if (transport) {
sctp_association_put(asoc); sctp_transport_put(transport);
asoc = NULL; asoc = NULL;
transport = NULL;
} else { } else {
sctp_endpoint_put(ep); sctp_endpoint_put(ep);
ep = NULL; ep = NULL;
...@@ -269,8 +270,8 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -269,8 +270,8 @@ int sctp_rcv(struct sk_buff *skb)
bh_unlock_sock(sk); bh_unlock_sock(sk);
/* Release the asoc/ep ref we took in the lookup calls. */ /* Release the asoc/ep ref we took in the lookup calls. */
if (asoc) if (transport)
sctp_association_put(asoc); sctp_transport_put(transport);
else else
sctp_endpoint_put(ep); sctp_endpoint_put(ep);
...@@ -283,8 +284,8 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -283,8 +284,8 @@ int sctp_rcv(struct sk_buff *skb)
discard_release: discard_release:
/* Release the asoc/ep ref we took in the lookup calls. */ /* Release the asoc/ep ref we took in the lookup calls. */
if (asoc) if (transport)
sctp_association_put(asoc); sctp_transport_put(transport);
else else
sctp_endpoint_put(ep); sctp_endpoint_put(ep);
...@@ -300,6 +301,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -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_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
struct sctp_inq *inqueue = &chunk->rcvr->inqueue; struct sctp_inq *inqueue = &chunk->rcvr->inqueue;
struct sctp_transport *t = chunk->transport;
struct sctp_ep_common *rcvr = NULL; struct sctp_ep_common *rcvr = NULL;
int backloged = 0; int backloged = 0;
...@@ -351,7 +353,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -351,7 +353,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
done: done:
/* Release the refs we took in sctp_add_backlog */ /* Release the refs we took in sctp_add_backlog */
if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type) 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) else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
sctp_endpoint_put(sctp_ep(rcvr)); sctp_endpoint_put(sctp_ep(rcvr));
else else
...@@ -363,6 +365,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -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) static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb)
{ {
struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
struct sctp_transport *t = chunk->transport;
struct sctp_ep_common *rcvr = chunk->rcvr; struct sctp_ep_common *rcvr = chunk->rcvr;
int ret; int ret;
...@@ -373,7 +376,7 @@ static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb) ...@@ -373,7 +376,7 @@ static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb)
* from us * from us
*/ */
if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type) 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) else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
sctp_endpoint_hold(sctp_ep(rcvr)); sctp_endpoint_hold(sctp_ep(rcvr));
else else
...@@ -537,15 +540,15 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb, ...@@ -537,15 +540,15 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb,
return sk; return sk;
out: out:
sctp_association_put(asoc); sctp_transport_put(transport);
return NULL; return NULL;
} }
/* Common cleanup code for icmp/icmpv6 error handler. */ /* 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); 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) ...@@ -641,7 +644,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
} }
out_unlock: out_unlock:
sctp_err_finish(sk, asoc); sctp_err_finish(sk, transport);
} }
/* /*
...@@ -952,11 +955,8 @@ static struct sctp_association *__sctp_lookup_association( ...@@ -952,11 +955,8 @@ static struct sctp_association *__sctp_lookup_association(
goto out; goto out;
asoc = t->asoc; asoc = t->asoc;
sctp_association_hold(asoc);
*pt = t; *pt = t;
sctp_transport_put(t);
out: out:
return asoc; return asoc;
} }
...@@ -986,7 +986,7 @@ int sctp_has_association(struct net *net, ...@@ -986,7 +986,7 @@ int sctp_has_association(struct net *net,
struct sctp_transport *transport; struct sctp_transport *transport;
if ((asoc = sctp_lookup_association(net, laddr, paddr, &transport))) { if ((asoc = sctp_lookup_association(net, laddr, paddr, &transport))) {
sctp_association_put(asoc); sctp_transport_put(transport);
return 1; return 1;
} }
......
...@@ -198,7 +198,7 @@ static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -198,7 +198,7 @@ static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
} }
out_unlock: out_unlock:
sctp_err_finish(sk, asoc); sctp_err_finish(sk, transport);
out: out:
if (likely(idev != NULL)) if (likely(idev != NULL))
in6_dev_put(idev); in6_dev_put(idev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment