From 9d215e695ba224a036591e923c12adc0252e775f Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Sat, 26 Feb 2011 11:14:05 -0500 Subject: [PATCH] netdevice: Fix promiscuity and allmulti overflow kernel.org commit dad9b335c6940de2746a9788eb456d09cf102f81 --- .../linux/linux-2.6/include/linux/netdevice.h | 4 +- release/src-rt/linux/linux-2.6/net/core/dev.c | 47 ++++++++++++++++++---- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/release/src-rt/linux/linux-2.6/include/linux/netdevice.h b/release/src-rt/linux/linux-2.6/include/linux/netdevice.h index 20fc6bae58..628574d6b8 100644 --- a/release/src-rt/linux/linux-2.6/include/linux/netdevice.h +++ b/release/src-rt/linux/linux-2.6/include/linux/netdevice.h @@ -1008,8 +1008,8 @@ extern void dev_mc_upload(struct net_device *dev); extern int dev_mc_delete(struct net_device *dev, void *addr, int alen, int all); extern int dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly); extern void dev_mc_discard(struct net_device *dev); -extern void dev_set_promiscuity(struct net_device *dev, int inc); -extern void dev_set_allmulti(struct net_device *dev, int inc); +extern int dev_set_promiscuity(struct net_device *dev, int inc); +extern int dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ diff --git a/release/src-rt/linux/linux-2.6/net/core/dev.c b/release/src-rt/linux/linux-2.6/net/core/dev.c index 41efb388c0..f6b0467ad8 100644 --- a/release/src-rt/linux/linux-2.6/net/core/dev.c +++ b/release/src-rt/linux/linux-2.6/net/core/dev.c @@ -2523,15 +2523,29 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) * remains above zero the interface remains promiscuous. Once it hits zero * the device reverts back to normal filtering operation. A negative inc * value is used to drop promiscuity on the device. + * Return 0 if successful or a negative errno code on error. */ -void dev_set_promiscuity(struct net_device *dev, int inc) +int dev_set_promiscuity(struct net_device *dev, int inc) { unsigned short old_flags = dev->flags; - if ((dev->promiscuity += inc) == 0) - dev->flags &= ~IFF_PROMISC; - else - dev->flags |= IFF_PROMISC; + dev->flags |= IFF_PROMISC; + dev->promiscuity += inc; + if (dev->promiscuity == 0) { + /* + * Avoid overflow. + * If inc causes overflow, untouch promisc and return error. + */ + if (inc < 0) + dev->flags &= ~IFF_PROMISC; + else { + dev->promiscuity -= inc; + printk(KERN_WARNING "%s: promiscuity touches roof, " + "set promiscuity failed, promiscuity feature " + "of device might be broken.\n", dev->name); + return -EOVERFLOW; + } + } if (dev->flags != old_flags) { dev_mc_upload(dev); printk(KERN_INFO "device %s %s promiscuous mode\n", @@ -2544,6 +2558,7 @@ void dev_set_promiscuity(struct net_device *dev, int inc) (old_flags & IFF_PROMISC), audit_get_loginuid(current->audit_context)); } + return 0; } /** @@ -2556,17 +2571,33 @@ void dev_set_promiscuity(struct net_device *dev, int inc) * to all interfaces. Once it hits zero the device reverts back to normal * filtering operation. A negative @inc value is used to drop the counter * when releasing a resource needing all multicasts. + * Return 0 if successful or a negative errno code on error. */ -void dev_set_allmulti(struct net_device *dev, int inc) +int dev_set_allmulti(struct net_device *dev, int inc) { unsigned short old_flags = dev->flags; dev->flags |= IFF_ALLMULTI; - if ((dev->allmulti += inc) == 0) - dev->flags &= ~IFF_ALLMULTI; + dev->allmulti += inc; + if (dev->allmulti == 0) { + /* + * Avoid overflow. + * If inc causes overflow, untouch allmulti and return error. + */ + if (inc < 0) + dev->flags &= ~IFF_ALLMULTI; + else { + dev->allmulti -= inc; + printk(KERN_WARNING "%s: allmulti touches roof, " + "set allmulti failed, allmulti feature of " + "device might be broken.\n", dev->name); + return -EOVERFLOW; + } + } if (dev->flags ^ old_flags) dev_mc_upload(dev); + return 0; } unsigned dev_get_flags(const struct net_device *dev) -- 2.11.4.GIT