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).

Fixes: 3a0af8fd61f9 ("bpf: BPF for lightweight tunnel infrastructure")
Signed-off-by: Eric Dumazet <[email protected]>
---
 net/core/dev.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index 
1a09e7869118059e92b53e00ed873dfc7485ffc3..ac784310a69c1d9ceecf68909334196b555871c5
 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)
-- 
2.53.0

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

Reply via email to