Skip to content

Commit dad9b33

Browse files
Wang Chendavem330
authored andcommitted
netdevice: Fix promiscuity and allmulti overflow
Max of promiscuity and allmulti plus positive @inc can cause overflow. Fox example: when allmulti=0xFFFFFFFF, any caller give dev_set_allmulti() a positive @inc will cause allmulti be off. This is not what we want, though it's rare case. The fix is that only negative @inc will cause allmulti or promiscuity be off and when any caller makes the counters touch the roof, we return error. Change of v2: Change void function dev_set_promiscuity/allmulti to return int. So callers can get the overflow error. Caller's fix will be done later. Change of v3: 1. Since we return error to caller, we don't need to print KERN_ERROR, KERN_WARNING is enough. 2. In dev_set_promiscuity(), if __dev_set_promiscuity() failed, we return at once. Signed-off-by: Wang Chen <[email protected]> Acked-by: Patrick McHardy <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent dd574db commit dad9b33

File tree

2 files changed

+47
-12
lines changed

2 files changed

+47
-12
lines changed

include/linux/netdevice.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,8 +1476,8 @@ extern int __dev_addr_delete(struct dev_addr_list **list, int *count, void *ad
14761476
extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly);
14771477
extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count);
14781478
extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count);
1479-
extern void dev_set_promiscuity(struct net_device *dev, int inc);
1480-
extern void dev_set_allmulti(struct net_device *dev, int inc);
1479+
extern int dev_set_promiscuity(struct net_device *dev, int inc);
1480+
extern int dev_set_allmulti(struct net_device *dev, int inc);
14811481
extern void netdev_state_change(struct net_device *dev);
14821482
extern void netdev_bonding_change(struct net_device *dev);
14831483
extern void netdev_features_change(struct net_device *dev);

net/core/dev.c

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2771,16 +2771,29 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
27712771
return 0;
27722772
}
27732773

2774-
static void __dev_set_promiscuity(struct net_device *dev, int inc)
2774+
static int __dev_set_promiscuity(struct net_device *dev, int inc)
27752775
{
27762776
unsigned short old_flags = dev->flags;
27772777

27782778
ASSERT_RTNL();
27792779

2780-
if ((dev->promiscuity += inc) == 0)
2781-
dev->flags &= ~IFF_PROMISC;
2782-
else
2783-
dev->flags |= IFF_PROMISC;
2780+
dev->flags |= IFF_PROMISC;
2781+
dev->promiscuity += inc;
2782+
if (dev->promiscuity == 0) {
2783+
/*
2784+
* Avoid overflow.
2785+
* If inc causes overflow, untouch promisc and return error.
2786+
*/
2787+
if (inc < 0)
2788+
dev->flags &= ~IFF_PROMISC;
2789+
else {
2790+
dev->promiscuity -= inc;
2791+
printk(KERN_WARNING "%s: promiscuity touches roof, "
2792+
"set promiscuity failed, promiscuity feature "
2793+
"of device might be broken.\n", dev->name);
2794+
return -EOVERFLOW;
2795+
}
2796+
}
27842797
if (dev->flags != old_flags) {
27852798
printk(KERN_INFO "device %s %s promiscuous mode\n",
27862799
dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
@@ -2798,6 +2811,7 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc)
27982811
if (dev->change_rx_flags)
27992812
dev->change_rx_flags(dev, IFF_PROMISC);
28002813
}
2814+
return 0;
28012815
}
28022816

28032817
/**
@@ -2809,14 +2823,19 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc)
28092823
* remains above zero the interface remains promiscuous. Once it hits zero
28102824
* the device reverts back to normal filtering operation. A negative inc
28112825
* value is used to drop promiscuity on the device.
2826+
* Return 0 if successful or a negative errno code on error.
28122827
*/
2813-
void dev_set_promiscuity(struct net_device *dev, int inc)
2828+
int dev_set_promiscuity(struct net_device *dev, int inc)
28142829
{
28152830
unsigned short old_flags = dev->flags;
2831+
int err;
28162832

2817-
__dev_set_promiscuity(dev, inc);
2833+
err = __dev_set_promiscuity(dev, inc);
2834+
if (!err)
2835+
return err;
28182836
if (dev->flags != old_flags)
28192837
dev_set_rx_mode(dev);
2838+
return err;
28202839
}
28212840

28222841
/**
@@ -2829,22 +2848,38 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
28292848
* to all interfaces. Once it hits zero the device reverts back to normal
28302849
* filtering operation. A negative @inc value is used to drop the counter
28312850
* when releasing a resource needing all multicasts.
2851+
* Return 0 if successful or a negative errno code on error.
28322852
*/
28332853

2834-
void dev_set_allmulti(struct net_device *dev, int inc)
2854+
int dev_set_allmulti(struct net_device *dev, int inc)
28352855
{
28362856
unsigned short old_flags = dev->flags;
28372857

28382858
ASSERT_RTNL();
28392859

28402860
dev->flags |= IFF_ALLMULTI;
2841-
if ((dev->allmulti += inc) == 0)
2842-
dev->flags &= ~IFF_ALLMULTI;
2861+
dev->allmulti += inc;
2862+
if (dev->allmulti == 0) {
2863+
/*
2864+
* Avoid overflow.
2865+
* If inc causes overflow, untouch allmulti and return error.
2866+
*/
2867+
if (inc < 0)
2868+
dev->flags &= ~IFF_ALLMULTI;
2869+
else {
2870+
dev->allmulti -= inc;
2871+
printk(KERN_WARNING "%s: allmulti touches roof, "
2872+
"set allmulti failed, allmulti feature of "
2873+
"device might be broken.\n", dev->name);
2874+
return -EOVERFLOW;
2875+
}
2876+
}
28432877
if (dev->flags ^ old_flags) {
28442878
if (dev->change_rx_flags)
28452879
dev->change_rx_flags(dev, IFF_ALLMULTI);
28462880
dev_set_rx_mode(dev);
28472881
}
2882+
return 0;
28482883
}
28492884

28502885
/*

0 commit comments

Comments
 (0)