]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: zero-initialize the fib lookup flow struct
authorAvinash Duduskar <avinash.duduskar@gmail.com>
Wed, 17 Jun 2026 22:47:19 +0000 (04:17 +0530)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 22 Jun 2026 01:07:53 +0000 (18:07 -0700)
bpf_ipv4_fib_lookup() and bpf_ipv6_fib_lookup() build the flow key on
the stack with a bare "struct flowi4 fl4;" / "struct flowi6 fl6;" and
fill it field by field, but never set flowi4_l3mdev / flowi6_l3mdev.

On the non-DIRECT path the lookup goes through the fib rules whenever the
netns has custom rules, which a VRF installs:

bpf_ipv4_fib_lookup() -> fib_lookup() -> __fib_lookup()
  -> l3mdev_update_flow()   reads !fl->flowi_l3mdev
  -> fib_rules_lookup() -> fib_rule_match()
       -> l3mdev_fib_rule_match()   uses fl->flowi_l3mdev

l3mdev_update_flow() resolves the l3mdev master from the ingress device
only while the field is still zero. Left at a nonzero stack value the
resolution is skipped, and l3mdev_fib_rule_match() then tests that value
as an ifindex, so the VRF master is not resolved and the rule fails to
match: an ingress enslaved to a VRF can fail to select its table. FIB
rules matching on an L3 master device (l3mdev_fib_rule_iif_match()/
_oif_match()) read the same value, so an "ip rule iif/oif <vrf>"
mismatches the same way.

Zero-initialize the whole flow struct rather than adding one more
field assignment, so any flowi field added later is covered too.
ip_route_input_slow() likewise zeroes the field before its input lookup.

CONFIG_INIT_STACK_ALL_ZERO masks this by default, but it depends on
compiler support (CC_HAS_AUTO_VAR_INIT_ZERO), so INIT_STACK_NONE builds,
including older toolchains that fall back to it, are exposed. Built with
INIT_STACK_ALL_PATTERN, a plain bpf_fib_lookup (no VLAN, no DIRECT) over a
VRF slave whose destination is routed only in the VRF table returns
BPF_FIB_LKUP_RET_NOT_FWDED, and resolves with this patch. On the default
config the lookup succeeds either way, so ordinary testing does not catch
the bug.

Fixes: 40867d74c374 ("net: Add l3mdev index to flow struct and avoid oif reset for port devices")
Signed-off-by: Avinash Duduskar <avinash.duduskar@gmail.com>
Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
Link: https://lore.kernel.org/r/20260617224719.1428599-1-avinash.duduskar@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
net/core/filter.c

index 2e96b4b847ce12d1063e6df24105451acdd3c21a..1dd5e37ae13027a9028f425d54e0a866ae784d4a 100644 (file)
@@ -6221,7 +6221,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
        struct in_device *in_dev;
        struct net_device *dev;
        struct fib_result res;
-       struct flowi4 fl4;
+       struct flowi4 fl4 = {};
        u32 mtu = 0;
        int err;
 
@@ -6361,7 +6361,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
        struct neighbour *neigh;
        struct net_device *dev;
        struct inet6_dev *idev;
-       struct flowi6 fl6;
+       struct flowi6 fl6 = {};
        int strict = 0;
        int oif, err;
        u32 mtu = 0;