Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vxlan: add adjacent link to limit depth level

Current vxlan code doesn't limit the number of nested devices.
Nested devices would be handled recursively and this routine needs
huge stack memory. So, unlimited nested devices could make
stack overflow.

In order to fix this issue, this patch adds adjacent links.
The adjacent link APIs internally check the depth level.

Test commands:
ip link add dummy0 type dummy
ip link add vxlan0 type vxlan id 0 group 239.1.1.1 dev dummy0 \
dstport 4789
for i in {1..100}
do
let A=$i-1
ip link add vxlan$i type vxlan id $i group 239.1.1.1 \
dev vxlan$A dstport 4789
done
ip link del dummy0

The top upper link is vxlan100 and the lowest link is vxlan0.
When vxlan0 is deleting, the upper devices will be deleted recursively.
It needs huge stack memory so it makes stack overflow.

Splat looks like:
[ 229.628477] =============================================================================
[ 229.629785] BUG page->ptl (Not tainted): Padding overwritten. 0x0000000026abf214-0x0000000091f6abb2
[ 229.629785] -----------------------------------------------------------------------------
[ 229.629785]
[ 229.655439] ==================================================================
[ 229.629785] INFO: Slab 0x00000000ff7cfda8 objects=19 used=19 fp=0x00000000fe33776c flags=0x200000000010200
[ 229.655688] BUG: KASAN: stack-out-of-bounds in unmap_single_vma+0x25a/0x2e0
[ 229.655688] Read of size 8 at addr ffff888113076928 by task vlan-network-in/2334
[ 229.655688]
[ 229.629785] Padding 0000000026abf214: 00 80 14 0d 81 88 ff ff 68 91 81 14 81 88 ff ff ........h.......
[ 229.629785] Padding 0000000001e24790: 38 91 81 14 81 88 ff ff 68 91 81 14 81 88 ff ff 8.......h.......
[ 229.629785] Padding 00000000b39397c8: 33 30 62 a7 ff ff ff ff ff eb 60 22 10 f1 ff 1f 30b.......`"....
[ 229.629785] Padding 00000000bc98f53a: 80 60 07 13 81 88 ff ff 00 80 14 0d 81 88 ff ff .`..............
[ 229.629785] Padding 000000002aa8123d: 68 91 81 14 81 88 ff ff f7 21 17 a7 ff ff ff ff h........!......
[ 229.629785] Padding 000000001c8c2369: 08 81 14 0d 81 88 ff ff 03 02 00 00 00 00 00 00 ................
[ 229.629785] Padding 000000004e290c5d: 21 90 a2 21 10 ed ff ff 00 00 00 00 00 fc ff df !..!............
[ 229.629785] Padding 000000000e25d731: 18 60 07 13 81 88 ff ff c0 8b 13 05 81 88 ff ff .`..............
[ 229.629785] Padding 000000007adc7ab3: b3 8a b5 41 00 00 00 00 ...A....
[ 229.629785] FIX page->ptl: Restoring 0x0000000026abf214-0x0000000091f6abb2=0x5a
[ ... ]

Fixes: acaf4e70997f ("net: vxlan: when lower dev unregisters remove vxlan dev as well")
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Taehee Yoo and committed by
David S. Miller
0ce1822c 32b6d34f

+44 -10
+43 -10
drivers/net/vxlan.c
··· 3566 3566 { 3567 3567 struct vxlan_net *vn = net_generic(net, vxlan_net_id); 3568 3568 struct vxlan_dev *vxlan = netdev_priv(dev); 3569 + struct net_device *remote_dev = NULL; 3569 3570 struct vxlan_fdb *f = NULL; 3570 3571 bool unregister = false; 3572 + struct vxlan_rdst *dst; 3571 3573 int err; 3572 3574 3575 + dst = &vxlan->default_dst; 3573 3576 err = vxlan_dev_configure(net, dev, conf, false, extack); 3574 3577 if (err) 3575 3578 return err; ··· 3580 3577 dev->ethtool_ops = &vxlan_ethtool_ops; 3581 3578 3582 3579 /* create an fdb entry for a valid default destination */ 3583 - if (!vxlan_addr_any(&vxlan->default_dst.remote_ip)) { 3580 + if (!vxlan_addr_any(&dst->remote_ip)) { 3584 3581 err = vxlan_fdb_create(vxlan, all_zeros_mac, 3585 - &vxlan->default_dst.remote_ip, 3582 + &dst->remote_ip, 3586 3583 NUD_REACHABLE | NUD_PERMANENT, 3587 3584 vxlan->cfg.dst_port, 3588 - vxlan->default_dst.remote_vni, 3589 - vxlan->default_dst.remote_vni, 3590 - vxlan->default_dst.remote_ifindex, 3585 + dst->remote_vni, 3586 + dst->remote_vni, 3587 + dst->remote_ifindex, 3591 3588 NTF_SELF, &f); 3592 3589 if (err) 3593 3590 return err; ··· 3598 3595 goto errout; 3599 3596 unregister = true; 3600 3597 3598 + if (dst->remote_ifindex) { 3599 + remote_dev = __dev_get_by_index(net, dst->remote_ifindex); 3600 + if (!remote_dev) 3601 + goto errout; 3602 + 3603 + err = netdev_upper_dev_link(remote_dev, dev, extack); 3604 + if (err) 3605 + goto errout; 3606 + } 3607 + 3601 3608 err = rtnl_configure_link(dev, NULL); 3602 3609 if (err) 3603 - goto errout; 3610 + goto unlink; 3604 3611 3605 3612 if (f) { 3606 - vxlan_fdb_insert(vxlan, all_zeros_mac, 3607 - vxlan->default_dst.remote_vni, f); 3613 + vxlan_fdb_insert(vxlan, all_zeros_mac, dst->remote_vni, f); 3608 3614 3609 3615 /* notify default fdb entry */ 3610 3616 err = vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), 3611 3617 RTM_NEWNEIGH, true, extack); 3612 3618 if (err) { 3613 3619 vxlan_fdb_destroy(vxlan, f, false, false); 3620 + if (remote_dev) 3621 + netdev_upper_dev_unlink(remote_dev, dev); 3614 3622 goto unregister; 3615 3623 } 3616 3624 } 3617 3625 3618 3626 list_add(&vxlan->next, &vn->vxlan_list); 3627 + if (remote_dev) 3628 + dst->remote_dev = remote_dev; 3619 3629 return 0; 3620 - 3630 + unlink: 3631 + if (remote_dev) 3632 + netdev_upper_dev_unlink(remote_dev, dev); 3621 3633 errout: 3622 3634 /* unregister_netdevice() destroys the default FDB entry with deletion 3623 3635 * notification. But the addition notification was not sent yet, so ··· 3950 3932 struct netlink_ext_ack *extack) 3951 3933 { 3952 3934 struct vxlan_dev *vxlan = netdev_priv(dev); 3953 - struct vxlan_rdst *dst = &vxlan->default_dst; 3954 3935 struct net_device *lowerdev; 3955 3936 struct vxlan_config conf; 3937 + struct vxlan_rdst *dst; 3956 3938 int err; 3957 3939 3940 + dst = &vxlan->default_dst; 3958 3941 err = vxlan_nl2conf(tb, data, dev, &conf, true, extack); 3959 3942 if (err) 3960 3943 return err; 3961 3944 3962 3945 err = vxlan_config_validate(vxlan->net, &conf, &lowerdev, 3963 3946 vxlan, extack); 3947 + if (err) 3948 + return err; 3949 + 3950 + err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev, 3951 + extack); 3964 3952 if (err) 3965 3953 return err; 3966 3954 ··· 3986 3962 NTF_SELF, true, extack); 3987 3963 if (err) { 3988 3964 spin_unlock_bh(&vxlan->hash_lock[hash_index]); 3965 + netdev_adjacent_change_abort(dst->remote_dev, 3966 + lowerdev, dev); 3989 3967 return err; 3990 3968 } 3991 3969 } ··· 4005 3979 if (conf.age_interval != vxlan->cfg.age_interval) 4006 3980 mod_timer(&vxlan->age_timer, jiffies); 4007 3981 3982 + netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev); 3983 + if (lowerdev && lowerdev != dst->remote_dev) 3984 + dst->remote_dev = lowerdev; 3985 + 3986 + netdev_update_lockdep_key(lowerdev); 4008 3987 vxlan_config_apply(dev, &conf, lowerdev, vxlan->net, true); 4009 3988 return 0; 4010 3989 } ··· 4022 3991 4023 3992 list_del(&vxlan->next); 4024 3993 unregister_netdevice_queue(dev, head); 3994 + if (vxlan->default_dst.remote_dev) 3995 + netdev_upper_dev_unlink(vxlan->default_dst.remote_dev, dev); 4025 3996 } 4026 3997 4027 3998 static size_t vxlan_get_size(const struct net_device *dev)
+1
include/net/vxlan.h
··· 197 197 u8 offloaded:1; 198 198 __be32 remote_vni; 199 199 u32 remote_ifindex; 200 + struct net_device *remote_dev; 200 201 struct list_head list; 201 202 struct rcu_head rcu; 202 203 struct dst_cache dst_cache;