diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-07 08:08:57 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-07 08:08:57 -0700 |
| commit | a13307e97d5c54b65720bb71fa379960ded1e51a (patch) | |
| tree | 2bda666bf91053890eac256d9d69236c08d80cdd /net | |
| parent | 0150da6be1c71cd0ad9262293971cb9ea371672b (diff) | |
| parent | 7a3c0289c3c8eb4607dff448ae9ff9f902c813af (diff) | |
| download | linux-a13307e97d5c54b65720bb71fa379960ded1e51a.tar.gz linux-a13307e97d5c54b65720bb71fa379960ded1e51a.zip | |
Merge tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
Pull BPF fixes from Daniel Borkmann:
- Fix BPF verifier to preserve full pointer state for commuted
scalar += pointer arithmetic (Yiyang Chen, Eduard Zingerman)
- Fix a use-after-free of request sockets in the BPF TCP iterator
batching (Jose Fernandez)
- Fix a use-after-free of sk_redir in the BPF sockmap send verdict
path (Chengfeng Ye)
- Fix a netns reference imbalance in the BPF conntrack kfuncs
(Chengfeng Ye)
- Fix bpf_get_fsverity_digest() dynptr assumptions and silent
digest truncation (Eric Biggers)
- Fix bpf_tcp_{gen,check}_syncookie to check sk_state before
sk_protocol to make sure it is a full socket (Luxiao Xu)
- Fix rqspinlock to reset the tail when preserving the queue
on deadlock (Kumar Kartikeya Dwivedi)
* tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf:
rqspinlock: Reset tail when preserving queue on deadlock
bpf: Check sk_state before sk_protocol in bpf_tcp_*_syncookie
fsverity: Fix silent truncation in bpf_get_fsverity_digest()
fsverity: Fix bpf_get_fsverity_digest() dynptr assumptions
bpf: tcp: Fix use-after-free in bpf_iter_tcp_established_batch()
bpf: Fix netns reference imbalance in conntrack kfuncs
bpf, sockmap: Fix sk_redir use-after-free in send verdict
selftests/bpf: Cover commuted pointer state propagation
bpf: Propagate untrusted pointer state in commuted arithmetic
bpf: Preserve pointer state for commuted arithmetic
bpf: Simplify sanitize_err() signature
Diffstat (limited to 'net')
| -rw-r--r-- | net/core/filter.c | 4 | ||||
| -rw-r--r-- | net/ipv4/tcp_bpf.c | 2 | ||||
| -rw-r--r-- | net/ipv4/tcp_ipv4.c | 43 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_bpf.c | 72 |
4 files changed, 76 insertions, 45 deletions
diff --git a/net/core/filter.c b/net/core/filter.c index 11bb0d236822..16845987b244 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -7684,7 +7684,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len return -EINVAL; /* sk_listener() allows TCP_NEW_SYN_RECV, which makes no sense here. */ - if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) + if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP) return -EINVAL; if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) @@ -7757,7 +7757,7 @@ BPF_CALL_5(bpf_tcp_gen_syncookie, struct sock *, sk, void *, iph, u32, iph_len, if (unlikely(!sk || th_len < sizeof(*th) || th_len != th->doff * 4)) return -EINVAL; - if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) + if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP) return -EINVAL; if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index a30475afb6f8..2e234d155b5e 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -469,6 +469,7 @@ more_data: case __SK_REDIRECT: redir_ingress = psock->redir_ingress; sk_redir = psock->sk_redir; + sock_hold(sk_redir); sk_msg_apply_bytes(psock, tosend); if (!psock->apply_bytes) { /* Clean up before releasing the sock lock. */ @@ -489,6 +490,7 @@ more_data: if (eval == __SK_REDIRECT) sock_put(sk_redir); + sock_put(sk_redir); lock_sock(sk); sk_mem_uncharge(sk, sent); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b8887cdd66c5..7f413f509d7d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -3078,24 +3078,24 @@ static unsigned int bpf_iter_tcp_established_batch(struct seq_file *seq, { struct bpf_tcp_iter_state *iter = seq->private; struct hlist_nulls_node *node; - unsigned int expected = 1; - struct sock *sk; - - sock_hold(*start_sk); - iter->batch[iter->end_sk++].sk = *start_sk; + struct sock *sk = *start_sk; + unsigned int expected = 0; - sk = sk_nulls_next(*start_sk); *start_sk = NULL; sk_nulls_for_each_from(sk, node) { - if (seq_sk_match(seq, sk)) { - if (iter->end_sk < iter->max_sk) { - sock_hold(sk); - iter->batch[iter->end_sk++].sk = sk; - } else if (!*start_sk) { - /* Remember where we left off. */ - *start_sk = sk; - } - expected++; + if (!seq_sk_match(seq, sk)) + continue; + expected++; + if (iter->end_sk < iter->max_sk) { + /* reqsk_queue_hash_req() inserts with sk_refcnt == 0 + * and refcount_set()s it after the bucket lock drops. + */ + if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) + continue; + iter->batch[iter->end_sk++].sk = sk; + } else if (!*start_sk) { + /* Remember where we left off. */ + *start_sk = sk; } } @@ -3133,12 +3133,13 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) struct sock *sk; int err; +again: sk = bpf_iter_tcp_resume(seq); if (!sk) return NULL; /* Done */ expected = bpf_iter_fill_batch(seq, &sk); - if (likely(iter->end_sk == expected)) + if (likely(!sk)) goto done; /* Batch size was too small. */ @@ -3157,7 +3158,7 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) return NULL; /* Done */ expected = bpf_iter_fill_batch(seq, &sk); - if (likely(iter->end_sk == expected)) + if (likely(!sk)) goto done; /* Batch size was still too small. Hold onto the lock while we try @@ -3170,10 +3171,14 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) return ERR_PTR(err); } - expected = bpf_iter_fill_batch(seq, &sk); - WARN_ON_ONCE(iter->end_sk != expected); + bpf_iter_fill_batch(seq, &sk); + WARN_ON_ONCE(sk); done: bpf_iter_tcp_unlock_bucket(seq); + if (unlikely(!iter->end_sk)) { + ++iter->state.bucket; + goto again; + } return iter->batch[0].sk; } diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c index f98d1d4b42c3..c2df7c948281 100644 --- a/net/netfilter/nf_conntrack_bpf.c +++ b/net/netfilter/nf_conntrack_bpf.c @@ -122,42 +122,54 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple, struct nf_conntrack_tuple otuple, rtuple; struct nf_conntrack_zone ct_zone; struct nf_conn *ct; + u8 ct_zone_dir = 0; + u16 ct_zone_id; + s32 netns_id; + u8 l4proto; int err; if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) return ERR_PTR(-EINVAL); + + netns_id = READ_ONCE(opts->netns_id); + l4proto = READ_ONCE(opts->l4proto); + ct_zone_id = READ_ONCE(opts->ct_zone_id); if (opts_len == NF_BPF_CT_OPTS_SZ) { - if (opts->reserved[0] || opts->reserved[1] || opts->reserved[2]) + ct_zone_dir = READ_ONCE(opts->ct_zone_dir); + if (READ_ONCE(opts->reserved[0]) || + READ_ONCE(opts->reserved[1]) || + READ_ONCE(opts->reserved[2])) return ERR_PTR(-EINVAL); } else { - if (opts->ct_zone_id) + if (ct_zone_id) return ERR_PTR(-EINVAL); } - if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS)) + if (unlikely(netns_id < BPF_F_CURRENT_NETNS)) return ERR_PTR(-EINVAL); - err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, + err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, IP_CT_DIR_ORIGINAL, &otuple); if (err < 0) return ERR_PTR(err); - err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, + err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, IP_CT_DIR_REPLY, &rtuple); if (err < 0) return ERR_PTR(err); - if (opts->netns_id >= 0) { - net = get_net_ns_by_id(net, opts->netns_id); + if (netns_id >= 0) { + net = get_net_ns_by_id(net, netns_id); if (unlikely(!net)) return ERR_PTR(-ENONET); } if (opts_len == NF_BPF_CT_OPTS_SZ) { - if (opts->ct_zone_dir == 0) - opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; - nf_ct_zone_init(&ct_zone, - opts->ct_zone_id, opts->ct_zone_dir, 0); + if (ct_zone_dir == 0) { + ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; + opts->ct_zone_dir = ct_zone_dir; + } + nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0); } else { ct_zone = nf_ct_zone_dflt; } @@ -171,7 +183,7 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple, __nf_ct_set_timeout(ct, timeout * HZ); out: - if (opts->netns_id >= 0) + if (netns_id >= 0) put_net(net); return ct; @@ -186,46 +198,58 @@ static struct nf_conn *__bpf_nf_ct_lookup(struct net *net, struct nf_conntrack_tuple tuple; struct nf_conntrack_zone ct_zone; struct nf_conn *ct; + u8 ct_zone_dir = 0; + u16 ct_zone_id; + s32 netns_id; + u8 l4proto; int err; if (!opts || !bpf_tuple) return ERR_PTR(-EINVAL); if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) return ERR_PTR(-EINVAL); + + netns_id = READ_ONCE(opts->netns_id); + l4proto = READ_ONCE(opts->l4proto); + ct_zone_id = READ_ONCE(opts->ct_zone_id); if (opts_len == NF_BPF_CT_OPTS_SZ) { - if (opts->reserved[0] || opts->reserved[1] || opts->reserved[2]) + ct_zone_dir = READ_ONCE(opts->ct_zone_dir); + if (READ_ONCE(opts->reserved[0]) || + READ_ONCE(opts->reserved[1]) || + READ_ONCE(opts->reserved[2])) return ERR_PTR(-EINVAL); } else { - if (opts->ct_zone_id) + if (ct_zone_id) return ERR_PTR(-EINVAL); } - if (unlikely(opts->l4proto != IPPROTO_TCP && opts->l4proto != IPPROTO_UDP)) + if (unlikely(l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP)) return ERR_PTR(-EPROTO); - if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS)) + if (unlikely(netns_id < BPF_F_CURRENT_NETNS)) return ERR_PTR(-EINVAL); - err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, + err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, IP_CT_DIR_ORIGINAL, &tuple); if (err < 0) return ERR_PTR(err); - if (opts->netns_id >= 0) { - net = get_net_ns_by_id(net, opts->netns_id); + if (netns_id >= 0) { + net = get_net_ns_by_id(net, netns_id); if (unlikely(!net)) return ERR_PTR(-ENONET); } if (opts_len == NF_BPF_CT_OPTS_SZ) { - if (opts->ct_zone_dir == 0) - opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; - nf_ct_zone_init(&ct_zone, - opts->ct_zone_id, opts->ct_zone_dir, 0); + if (ct_zone_dir == 0) { + ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; + opts->ct_zone_dir = ct_zone_dir; + } + nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0); } else { ct_zone = nf_ct_zone_dflt; } hash = nf_conntrack_find_get(net, &ct_zone, &tuple); - if (opts->netns_id >= 0) + if (netns_id >= 0) put_net(net); if (!hash) return ERR_PTR(-ENOENT); |
