]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Reject redirect helpers without a bpf_net_context
authorDaniel Borkmann <daniel@iogearbox.net>
Mon, 6 Jul 2026 18:56:07 +0000 (20:56 +0200)
committerJakub Kicinski <kuba@kernel.org>
Tue, 21 Jul 2026 01:16:42 +0000 (18:16 -0700)
The bpf_redirect*() helpers and skb_do_redirect() obtain the per-task
bpf_redirect_info via bpf_net_ctx_get_ri(), which dereferences the
current->bpf_net_context unconditionally. That context is established
on the paths that run tc BPF such as sch_handle_{ingress,egress}(),
*except* for the case where {cls,act}_bpf was attached to a proper
qdisc. A program running from there reaches the NULL deref in two ways:

* It calls bpf_redirect() directly, which dereferences the context at
  the top of the helper:

     tc qdisc add dev eth0 root handle 1: red limit 1MB min 10KB max 20KB \
        avpkt 1000 burst 100 qevent early_drop block 10
     tc filter add block 10 pref 1 bpf obj redirect.o

* It simply returns TC_ACT_REDIRECT without helper call: tcf_qevent_handle()
  then dispatches to skb_do_redirect(), which dereferences the context

Rather than extending bpf_net_context management into the qdisc path,
make the redirect helpers refuse to operate when no context exists, and
have tcf_qevent_handle() drop a TC_ACT_REDIRECT verdict instead of
calling skb_do_redirect(). Previous behaviour was a crash, so nothing
regresses by not supporting it.

Fixes: 401cb7dae813 ("net: Reference bpf_redirect_info via task_struct on PREEMPT_RT.")
Fixes: 3625750f05ec ("net: sched: Introduce helpers for qevent blocks")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: https://patch.msgid.link/20260706185609.330006-2-daniel@iogearbox.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/core/filter.c
net/sched/cls_api.c

index b446aa8be5c375f3d2dbc3e4c0be12a1b3a4a862..11bb0d236822af23bfd7ded995a9041bff586786 100644 (file)
@@ -2552,11 +2552,13 @@ out_drop:
 
 BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags)
 {
-       struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+       struct bpf_redirect_info *ri;
 
-       if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL)))
+       if (unlikely(!bpf_net_ctx_get() ||
+                    (flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL))))
                return TC_ACT_SHOT;
 
+       ri = bpf_net_ctx_get_ri();
        ri->flags = flags;
        ri->tgt_index = ifindex;
 
@@ -2573,11 +2575,12 @@ static const struct bpf_func_proto bpf_redirect_proto = {
 
 BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags)
 {
-       struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+       struct bpf_redirect_info *ri;
 
-       if (unlikely(flags))
+       if (unlikely(!bpf_net_ctx_get() || flags))
                return TC_ACT_SHOT;
 
+       ri = bpf_net_ctx_get_ri();
        ri->flags = BPF_F_PEER;
        ri->tgt_index = ifindex;
 
@@ -2595,11 +2598,13 @@ static const struct bpf_func_proto bpf_redirect_peer_proto = {
 BPF_CALL_4(bpf_redirect_neigh, u32, ifindex, struct bpf_redir_neigh *, params,
           int, plen, u64, flags)
 {
-       struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+       struct bpf_redirect_info *ri;
 
-       if (unlikely((plen && plen < sizeof(*params)) || flags))
+       if (unlikely((plen && plen < sizeof(*params)) ||
+                    !bpf_net_ctx_get() || flags))
                return TC_ACT_SHOT;
 
+       ri = bpf_net_ctx_get_ri();
        ri->flags = BPF_F_NEIGH | (plen ? BPF_F_NEXTHOP : 0);
        ri->tgt_index = ifindex;
 
index ffeea6db833747b5befee4d6c0ce2f889771a7e1..523cf2a8bd1d6d1257bf7110709be89f0b5daec3 100644 (file)
@@ -4046,6 +4046,8 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
        fl = rcu_dereference_bh(qe->filter_chain);
 
        switch (tcf_classify(skb, NULL, fl, &cl_res, false)) {
+       case TC_ACT_REDIRECT:
+               fallthrough;
        case TC_ACT_SHOT:
                qdisc_qstats_drop(sch);
                __qdisc_drop(skb, to_free);
@@ -4057,10 +4059,6 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
                __qdisc_drop(skb, to_free);
                *ret = __NET_XMIT_STOLEN;
                return NULL;
-       case TC_ACT_REDIRECT:
-               skb_do_redirect(skb);
-               *ret = __NET_XMIT_STOLEN;
-               return NULL;
        case TC_ACT_CONSUMED:
                *ret = __NET_XMIT_STOLEN;
                return NULL;