Patch "neigh: fix possible DoS due to net iface start/stop loop" has been added to the 4.14-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    neigh: fix possible DoS due to net iface start/stop loop

to the 4.14-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     neigh-fix-possible-dos-due-to-net-iface-start-stop-l.patch
and it can be found in the queue-4.14 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 4deaacb992f2bf7477ec740e048ece18557edac8
Author: Denis V. Lunev <den@xxxxxxxxxx>
Date:   Thu Aug 11 18:20:11 2022 +0300

    neigh: fix possible DoS due to net iface start/stop loop
    
    [ Upstream commit 66ba215cb51323e4e55e38fd5f250e0fae0cbc94 ]
    
    Normal processing of ARP request (usually this is Ethernet broadcast
    packet) coming to the host is looking like the following:
    * the packet comes to arp_process() call and is passed through routing
      procedure
    * the request is put into the queue using pneigh_enqueue() if
      corresponding ARP record is not local (common case for container
      records on the host)
    * the request is processed by timer (within 80 jiffies by default) and
      ARP reply is sent from the same arp_process() using
      NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED condition (flag is set inside
      pneigh_enqueue())
    
    And here the problem comes. Linux kernel calls pneigh_queue_purge()
    which destroys the whole queue of ARP requests on ANY network interface
    start/stop event through __neigh_ifdown().
    
    This is actually not a problem within the original world as network
    interface start/stop was accessible to the host 'root' only, which
    could do more destructive things. But the world is changed and there
    are Linux containers available. Here container 'root' has an access
    to this API and could be considered as untrusted user in the hosting
    (container's) world.
    
    Thus there is an attack vector to other containers on node when
    container's root will endlessly start/stop interfaces. We have observed
    similar situation on a real production node when docker container was
    doing such activity and thus other containers on the node become not
    accessible.
    
    The patch proposed doing very simple thing. It drops only packets from
    the same namespace in the pneigh_queue_purge() where network interface
    state change is detected. This is enough to prevent the problem for the
    whole node preserving original semantics of the code.
    
    v2:
            - do del_timer_sync() if queue is empty after pneigh_queue_purge()
    v3:
            - rebase to net tree
    
    Cc: "David S. Miller" <davem@xxxxxxxxxxxxx>
    Cc: Eric Dumazet <edumazet@xxxxxxxxxx>
    Cc: Jakub Kicinski <kuba@xxxxxxxxxx>
    Cc: Paolo Abeni <pabeni@xxxxxxxxxx>
    Cc: Daniel Borkmann <daniel@xxxxxxxxxxxxx>
    Cc: David Ahern <dsahern@xxxxxxxxxx>
    Cc: Yajun Deng <yajun.deng@xxxxxxxxx>
    Cc: Roopa Prabhu <roopa@xxxxxxxxxx>
    Cc: Christian Brauner <brauner@xxxxxxxxxx>
    Cc: netdev@xxxxxxxxxxxxxxx
    Cc: linux-kernel@xxxxxxxxxxxxxxx
    Cc: Alexey Kuznetsov <kuznet@xxxxxxxxxxxxx>
    Cc: Alexander Mikhalitsyn <alexander.mikhalitsyn@xxxxxxxxxxxxx>
    Cc: Konstantin Khorenko <khorenko@xxxxxxxxxxxxx>
    Cc: kernel@xxxxxxxxxx
    Cc: devel@xxxxxxxxxx
    Investigated-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@xxxxxxxxxxxxx>
    Signed-off-by: Denis V. Lunev <den@xxxxxxxxxx>
    Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 358e84af0210b..8af9761768e00 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -222,14 +222,23 @@ static int neigh_del_timer(struct neighbour *n)
 	return 0;
 }
 
-static void pneigh_queue_purge(struct sk_buff_head *list)
+static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net)
 {
+	unsigned long flags;
 	struct sk_buff *skb;
 
-	while ((skb = skb_dequeue(list)) != NULL) {
-		dev_put(skb->dev);
-		kfree_skb(skb);
+	spin_lock_irqsave(&list->lock, flags);
+	skb = skb_peek(list);
+	while (skb != NULL) {
+		struct sk_buff *skb_next = skb_peek_next(skb, list);
+		if (net == NULL || net_eq(dev_net(skb->dev), net)) {
+			__skb_unlink(skb, list);
+			dev_put(skb->dev);
+			kfree_skb(skb);
+		}
+		skb = skb_next;
 	}
+	spin_unlock_irqrestore(&list->lock, flags);
 }
 
 static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
@@ -295,9 +304,9 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
 	write_lock_bh(&tbl->lock);
 	neigh_flush_dev(tbl, dev);
 	pneigh_ifdown_and_unlock(tbl, dev);
-
-	del_timer_sync(&tbl->proxy_timer);
-	pneigh_queue_purge(&tbl->proxy_queue);
+	pneigh_queue_purge(&tbl->proxy_queue, dev_net(dev));
+	if (skb_queue_empty_lockless(&tbl->proxy_queue))
+		del_timer_sync(&tbl->proxy_timer);
 	return 0;
 }
 EXPORT_SYMBOL(neigh_ifdown);
@@ -1609,7 +1618,7 @@ int neigh_table_clear(int index, struct neigh_table *tbl)
 	/* It is not clean... Fix it to unload IPv6 module safely */
 	cancel_delayed_work_sync(&tbl->gc_work);
 	del_timer_sync(&tbl->proxy_timer);
-	pneigh_queue_purge(&tbl->proxy_queue);
+	pneigh_queue_purge(&tbl->proxy_queue, NULL);
 	neigh_ifdown(tbl, NULL);
 	if (atomic_read(&tbl->entries))
 		pr_crit("neighbour leakage\n");



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux