Skip to content

Commit a204466

Browse files
author
Martin KaFai Lau
committed
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>
2 parents 9d8e92e + 64c2f93 commit a204466

4 files changed

Lines changed: 20 additions & 6 deletions

File tree

net/core/sock_map.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ static bool sock_map_redirect_allowed(const struct sock *sk)
530530
if (sk_is_tcp(sk))
531531
return sk->sk_state != TCP_LISTEN;
532532
else
533-
return sk->sk_state == TCP_ESTABLISHED;
533+
return READ_ONCE(sk->sk_state) == TCP_ESTABLISHED;
534534
}
535535

536536
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)
543543
if (sk_is_tcp(sk))
544544
return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN);
545545
if (sk_is_stream_unix(sk))
546-
return (1 << sk->sk_state) & TCPF_ESTABLISHED;
546+
return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED;
547547
if (sk_is_vsock(sk) &&
548548
(sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET))
549549
return (1 << sk->sk_state) & TCPF_ESTABLISHED;

net/unix/af_unix.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3735,15 +3735,15 @@ static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v)
37353735
struct bpf_prog *prog;
37363736
struct sock *sk = v;
37373737
uid_t uid;
3738-
bool slow;
37393738
int ret;
37403739

37413740
if (v == SEQ_START_TOKEN)
37423741
return 0;
37433742

3744-
slow = lock_sock_fast(sk);
3743+
lock_sock(sk);
3744+
unix_state_lock(sk);
37453745

3746-
if (unlikely(sk_unhashed(sk))) {
3746+
if (unlikely(sock_flag(sk, SOCK_DEAD))) {
37473747
ret = SEQ_SKIP;
37483748
goto unlock;
37493749
}
@@ -3753,7 +3753,8 @@ static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v)
37533753
prog = bpf_iter_get_info(&meta, false);
37543754
ret = unix_prog_seq_show(prog, &meta, v, uid);
37553755
unlock:
3756-
unlock_sock_fast(sk, slow);
3756+
unix_state_unlock(sk);
3757+
release_sock(sk);
37573758
return ret;
37583759
}
37593760

net/unix/unix_bpf.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ int unix_stream_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool r
185185
*/
186186
if (!psock->sk_pair) {
187187
sk_pair = unix_peer(sk);
188+
if (unlikely(!sk_pair))
189+
return -EINVAL;
190+
188191
sock_hold(sk_pair);
189192
psock->sk_pair = sk_pair;
190193
}

tools/testing/selftests/bpf/progs/bpf_iter_unix.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77

88
char _license[] SEC("license") = "GPL";
99

10+
SEC(".maps") struct {
11+
__uint(type, BPF_MAP_TYPE_SOCKMAP);
12+
__uint(max_entries, 1);
13+
__type(key, __u32);
14+
__type(value, __u64);
15+
} sockmap;
16+
1017
static long sock_i_ino(const struct sock *sk)
1118
{
1219
const struct socket *sk_socket = sk->sk_socket;
@@ -76,5 +83,8 @@ int dump_unix(struct bpf_iter__unix *ctx)
7683

7784
BPF_SEQ_PRINTF(seq, "\n");
7885

86+
/* Test for deadlock. */
87+
bpf_map_update_elem(&sockmap, &(int){0}, sk, 0);
88+
7989
return 0;
8090
}

0 commit comments

Comments
 (0)