lowpan_event() assumes any netdevice with dev->type == ARPHRD_6LOWPAN has a struct lowpan_dev in netdev_priv(dev). However, non-6LoWPAN devices can also have ARPHRD_6LOWPAN, e.g. a TUN device via TUNSETLINK or a bonding device inheriting dev->type from its lower device. Bringing such a device up or down causes lowpan_event() to read and overwrite unrelated netdev_priv() memory. Since unprivileged users can create TUN and bonding devices in a user and network namespace, they can corrupt struct bonding and trigger a slab use-after-free. Also check dev->ndisc_ops == &lowpan_ndisc_ops, which is set only by lowpan_register_netdevice() on genuine 6LoWPAN devices. Signed-off-by: Roman Gushchin Fixes: 5609c185f24d ("6lowpan: iphc: add support for stateful compression") Fixes: 2ad3ed59198c ("6lowpan: add 802.15.4 short addr slaac") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/bug/linux-97b284e0-576e-4373-b62f-eb132f482b91 --- net/6lowpan/6lowpan_i.h | 13 ++++++++++--- net/6lowpan/core.c | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h index 01853cec0209..07f11d4dde6e 100644 --- a/net/6lowpan/6lowpan_i.h +++ b/net/6lowpan/6lowpan_i.h @@ -2,19 +2,26 @@ #ifndef __6LOWPAN_I_H #define __6LOWPAN_I_H +#include #include #include -/* caller need to be sure it's dev->type is ARPHRD_6LOWPAN */ +extern const struct ndisc_ops lowpan_ndisc_ops; + +static inline bool lowpan_is_dev(const struct net_device *dev) +{ + return dev->type == ARPHRD_6LOWPAN && + dev->ndisc_ops == &lowpan_ndisc_ops; +} + +/* caller must ensure lowpan_is_dev(dev) is true */ static inline bool lowpan_is_ll(const struct net_device *dev, enum lowpan_lltypes lltype) { return lowpan_dev(dev)->lltype == lltype; } -extern const struct ndisc_ops lowpan_ndisc_ops; - int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev); #ifdef CONFIG_6LOWPAN_DEBUGFS diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c index 850d4a185f55..748103d37e31 100644 --- a/net/6lowpan/core.c +++ b/net/6lowpan/core.c @@ -113,7 +113,7 @@ static int lowpan_event(struct notifier_block *unused, struct in6_addr addr; int i; - if (dev->type != ARPHRD_6LOWPAN) + if (!lowpan_is_dev(dev)) return NOTIFY_DONE; idev = __in6_dev_get(dev); -- 2.56.0.455.g6c5a503b38-goog