summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin KaFai Lau <martin.lau@kernel.org>2026-04-15 23:15:25 +0300
committerMartin KaFai Lau <martin.lau@kernel.org>2026-04-16 03:23:18 +0300
commita20446652956ed26be529b8788e76dfddf705717 (patch)
tree6f75774aaa618edfb54067b5716c4d42e9ff952f
parent9d8e92e15f75794c469f586a2c47fab58f093a3a (diff)
parent64c2f93fc3254d3bf5de4445fb732ee5c451edb6 (diff)
downloadlinux-a20446652956ed26be529b8788e76dfddf705717.tar.xz
Merge branch 'bpf-sockmap-fix-af_unix-null-ptr-deref-in-proto-update'
Michal Luczaj says: ==================== bpf, sockmap: Fix af_unix null-ptr-deref in proto update Updating sockmap/sockhash using a unix sock races unix_stream_connect(): when sock_map_sk_state_allowed() passes (sk_state == TCP_ESTABLISHED), unix_peer(sk) in unix_stream_bpf_update_proto() may still return NULL. ==================== Link: https://patch.msgid.link/20260414-unix-proto-update-null-ptr-deref-v4-0-2af6fe97918e@rbox.co Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
-rw-r--r--net/core/sock_map.c4
-rw-r--r--net/unix/af_unix.c9
-rw-r--r--net/unix/unix_bpf.c3
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_iter_unix.c10
4 files changed, 20 insertions, 6 deletions
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index b0e96337a269..02a68be3002a 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -530,7 +530,7 @@ static bool sock_map_redirect_allowed(const struct sock *sk)
if (sk_is_tcp(sk))
return sk->sk_state != TCP_LISTEN;
else
- return sk->sk_state == TCP_ESTABLISHED;
+ return READ_ONCE(sk->sk_state) == TCP_ESTABLISHED;
}
static bool sock_map_sk_is_suitable(const struct sock *sk)
@@ -543,7 +543,7 @@ 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_stream_unix(sk))
- return (1 << sk->sk_state) & TCPF_ESTABLISHED;
+ return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED;
if (sk_is_vsock(sk) &&
(sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET))
return (1 << sk->sk_state) & TCPF_ESTABLISHED;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 4c4a8d23ddd2..f668ff107722 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -3735,15 +3735,15 @@ static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v)
struct bpf_prog *prog;
struct sock *sk = v;
uid_t uid;
- bool slow;
int ret;
if (v == SEQ_START_TOKEN)
return 0;
- slow = lock_sock_fast(sk);
+ lock_sock(sk);
+ unix_state_lock(sk);
- if (unlikely(sk_unhashed(sk))) {
+ if (unlikely(sock_flag(sk, SOCK_DEAD))) {
ret = SEQ_SKIP;
goto unlock;
}
@@ -3753,7 +3753,8 @@ static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v)
prog = bpf_iter_get_info(&meta, false);
ret = unix_prog_seq_show(prog, &meta, v, uid);
unlock:
- unlock_sock_fast(sk, slow);
+ unix_state_unlock(sk);
+ release_sock(sk);
return ret;
}
diff --git a/net/unix/unix_bpf.c b/net/unix/unix_bpf.c
index d14cd5454a8d..f86ff19e9764 100644
--- a/net/unix/unix_bpf.c
+++ b/net/unix/unix_bpf.c
@@ -185,6 +185,9 @@ int unix_stream_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool r
*/
if (!psock->sk_pair) {
sk_pair = unix_peer(sk);
+ if (unlikely(!sk_pair))
+ return -EINVAL;
+
sock_hold(sk_pair);
psock->sk_pair = sk_pair;
}
diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_unix.c b/tools/testing/selftests/bpf/progs/bpf_iter_unix.c
index fea275df9e22..a2652c8c3616 100644
--- a/tools/testing/selftests/bpf/progs/bpf_iter_unix.c
+++ b/tools/testing/selftests/bpf/progs/bpf_iter_unix.c
@@ -7,6 +7,13 @@
char _license[] SEC("license") = "GPL";
+SEC(".maps") struct {
+ __uint(type, BPF_MAP_TYPE_SOCKMAP);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u64);
+} sockmap;
+
static long sock_i_ino(const struct sock *sk)
{
const struct socket *sk_socket = sk->sk_socket;
@@ -76,5 +83,8 @@ int dump_unix(struct bpf_iter__unix *ctx)
BPF_SEQ_PRINTF(seq, "\n");
+ /* Test for deadlock. */
+ bpf_map_update_elem(&sockmap, &(int){0}, sk, 0);
+
return 0;
}