Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=2dc2f207fb251666d2396fe1a69272b307ecc333
Commit:     2dc2f207fb251666d2396fe1a69272b307ecc333
Parent:     398bcbebb6f721ac308df1e3d658c0029bb74503
Author:     Patrick McHardy <[EMAIL PROTECTED]>
AuthorDate: Sun Jan 20 06:25:48 2008 -0800
Committer:  David S. Miller <[EMAIL PROTECTED]>
CommitDate: Sun Jan 20 20:31:41 2008 -0800

    [NETFILTER]: bridge-netfilter: fix net_device refcnt leaks
    
    When packets are flood-forwarded to multiple output devices, the
    bridge-netfilter code reuses skb->nf_bridge for each clone to store
    the bridge port. When queueing packets using NFQUEUE netfilter takes
    a reference to skb->nf_bridge->physoutdev, which is overwritten
    when the packet is forwarded to the second port. This causes
    refcount unterflows for the first device and refcount leaks for all
    others. Additionally this provides incorrect data to the iptables
    physdev match.
    
    Unshare skb->nf_bridge by copying it if it is shared before assigning
    the physoutdev device.
    
    Reported, tested and based on initial patch by
    Jan Christoph Nordholz <[EMAIL PROTECTED]>.
    
    Signed-off-by: Patrick McHardy <[EMAIL PROTECTED]>
    Signed-off-by: David S. Miller <[EMAIL PROTECTED]>
---
 net/bridge/br_netfilter.c |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 5d8b939..9f78a69 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -142,6 +142,23 @@ static inline struct nf_bridge_info 
*nf_bridge_alloc(struct sk_buff *skb)
        return skb->nf_bridge;
 }
 
+static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb)
+{
+       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+       if (atomic_read(&nf_bridge->use) > 1) {
+               struct nf_bridge_info *tmp = nf_bridge_alloc(skb);
+
+               if (tmp) {
+                       memcpy(tmp, nf_bridge, sizeof(struct nf_bridge_info));
+                       atomic_set(&tmp->use, 1);
+                       nf_bridge_put(nf_bridge);
+               }
+               nf_bridge = tmp;
+       }
+       return nf_bridge;
+}
+
 static inline void nf_bridge_push_encap_header(struct sk_buff *skb)
 {
        unsigned int len = nf_bridge_encap_header_len(skb);
@@ -637,6 +654,11 @@ static unsigned int br_nf_forward_ip(unsigned int hook, 
struct sk_buff *skb,
        if (!skb->nf_bridge)
                return NF_ACCEPT;
 
+       /* Need exclusive nf_bridge_info since we might have multiple
+        * different physoutdevs. */
+       if (!nf_bridge_unshare(skb))
+               return NF_DROP;
+
        parent = bridge_parent(out);
        if (!parent)
                return NF_DROP;
@@ -718,6 +740,11 @@ static unsigned int br_nf_local_out(unsigned int hook, 
struct sk_buff *skb,
        if (!skb->nf_bridge)
                return NF_ACCEPT;
 
+       /* Need exclusive nf_bridge_info since we might have multiple
+        * different physoutdevs. */
+       if (!nf_bridge_unshare(skb))
+               return NF_DROP;
+
        nf_bridge = skb->nf_bridge;
        if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT))
                return NF_ACCEPT;
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to