bpf_prog_test_run_skb() calls eth_type_trans() first and then uses skb->protocol to initialize sk family and address fields for the test run.
For IPv4 and IPv6 packets, it may access ip_hdr(skb) or ipv6_hdr(skb) to initialize sk fields. Reject the input earlier if the Ethernet frame carries IPv4/IPv6 EtherType but the L3 header is too short. Fold the IPv4/IPv6 header length checks into the existing protocol switch and return -EINVAL before accessing the network headers. Also extend empty_skb selftests with ETH_HLEN-sized packets carrying IPv4/IPv6 EtherType but no L3 header. Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=619b9ef527f510a57cfc Signed-off-by: Sun Jian <[email protected]> --- Changes in v3: - Rework the fix by moving the checks into the existing protocol switch in bpf_prog_test_run_skb() instead of duplicating the switch. - Add empty_skb selftests for ETH_HLEN-sized packets with IPv4/IPv6 EtherType but no L3 header. - Retarget to bpf-next. Link: https://lore.kernel.org/bpf/[email protected]/ net/bpf/test_run.c | 20 ++++++++----- .../selftests/bpf/prog_tests/empty_skb.c | 29 +++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 178c4738e63b..300e2bfc5a62 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -1120,19 +1120,23 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, switch (skb->protocol) { case htons(ETH_P_IP): - sk->sk_family = AF_INET; - if (sizeof(struct iphdr) <= skb_headlen(skb)) { - sk->sk_rcv_saddr = ip_hdr(skb)->saddr; - sk->sk_daddr = ip_hdr(skb)->daddr; + if (skb_headlen(skb) < sizeof(struct iphdr)) { + ret = -EINVAL; + goto out; } + sk->sk_family = AF_INET; + sk->sk_rcv_saddr = ip_hdr(skb)->saddr; + sk->sk_daddr = ip_hdr(skb)->daddr; break; #if IS_ENABLED(CONFIG_IPV6) case htons(ETH_P_IPV6): - sk->sk_family = AF_INET6; - if (sizeof(struct ipv6hdr) <= skb_headlen(skb)) { - sk->sk_v6_rcv_saddr = ipv6_hdr(skb)->saddr; - sk->sk_v6_daddr = ipv6_hdr(skb)->daddr; + if (skb_headlen(skb) < sizeof(struct ipv6hdr)) { + ret = -EINVAL; + goto out; } + sk->sk_family = AF_INET6; + sk->sk_v6_rcv_saddr = ipv6_hdr(skb)->saddr; + sk->sk_v6_daddr = ipv6_hdr(skb)->daddr; break; #endif default: diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c index 438583e1f2d1..d53567e9cd77 100644 --- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c +++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c @@ -12,6 +12,8 @@ void test_empty_skb(void) struct bpf_program *prog; char eth_hlen_pp[15]; char eth_hlen[14]; + char ipv4_eth_hlen[14]; + char ipv6_eth_hlen[14]; int veth_ifindex; int ipip_ifindex; int err; @@ -46,6 +48,24 @@ void test_empty_skb(void) .err = -EINVAL, }, + /* ETH_HLEN-sized packets with IPv4/IPv6 EtherType but + * no L3 header are rejected. + */ + { + .msg = "veth short IPv4 ingress packet", + .data_in = ipv4_eth_hlen, + .data_size_in = sizeof(ipv4_eth_hlen), + .ifindex = &veth_ifindex, + .err = -EINVAL, + }, + { + .msg = "veth short IPv6 ingress packet", + .data_in = ipv6_eth_hlen, + .data_size_in = sizeof(ipv6_eth_hlen), + .ifindex = &veth_ifindex, + .err = -EINVAL, + }, + /* ETH_HLEN-sized packets: * - can not be redirected at LWT_XMIT * - can be redirected at TC to non-tunneling dest @@ -108,6 +128,15 @@ void test_empty_skb(void) SYS(out, "ip addr add 192.168.1.1/16 dev ipip0"); ipip_ifindex = if_nametoindex("ipip0"); + memset(ipv4_eth_hlen, 0, sizeof(ipv4_eth_hlen)); + memset(ipv6_eth_hlen, 0, sizeof(ipv6_eth_hlen)); + + ipv4_eth_hlen[12] = 0x08; + ipv4_eth_hlen[13] = 0x00; + + ipv6_eth_hlen[12] = 0x86; + ipv6_eth_hlen[13] = 0xdd; + bpf_obj = empty_skb__open_and_load(); if (!ASSERT_OK_PTR(bpf_obj, "open skeleton")) goto out; -- 2.43.0

