summaryrefslogtreecommitdiff
path: root/net/core/sock_map.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/core/sock_map.c')
-rw-r--r--net/core/sock_map.c69
1 files changed, 51 insertions, 18 deletions
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index 02a68be3002a..ca49bc7f8687 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -392,8 +392,8 @@ static void *sock_map_lookup(struct bpf_map *map, void *key)
sk = __sock_map_lookup_elem(map, *(u32 *)key);
if (!sk)
return NULL;
- if (sk_is_refcounted(sk) && !refcount_inc_not_zero(&sk->sk_refcnt))
- return NULL;
+ if (sk_is_refcounted(sk))
+ sock_hold(sk);
return sk;
}
@@ -542,6 +542,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk)
{
if (sk_is_tcp(sk))
return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN);
+ if (sk_is_udp(sk))
+ return sk_hashed(sk);
if (sk_is_stream_unix(sk))
return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED;
if (sk_is_vsock(sk) &&
@@ -1216,8 +1218,8 @@ static void *sock_hash_lookup(struct bpf_map *map, void *key)
sk = __sock_hash_lookup_elem(map, key);
if (!sk)
return NULL;
- if (sk_is_refcounted(sk) && !refcount_inc_not_zero(&sk->sk_refcnt))
- return NULL;
+ if (sk_is_refcounted(sk))
+ sock_hold(sk);
return sk;
}
@@ -1515,6 +1517,17 @@ static int sock_map_prog_link_lookup(struct bpf_map *map, struct bpf_prog ***ppr
return 0;
}
+static int sock_map_prog_attach_check(enum bpf_attach_type attach_type,
+ struct bpf_prog *prog)
+{
+ /* A stream parser must not modify the skb, only measure it. */
+ if (prog && attach_type == BPF_SK_SKB_STREAM_PARSER &&
+ prog->aux->changes_pkt_data)
+ return -EINVAL;
+
+ return 0;
+}
+
/* Handle the following four cases:
* prog_attach: prog != NULL, old == NULL, link == NULL
* prog_detach: prog == NULL, old != NULL, link == NULL
@@ -1533,6 +1546,10 @@ static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
if (ret)
return ret;
+ ret = sock_map_prog_attach_check(which, prog);
+ if (ret)
+ return ret;
+
/* for prog_attach/prog_detach/link_attach, return error if a bpf_link
* exists for that prog.
*/
@@ -1630,18 +1647,23 @@ void sock_map_unhash(struct sock *sk)
void (*saved_unhash)(struct sock *sk);
struct sk_psock *psock;
+retry:
rcu_read_lock();
psock = sk_psock(sk);
if (unlikely(!psock)) {
rcu_read_unlock();
saved_unhash = READ_ONCE(sk->sk_prot)->unhash;
+ if (unlikely(saved_unhash == sock_map_unhash))
+ goto retry;
} else {
saved_unhash = psock->saved_unhash;
sock_map_remove_links(sk, psock);
rcu_read_unlock();
+
+ if (WARN_ON_ONCE(saved_unhash == sock_map_unhash))
+ return;
}
- if (WARN_ON_ONCE(saved_unhash == sock_map_unhash))
- return;
+
if (saved_unhash)
saved_unhash(sk);
}
@@ -1652,20 +1674,25 @@ void sock_map_destroy(struct sock *sk)
void (*saved_destroy)(struct sock *sk);
struct sk_psock *psock;
+retry:
rcu_read_lock();
psock = sk_psock_get(sk);
if (unlikely(!psock)) {
rcu_read_unlock();
saved_destroy = READ_ONCE(sk->sk_prot)->destroy;
+ if (unlikely(saved_destroy == sock_map_destroy))
+ goto retry;
} else {
saved_destroy = psock->saved_destroy;
sock_map_remove_links(sk, psock);
rcu_read_unlock();
sk_psock_stop(psock);
sk_psock_put(sk, psock);
+
+ if (WARN_ON_ONCE(saved_destroy == sock_map_destroy))
+ return;
}
- if (WARN_ON_ONCE(saved_destroy == sock_map_destroy))
- return;
+
if (saved_destroy)
saved_destroy(sk);
}
@@ -1676,32 +1703,33 @@ void sock_map_close(struct sock *sk, long timeout)
void (*saved_close)(struct sock *sk, long timeout);
struct sk_psock *psock;
+retry:
lock_sock(sk);
rcu_read_lock();
- psock = sk_psock(sk);
+ psock = sk_psock_get(sk);
if (likely(psock)) {
saved_close = psock->saved_close;
sock_map_remove_links(sk, psock);
- psock = sk_psock_get(sk);
- if (unlikely(!psock))
- goto no_psock;
rcu_read_unlock();
sk_psock_stop(psock);
release_sock(sk);
cancel_delayed_work_sync(&psock->work);
sk_psock_put(sk, psock);
+
+ /* Make sure we do not recurse. This is a bug.
+ * Leak the socket instead of crashing on a stack overflow.
+ */
+ if (WARN_ON_ONCE(saved_close == sock_map_close))
+ return;
} else {
saved_close = READ_ONCE(sk->sk_prot)->close;
-no_psock:
rcu_read_unlock();
release_sock(sk);
+
+ if (unlikely(saved_close == sock_map_close))
+ goto retry;
}
- /* Make sure we do not recurse. This is a bug.
- * Leak the socket instead of crashing on a stack overflow.
- */
- if (WARN_ON_ONCE(saved_close == sock_map_close))
- return;
saved_close(sk, timeout);
}
EXPORT_SYMBOL_GPL(sock_map_close);
@@ -1765,6 +1793,11 @@ static int sock_map_link_update_prog(struct bpf_link *link,
ret = -EINVAL;
goto out;
}
+
+ ret = sock_map_prog_attach_check(link->attach_type, prog);
+ if (ret)
+ goto out;
+
if (!sockmap_link->map) {
ret = -ENOLINK;
goto out;