| From 0fd1dd0d46ff3504baea197a2114ea5233c0d019 Mon Sep 17 00:00:00 2001 |
| From: Herbert Xu <herbert@gondor.apana.org.au> |
| Date: Fri, 11 Feb 2011 12:36:55 +0000 |
| Subject: [PATCH] bridge: Fix mglist corruption that leads to memory corruption |
| |
| commit 6b0d6a9b4296fa16a28d10d416db7a770fc03287 upstream. |
| |
| The list mp->mglist is used to indicate whether a multicast group |
| is active on the bridge interface itself as opposed to one of the |
| constituent interfaces in the bridge. |
| |
| Unfortunately the operation that adds the mp->mglist node to the |
| list neglected to check whether it has already been added. This |
| leads to list corruption in the form of nodes pointing to itself. |
| |
| Normally this would be quite obvious as it would cause an infinite |
| loop when walking the list. However, as this list is never actually |
| walked (which means that we don't really need it, I'll get rid of |
| it in a subsequent patch), this instead is hidden until we perform |
| a delete operation on the affected nodes. |
| |
| As the same node may now be pointed to by more than one node, the |
| delete operations can then cause modification of freed memory. |
| |
| This was observed in practice to cause corruption in 512-byte slabs, |
| most commonly leading to crashes in jbd2. |
| |
| Thanks to Josef Bacik for pointing me in the right direction. |
| |
| Reported-by: Ian Page Hands <ihands@redhat.com> |
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| --- |
| net/bridge/br_multicast.c | 3 ++- |
| 1 file changed, 2 insertions(+), 1 deletion(-) |
| |
| diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c |
| index eaa0e1bae49b..ea4452f3dacb 100644 |
| --- a/net/bridge/br_multicast.c |
| +++ b/net/bridge/br_multicast.c |
| @@ -532,7 +532,8 @@ static int br_multicast_add_group(struct net_bridge *br, |
| goto err; |
| |
| if (!port) { |
| - hlist_add_head(&mp->mglist, &br->mglist); |
| + if (hlist_unhashed(&mp->mglist)) |
| + hlist_add_head(&mp->mglist, &br->mglist); |
| mod_timer(&mp->timer, now + br->multicast_membership_interval); |
| goto out; |
| } |
| -- |
| 1.8.5.2 |
| |