mirror of
https://github.com/torvalds/linux.git
synced 2024-12-31 23:31:29 +00:00
[SCTP]: Fix potential null pointer dereference while handling an icmp error
Signed-off-by: Sridhar Samudrala <sri@us.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
ee71a29eb5
commit
d1ad1ff299
@ -167,15 +167,12 @@ void sctp_unhash_established(struct sctp_association *);
|
|||||||
void sctp_hash_endpoint(struct sctp_endpoint *);
|
void sctp_hash_endpoint(struct sctp_endpoint *);
|
||||||
void sctp_unhash_endpoint(struct sctp_endpoint *);
|
void sctp_unhash_endpoint(struct sctp_endpoint *);
|
||||||
struct sock *sctp_err_lookup(int family, struct sk_buff *,
|
struct sock *sctp_err_lookup(int family, struct sk_buff *,
|
||||||
struct sctphdr *, struct sctp_endpoint **,
|
struct sctphdr *, struct sctp_association **,
|
||||||
struct sctp_association **,
|
|
||||||
struct sctp_transport **);
|
struct sctp_transport **);
|
||||||
void sctp_err_finish(struct sock *, struct sctp_endpoint *,
|
void sctp_err_finish(struct sock *, struct sctp_association *);
|
||||||
struct sctp_association *);
|
|
||||||
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_proto_unreachable(struct sock *sk,
|
void sctp_icmp_proto_unreachable(struct sock *sk,
|
||||||
struct sctp_endpoint *ep,
|
|
||||||
struct sctp_association *asoc,
|
struct sctp_association *asoc,
|
||||||
struct sctp_transport *t);
|
struct sctp_transport *t);
|
||||||
|
|
||||||
|
@ -351,7 +351,6 @@ void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc,
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void sctp_icmp_proto_unreachable(struct sock *sk,
|
void sctp_icmp_proto_unreachable(struct sock *sk,
|
||||||
struct sctp_endpoint *ep,
|
|
||||||
struct sctp_association *asoc,
|
struct sctp_association *asoc,
|
||||||
struct sctp_transport *t)
|
struct sctp_transport *t)
|
||||||
{
|
{
|
||||||
@ -367,7 +366,6 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
|
|||||||
/* Common lookup code for icmp/icmpv6 error handler. */
|
/* Common lookup code for icmp/icmpv6 error handler. */
|
||||||
struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
|
struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
|
||||||
struct sctphdr *sctphdr,
|
struct sctphdr *sctphdr,
|
||||||
struct sctp_endpoint **epp,
|
|
||||||
struct sctp_association **app,
|
struct sctp_association **app,
|
||||||
struct sctp_transport **tpp)
|
struct sctp_transport **tpp)
|
||||||
{
|
{
|
||||||
@ -375,11 +373,10 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
|
|||||||
union sctp_addr daddr;
|
union sctp_addr daddr;
|
||||||
struct sctp_af *af;
|
struct sctp_af *af;
|
||||||
struct sock *sk = NULL;
|
struct sock *sk = NULL;
|
||||||
struct sctp_endpoint *ep = NULL;
|
|
||||||
struct sctp_association *asoc = NULL;
|
struct sctp_association *asoc = NULL;
|
||||||
struct sctp_transport *transport = NULL;
|
struct sctp_transport *transport = NULL;
|
||||||
|
|
||||||
*app = NULL; *epp = NULL; *tpp = NULL;
|
*app = NULL; *tpp = NULL;
|
||||||
|
|
||||||
af = sctp_get_af_specific(family);
|
af = sctp_get_af_specific(family);
|
||||||
if (unlikely(!af)) {
|
if (unlikely(!af)) {
|
||||||
@ -394,27 +391,16 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
|
|||||||
* packet.
|
* packet.
|
||||||
*/
|
*/
|
||||||
asoc = __sctp_lookup_association(&saddr, &daddr, &transport);
|
asoc = __sctp_lookup_association(&saddr, &daddr, &transport);
|
||||||
if (!asoc) {
|
if (!asoc)
|
||||||
/* If there is no matching association, see if it matches any
|
return NULL;
|
||||||
* endpoint. This may happen for an ICMP error generated in
|
|
||||||
* response to an INIT_ACK.
|
sk = asoc->base.sk;
|
||||||
*/
|
|
||||||
ep = __sctp_rcv_lookup_endpoint(&daddr);
|
if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) {
|
||||||
if (!ep) {
|
ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
|
||||||
return NULL;
|
goto out;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asoc) {
|
|
||||||
sk = asoc->base.sk;
|
|
||||||
|
|
||||||
if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) {
|
|
||||||
ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
sk = ep->base.sk;
|
|
||||||
|
|
||||||
sctp_bh_lock_sock(sk);
|
sctp_bh_lock_sock(sk);
|
||||||
|
|
||||||
/* If too many ICMPs get dropped on busy
|
/* If too many ICMPs get dropped on busy
|
||||||
@ -423,7 +409,6 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
|
|||||||
if (sock_owned_by_user(sk))
|
if (sock_owned_by_user(sk))
|
||||||
NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
|
NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
|
||||||
|
|
||||||
*epp = ep;
|
|
||||||
*app = asoc;
|
*app = asoc;
|
||||||
*tpp = transport;
|
*tpp = transport;
|
||||||
return sk;
|
return sk;
|
||||||
@ -432,21 +417,16 @@ out:
|
|||||||
sock_put(sk);
|
sock_put(sk);
|
||||||
if (asoc)
|
if (asoc)
|
||||||
sctp_association_put(asoc);
|
sctp_association_put(asoc);
|
||||||
if (ep)
|
|
||||||
sctp_endpoint_put(ep);
|
|
||||||
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_endpoint *ep,
|
void sctp_err_finish(struct sock *sk, struct sctp_association *asoc)
|
||||||
struct sctp_association *asoc)
|
|
||||||
{
|
{
|
||||||
sctp_bh_unlock_sock(sk);
|
sctp_bh_unlock_sock(sk);
|
||||||
sock_put(sk);
|
sock_put(sk);
|
||||||
if (asoc)
|
if (asoc)
|
||||||
sctp_association_put(asoc);
|
sctp_association_put(asoc);
|
||||||
if (ep)
|
|
||||||
sctp_endpoint_put(ep);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -471,7 +451,6 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
|
|||||||
int type = skb->h.icmph->type;
|
int type = skb->h.icmph->type;
|
||||||
int code = skb->h.icmph->code;
|
int code = skb->h.icmph->code;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct sctp_endpoint *ep;
|
|
||||||
struct sctp_association *asoc;
|
struct sctp_association *asoc;
|
||||||
struct sctp_transport *transport;
|
struct sctp_transport *transport;
|
||||||
struct inet_sock *inet;
|
struct inet_sock *inet;
|
||||||
@ -488,7 +467,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
|
|||||||
savesctp = skb->h.raw;
|
savesctp = skb->h.raw;
|
||||||
skb->nh.iph = iph;
|
skb->nh.iph = iph;
|
||||||
skb->h.raw = (char *)sh;
|
skb->h.raw = (char *)sh;
|
||||||
sk = sctp_err_lookup(AF_INET, skb, sh, &ep, &asoc, &transport);
|
sk = sctp_err_lookup(AF_INET, skb, sh, &asoc, &transport);
|
||||||
/* Put back, the original pointers. */
|
/* Put back, the original pointers. */
|
||||||
skb->nh.raw = saveip;
|
skb->nh.raw = saveip;
|
||||||
skb->h.raw = savesctp;
|
skb->h.raw = savesctp;
|
||||||
@ -515,7 +494,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (ICMP_PROT_UNREACH == code) {
|
if (ICMP_PROT_UNREACH == code) {
|
||||||
sctp_icmp_proto_unreachable(sk, ep, asoc,
|
sctp_icmp_proto_unreachable(sk, asoc,
|
||||||
transport);
|
transport);
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
@ -544,7 +523,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
sctp_err_finish(sk, ep, asoc);
|
sctp_err_finish(sk, asoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -91,7 +91,6 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
|||||||
struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
|
struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
|
||||||
struct sctphdr *sh = (struct sctphdr *)(skb->data + offset);
|
struct sctphdr *sh = (struct sctphdr *)(skb->data + offset);
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct sctp_endpoint *ep;
|
|
||||||
struct sctp_association *asoc;
|
struct sctp_association *asoc;
|
||||||
struct sctp_transport *transport;
|
struct sctp_transport *transport;
|
||||||
struct ipv6_pinfo *np;
|
struct ipv6_pinfo *np;
|
||||||
@ -105,7 +104,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
|||||||
savesctp = skb->h.raw;
|
savesctp = skb->h.raw;
|
||||||
skb->nh.ipv6h = iph;
|
skb->nh.ipv6h = iph;
|
||||||
skb->h.raw = (char *)sh;
|
skb->h.raw = (char *)sh;
|
||||||
sk = sctp_err_lookup(AF_INET6, skb, sh, &ep, &asoc, &transport);
|
sk = sctp_err_lookup(AF_INET6, skb, sh, &asoc, &transport);
|
||||||
/* Put back, the original pointers. */
|
/* Put back, the original pointers. */
|
||||||
skb->nh.raw = saveip;
|
skb->nh.raw = saveip;
|
||||||
skb->h.raw = savesctp;
|
skb->h.raw = savesctp;
|
||||||
@ -124,7 +123,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
|||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
case ICMPV6_PARAMPROB:
|
case ICMPV6_PARAMPROB:
|
||||||
if (ICMPV6_UNK_NEXTHDR == code) {
|
if (ICMPV6_UNK_NEXTHDR == code) {
|
||||||
sctp_icmp_proto_unreachable(sk, ep, asoc, transport);
|
sctp_icmp_proto_unreachable(sk, asoc, transport);
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -142,7 +141,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
sctp_err_finish(sk, ep, asoc);
|
sctp_err_finish(sk, asoc);
|
||||||
out:
|
out:
|
||||||
if (likely(idev != NULL))
|
if (likely(idev != NULL))
|
||||||
in6_dev_put(idev);
|
in6_dev_put(idev);
|
||||||
|
Loading…
Reference in New Issue
Block a user