Skip to content

Commit

Permalink
bpf: Don't refcount LISTEN sockets in sk_assign()
Browse files Browse the repository at this point in the history
Avoid taking a reference on listen sockets by checking the socket type
in the sk_assign and in the corresponding skb_steal_sock() code in the
the transport layer, and by ensuring that the prefetch free (sock_pfree)
function uses the same logic to check whether the socket is refcounted.

Suggested-by: Martin KaFai Lau <[email protected]>
Signed-off-by: Joe Stringer <[email protected]>
Signed-off-by: Alexei Starovoitov <[email protected]>
Acked-by: Martin KaFai Lau <[email protected]>
Link: https://lore.kernel.org/bpf/[email protected]
  • Loading branch information
joestringer authored and Alexei Starovoitov committed Mar 30, 2020
1 parent 71489e2 commit 7ae215d
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 12 deletions.
25 changes: 17 additions & 8 deletions include/net/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -2537,6 +2537,21 @@ skb_sk_is_prefetched(struct sk_buff *skb)
#endif /* CONFIG_INET */
}

/* This helper checks if a socket is a full socket,
* ie _not_ a timewait or request socket.
*/
static inline bool sk_fullsock(const struct sock *sk)
{
return (1 << sk->sk_state) & ~(TCPF_TIME_WAIT | TCPF_NEW_SYN_RECV);
}

static inline bool
sk_is_refcounted(struct sock *sk)
{
/* Only full sockets have sk->sk_flags. */
return !sk_fullsock(sk) || !sock_flag(sk, SOCK_RCU_FREE);
}

/**
* skb_steal_sock
* @skb to steal the socket from
Expand All @@ -2549,6 +2564,8 @@ skb_steal_sock(struct sk_buff *skb, bool *refcounted)
struct sock *sk = skb->sk;

*refcounted = true;
if (skb_sk_is_prefetched(skb))
*refcounted = sk_is_refcounted(sk);
skb->destructor = NULL;
skb->sk = NULL;
return sk;
Expand All @@ -2557,14 +2574,6 @@ skb_steal_sock(struct sk_buff *skb, bool *refcounted)
return NULL;
}

/* This helper checks if a socket is a full socket,
* ie _not_ a timewait or request socket.
*/
static inline bool sk_fullsock(const struct sock *sk)
{
return (1 << sk->sk_state) & ~(TCPF_TIME_WAIT | TCPF_NEW_SYN_RECV);
}

/* Checks if this SKB belongs to an HW offloaded socket
* and whether any SW fallbacks are required based on dev.
* Check decrypted mark in case skb_orphan() cleared socket.
Expand Down
6 changes: 3 additions & 3 deletions net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -5401,8 +5401,7 @@ static const struct bpf_func_proto bpf_sk_lookup_udp_proto = {

BPF_CALL_1(bpf_sk_release, struct sock *, sk)
{
/* Only full sockets have sk->sk_flags. */
if (!sk_fullsock(sk) || !sock_flag(sk, SOCK_RCU_FREE))
if (sk_is_refcounted(sk))
sock_gen_put(sk);
return 0;
}
Expand Down Expand Up @@ -5928,7 +5927,8 @@ BPF_CALL_3(bpf_sk_assign, struct sk_buff *, skb, struct sock *, sk, u64, flags)
return -ENETUNREACH;
if (unlikely(sk->sk_reuseport))
return -ESOCKTNOSUPPORT;
if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
if (sk_is_refcounted(sk) &&
unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
return -ENOENT;

skb_orphan(skb);
Expand Down
3 changes: 2 additions & 1 deletion net/core/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -2077,7 +2077,8 @@ EXPORT_SYMBOL(sock_efree);
#ifdef CONFIG_INET
void sock_pfree(struct sk_buff *skb)
{
sock_gen_put(skb->sk);
if (sk_is_refcounted(skb->sk))
sock_gen_put(skb->sk);
}
EXPORT_SYMBOL(sock_pfree);
#endif /* CONFIG_INET */
Expand Down

0 comments on commit 7ae215d

Please sign in to comment.