diff options
author | Eric Dumazet <edumazet@google.com> | 2015-11-01 18:36:55 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-11-02 16:28:49 -0500 |
commit | 9e17f8a475fca81950fdddc08df428ed66cf441f (patch) | |
tree | 417a79910266f08806f78be2dd970102afa3356f | |
parent | eca1e006cf6f6f66a1a90c055a8a6d393475c3f9 (diff) |
net: make skb_set_owner_w() more robust
skb_set_owner_w() is called from various places that assume
skb->sk always point to a full blown socket (as it changes
sk->sk_wmem_alloc)
We'd like to attach skb to request sockets, and in the future
to timewait sockets as well. For these kind of pseudo sockets,
we need to take a traditional refcount and use sock_edemux()
as the destructor.
It is now time to un-inline skb_set_owner_w(), being too big.
Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Bisected-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/sock.h | 17 | ||||
-rw-r--r-- | net/core/sock.c | 22 | ||||
-rw-r--r-- | net/ipv4/tcp_output.c | 4 |
3 files changed, 25 insertions, 18 deletions
diff --git a/include/net/sock.h b/include/net/sock.h index aeed5c95f3ca..f570e75e3da9 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -1951,6 +1951,8 @@ static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk) | |||
1951 | } | 1951 | } |
1952 | } | 1952 | } |
1953 | 1953 | ||
1954 | void skb_set_owner_w(struct sk_buff *skb, struct sock *sk); | ||
1955 | |||
1954 | /* | 1956 | /* |
1955 | * Queue a received datagram if it will fit. Stream and sequenced | 1957 | * Queue a received datagram if it will fit. Stream and sequenced |
1956 | * protocols can't normally use this as they need to fit buffers in | 1958 | * protocols can't normally use this as they need to fit buffers in |
@@ -1959,21 +1961,6 @@ static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk) | |||
1959 | * Inlined as it's very short and called for pretty much every | 1961 | * Inlined as it's very short and called for pretty much every |
1960 | * packet ever received. | 1962 | * packet ever received. |
1961 | */ | 1963 | */ |
1962 | |||
1963 | static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk) | ||
1964 | { | ||
1965 | skb_orphan(skb); | ||
1966 | skb->sk = sk; | ||
1967 | skb->destructor = sock_wfree; | ||
1968 | skb_set_hash_from_sk(skb, sk); | ||
1969 | /* | ||
1970 | * We used to take a refcount on sk, but following operation | ||
1971 | * is enough to guarantee sk_free() wont free this sock until | ||
1972 | * all in-flight packets are completed | ||
1973 | */ | ||
1974 | atomic_add(skb->truesize, &sk->sk_wmem_alloc); | ||
1975 | } | ||
1976 | |||
1977 | static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk) | 1964 | static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk) |
1978 | { | 1965 | { |
1979 | skb_orphan(skb); | 1966 | skb_orphan(skb); |
diff --git a/net/core/sock.c b/net/core/sock.c index 0ef30aa90132..7529eb9463be 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -1656,6 +1656,28 @@ void sock_wfree(struct sk_buff *skb) | |||
1656 | } | 1656 | } |
1657 | EXPORT_SYMBOL(sock_wfree); | 1657 | EXPORT_SYMBOL(sock_wfree); |
1658 | 1658 | ||
1659 | void skb_set_owner_w(struct sk_buff *skb, struct sock *sk) | ||
1660 | { | ||
1661 | skb_orphan(skb); | ||
1662 | skb->sk = sk; | ||
1663 | #ifdef CONFIG_INET | ||
1664 | if (unlikely(!sk_fullsock(sk))) { | ||
1665 | skb->destructor = sock_edemux; | ||
1666 | sock_hold(sk); | ||
1667 | return; | ||
1668 | } | ||
1669 | #endif | ||
1670 | skb->destructor = sock_wfree; | ||
1671 | skb_set_hash_from_sk(skb, sk); | ||
1672 | /* | ||
1673 | * We used to take a refcount on sk, but following operation | ||
1674 | * is enough to guarantee sk_free() wont free this sock until | ||
1675 | * all in-flight packets are completed | ||
1676 | */ | ||
1677 | atomic_add(skb->truesize, &sk->sk_wmem_alloc); | ||
1678 | } | ||
1679 | EXPORT_SYMBOL(skb_set_owner_w); | ||
1680 | |||
1659 | void skb_orphan_partial(struct sk_buff *skb) | 1681 | void skb_orphan_partial(struct sk_buff *skb) |
1660 | { | 1682 | { |
1661 | /* TCP stack sets skb->ooo_okay based on sk_wmem_alloc, | 1683 | /* TCP stack sets skb->ooo_okay based on sk_wmem_alloc, |
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f4f9793eb025..cb7ca569052c 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -2963,9 +2963,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, | |||
2963 | skb_reserve(skb, MAX_TCP_HEADER); | 2963 | skb_reserve(skb, MAX_TCP_HEADER); |
2964 | 2964 | ||
2965 | if (attach_req) { | 2965 | if (attach_req) { |
2966 | skb->destructor = sock_edemux; | 2966 | skb_set_owner_w(skb, req_to_sk(req)); |
2967 | sock_hold(req_to_sk(req)); | ||
2968 | skb->sk = req_to_sk(req); | ||
2969 | } else { | 2967 | } else { |
2970 | /* sk is a const pointer, because we want to express multiple | 2968 | /* sk is a const pointer, because we want to express multiple |
2971 | * cpu might call us concurrently. | 2969 | * cpu might call us concurrently. |