mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-11-02 07:43:17 +00:00
110 lines
4.3 KiB
Diff
110 lines
4.3 KiB
Diff
From cfc0872ee582ec66872c9cca964e876b1595b794 Mon Sep 17 00:00:00 2001
|
|
From: Xin Long <lucien.xin@gmail.com>
|
|
Date: Tue, 25 Apr 2017 22:58:37 +0800
|
|
Subject: [PATCH 14/14] bridge: move bridge multicast cleanup to ndo_uninit
|
|
|
|
During removing a bridge device, if the bridge is still up, a new mdb entry
|
|
still can be added in br_multicast_add_group() after all mdb entries are
|
|
removed in br_multicast_dev_del(). Like the path:
|
|
|
|
mld_ifc_timer_expire ->
|
|
mld_sendpack -> ...
|
|
br_multicast_rcv ->
|
|
br_multicast_add_group
|
|
|
|
The new mp's timer will be set up. If the timer expires after the bridge
|
|
is freed, it may cause use-after-free panic in br_multicast_group_expired.
|
|
|
|
BUG: unable to handle kernel NULL pointer dereference at 0000000000000048
|
|
IP: [<ffffffffa07ed2c8>] br_multicast_group_expired+0x28/0xb0 [bridge]
|
|
Call Trace:
|
|
<IRQ>
|
|
[<ffffffff81094536>] call_timer_fn+0x36/0x110
|
|
[<ffffffffa07ed2a0>] ? br_mdb_free+0x30/0x30 [bridge]
|
|
[<ffffffff81096967>] run_timer_softirq+0x237/0x340
|
|
[<ffffffff8108dcbf>] __do_softirq+0xef/0x280
|
|
[<ffffffff8169889c>] call_softirq+0x1c/0x30
|
|
[<ffffffff8102c275>] do_softirq+0x65/0xa0
|
|
[<ffffffff8108e055>] irq_exit+0x115/0x120
|
|
[<ffffffff81699515>] smp_apic_timer_interrupt+0x45/0x60
|
|
[<ffffffff81697a5d>] apic_timer_interrupt+0x6d/0x80
|
|
|
|
Nikolay also found it would cause a memory leak - the mdb hash is
|
|
reallocated and not freed due to the mdb rehash.
|
|
|
|
unreferenced object 0xffff8800540ba800 (size 2048):
|
|
backtrace:
|
|
[<ffffffff816e2287>] kmemleak_alloc+0x67/0xc0
|
|
[<ffffffff81260bea>] __kmalloc+0x1ba/0x3e0
|
|
[<ffffffffa05c60ee>] br_mdb_rehash+0x5e/0x340 [bridge]
|
|
[<ffffffffa05c74af>] br_multicast_new_group+0x43f/0x6e0 [bridge]
|
|
[<ffffffffa05c7aa3>] br_multicast_add_group+0x203/0x260 [bridge]
|
|
[<ffffffffa05ca4b5>] br_multicast_rcv+0x945/0x11d0 [bridge]
|
|
[<ffffffffa05b6b10>] br_dev_xmit+0x180/0x470 [bridge]
|
|
[<ffffffff815c781b>] dev_hard_start_xmit+0xbb/0x3d0
|
|
[<ffffffff815c8743>] __dev_queue_xmit+0xb13/0xc10
|
|
[<ffffffff815c8850>] dev_queue_xmit+0x10/0x20
|
|
[<ffffffffa02f8d7a>] ip6_finish_output2+0x5ca/0xac0 [ipv6]
|
|
[<ffffffffa02fbfc6>] ip6_finish_output+0x126/0x2c0 [ipv6]
|
|
[<ffffffffa02fc245>] ip6_output+0xe5/0x390 [ipv6]
|
|
[<ffffffffa032b92c>] NF_HOOK.constprop.44+0x6c/0x240 [ipv6]
|
|
[<ffffffffa032bd16>] mld_sendpack+0x216/0x3e0 [ipv6]
|
|
[<ffffffffa032d5eb>] mld_ifc_timer_expire+0x18b/0x2b0 [ipv6]
|
|
|
|
This could happen when ip link remove a bridge or destroy a netns with a
|
|
bridge device inside.
|
|
|
|
With Nikolay's suggestion, this patch is to clean up bridge multicast in
|
|
ndo_uninit after bridge dev is shutdown, instead of br_dev_delete, so
|
|
that netif_running check in br_multicast_add_group can avoid this issue.
|
|
|
|
v1->v2:
|
|
- fix this issue by moving br_multicast_dev_del to ndo_uninit, instead
|
|
of calling dev_close in br_dev_delete.
|
|
|
|
(NOTE: Depends upon b6fe0440c637 ("bridge: implement missing ndo_uninit()"))
|
|
|
|
(NOTE: Manually fixed cherry-pick conflict as the code in the vicinity had
|
|
changed. The logic of this fix should be preserved)
|
|
|
|
Fixes: e10177abf842 ("bridge: multicast: fix handling of temp and perm entries")
|
|
Reported-by: Jianwen Ji <jiji@redhat.com>
|
|
Signed-off-by: Xin Long <lucien.xin@gmail.com>
|
|
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
|
|
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
|
|
Signed-off-by: David S. Miller <davem@davemloft.net>
|
|
Signed-off-by: Rolf Neugebauer <rn@rneugeba.io>
|
|
(cherry picked from commit b1b9d366028ff580e6dd80b48a69c473361456f1)
|
|
---
|
|
net/bridge/br_device.c | 1 +
|
|
net/bridge/br_if.c | 1 -
|
|
2 files changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
|
|
index 15be72678bc8..a0a7bb6a991f 100644
|
|
--- a/net/bridge/br_device.c
|
|
+++ b/net/bridge/br_device.c
|
|
@@ -126,6 +126,7 @@ static void br_dev_uninit(struct net_device *dev)
|
|
{
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
+ br_multicast_dev_del(br);
|
|
br_multicast_uninit_stats(br);
|
|
br_vlan_flush(br);
|
|
free_percpu(br->stats);
|
|
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
|
|
index 1764de88483c..e25b75654256 100644
|
|
--- a/net/bridge/br_if.c
|
|
+++ b/net/bridge/br_if.c
|
|
@@ -311,7 +311,6 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
|
|
|
|
br_fdb_delete_by_port(br, NULL, 0, 1);
|
|
|
|
- br_multicast_dev_del(br);
|
|
del_timer_sync(&br->gc_timer);
|
|
|
|
br_sysfs_delbr(br->dev);
|
|
--
|
|
2.19.2
|
|
|