A tc egress BPF program can push a tiny link-layer header with
bpf_skb_change_head() on an skb whose head is almost empty, then
redirect it to an Ethernet device ingress. __bpf_redirect_common()
only checks mac_header < network_header, and __dev_forward_skb2()
crashes in eth_type_trans():

  tc filter add dev lo egress pref 1 matchall action vlan pop_eth continue
  tc filter add dev lo egress pref 2 bpf da obj prog.o
  # prog: bpf_skb_change_head(skb, 1, 0);
  #       return bpf_redirect(veth1_ifindex, BPF_F_INGRESS);
  # AF_PACKET SOCK_RAW on lo: eth / eth / 8192 bytes of payload

  kernel BUG at include/linux/skbuff.h:2839!
  RIP: 0010:eth_type_trans (include/linux/etherdevice.h:635 
net/ethernet/eth.c:163)
  Call Trace:
   __dev_forward_skb2 (net/core/dev.c:2507)
   dev_forward_skb_nomtu (net/core/dev.c:2546)
   __dev_queue_xmit (net/core/dev.c:4738 net/core/dev.c:4998)
   packet_sendmsg (net/packet/af_packet.c:3157 net/packet/af_packet.c:3189)

Use eth_type_trans_careful(), and drop the packet if the Ethernet
header can not be pulled. This also covers other dev_forward_skb()
users (veth, macvlan, ipvlan, netdevsim, l2tp_eth).

Update the empty_skb BPF selftest: at lwt_xmit, BPF_PROG_TEST_RUN
strips the Ethernet header, so the "veth ETH_HLEN+1" packet is a 1-byte
skb. Redirecting it to veth ingress used to read the Ethernet header
past skb->tail and deliver a 1-byte frame; it is now dropped with
NET_RX_DROP, as veth_xmit() already does at egress.

Fixes: 3a0af8fd61f9 ("bpf: BPF for lightweight tunnel infrastructure")
Signed-off-by: Eric Dumazet <[email protected]>
Reviewed-by: Jamal Hadi Salim <[email protected]>
Tested-by: Jamal Hadi Salim <[email protected]>
---
 net/core/dev.c                                | 18 +++++++++++++-----
 .../selftests/bpf/prog_tests/empty_skb.c      | 19 ++++++++++++-------
 2 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index 
b3622e532f269333ed62e31fbca0f6ac56dcde8f..a51d53491fa23158cb31fa4764f41f0ad9ecc978
 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2501,14 +2501,22 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable);
 static int __dev_forward_skb2(struct net_device *dev, struct sk_buff *skb,
                              bool check_mtu)
 {
-       int ret = ____dev_forward_skb(dev, skb, check_mtu);
+       enum skb_drop_reason reason;
+       int ret;
+
+       ret = ____dev_forward_skb(dev, skb, check_mtu);
+       if (unlikely(ret))
+               return ret;
 
-       if (likely(!ret)) {
-               skb->protocol = eth_type_trans(skb, dev);
-               skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
+       reason = eth_type_trans_careful(skb, dev);
+       if (unlikely(reason)) {
+               dev_core_stats_rx_dropped_inc(dev);
+               kfree_skb_reason(skb, reason);
+               return NET_RX_DROP;
        }
+       skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
 
-       return ret;
+       return 0;
 }
 
 int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c 
b/tools/testing/selftests/bpf/prog_tests/empty_skb.c
index 
c9fcb70cbafbce571d7826e233920193625dead1..a3938c16557b140e3ee5c116139cf8b636f85e35
 100644
--- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c
+++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c
@@ -24,7 +24,7 @@ void test_empty_skb(void)
                int *ifindex;
                int err;
                int ret;
-               int lwt_egress_ret; /* expected retval at lwt/egress */
+               int lwt_ret; /* expected retval at lwt */
                __be16 h_proto;
                bool success_on_tc;
                bool adjust_room;
@@ -82,7 +82,7 @@ void test_empty_skb(void)
                        .data_size_in = sizeof(eth_hlen),
                        .ifindex = &veth_ifindex,
                        .ret = -ERANGE,
-                       .lwt_egress_ret = -ERANGE,
+                       .lwt_ret = -ERANGE,
                        .success_on_tc = true,
                },
                {
@@ -96,17 +96,23 @@ void test_empty_skb(void)
                        .data_size_in = sizeof(eth_hlen),
                        .ifindex = &ipip_ifindex,
                        .ret = -ERANGE,
-                       .lwt_egress_ret = -ERANGE,
+                       .lwt_ret = -ERANGE,
                },
 
-               /* ETH_HLEN+1-sized packet should be redirected. */
+               /* ETH_HLEN+1-sized packet should be redirected at TC.
+                * At LWT, the Ethernet header is not part of the skb, and
+                * a 1-byte packet is too short for an Ethernet device.
+                */
 
                {
                        .msg = "veth ETH_HLEN+1 packet ingress",
                        .data_in = eth_hlen_pp,
                        .data_size_in = sizeof(eth_hlen_pp),
                        .ifindex = &veth_ifindex,
-                       .lwt_egress_ret = 1, /* veth_xmit NET_XMIT_DROP */
+                       /* lwt: skb->len=1, dropped by veth_xmit() at egress
+                        * and by __dev_forward_skb2() at ingress.
+                        */
+                       .lwt_ret = 1, /* NET_XMIT_DROP / NET_RX_DROP */
                },
                {
                        .msg = "ipip ETH_HLEN+1 packet ingress",
@@ -144,7 +150,6 @@ void test_empty_skb(void)
                        eth_hlen.h_proto = tests[i].h_proto;
 
                bpf_object__for_each_program(prog, bpf_obj->obj) {
-                       bool at_egress = strstr(bpf_program__name(prog), 
"egress") != NULL;
                        bool at_tc = !strncmp(bpf_program__section_name(prog), 
"tc", 2);
                        bool is_adjust_room = !strcmp(bpf_program__name(prog), 
"tc_adjust_room");
                        int expected_ret;
@@ -153,7 +158,7 @@ void test_empty_skb(void)
                        if (tests[i].adjust_room != is_adjust_room)
                                continue;
 
-                       expected_ret = at_egress && !at_tc ? 
tests[i].lwt_egress_ret : tests[i].ret;
+                       expected_ret = at_tc ? tests[i].ret : tests[i].lwt_ret;
 
                        tattr.data_in = tests[i].data_in;
                        tattr.data_size_in = tests[i].data_size_in;
-- 
2.53.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to