Commit a2044665 authored by Martin KaFai Lau's avatar Martin KaFai Lau
Browse files

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: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parents 9d8e92e1 64c2f93f
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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;
+5 −4
Original line number Diff line number Diff line
@@ -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;
}

+3 −0
Original line number Diff line number Diff line
@@ -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;
	}
+10 −0
Original line number Diff line number Diff line
@@ -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;
}