Commit d5485d9d authored by Yang Yingliang's avatar Yang Yingliang Committed by David S. Miller
Browse files

net: neigh: don't call kfree_skb() under spin_lock_irqsave()



It is not allowed to call kfree_skb() from hardware interrupt
context or with interrupts being disabled. So add all skb to
a tmp list, then free them after spin_unlock_irqrestore() at
once.

Fixes: 66ba215c ("neigh: fix possible DoS due to net iface start/stop loop")
Suggested-by: default avatarDenis V. Lunev <den@openvz.org>
Signed-off-by: default avatarYang Yingliang <yangyingliang@huawei.com>
Reviewed-by: default avatarNikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 550e9a4d
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -309,14 +309,17 @@ static int neigh_del_timer(struct neighbour *n)

static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net)
{
	struct sk_buff_head tmp;
	unsigned long flags;
	struct sk_buff *skb;

	skb_queue_head_init(&tmp);
	spin_lock_irqsave(&list->lock, flags);
	skb = skb_peek(list);
	while (skb != NULL) {
		struct sk_buff *skb_next = skb_peek_next(skb, list);
		struct net_device *dev = skb->dev;

		if (net == NULL || net_eq(dev_net(dev), net)) {
			struct in_device *in_dev;

@@ -326,13 +329,16 @@ static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net)
				in_dev->arp_parms->qlen--;
			rcu_read_unlock();
			__skb_unlink(skb, list);

			dev_put(dev);
			kfree_skb(skb);
			__skb_queue_tail(&tmp, skb);
		}
		skb = skb_next;
	}
	spin_unlock_irqrestore(&list->lock, flags);

	while ((skb = __skb_dequeue(&tmp))) {
		dev_put(skb->dev);
		kfree_skb(skb);
	}
}

static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,