aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2008-01-20 09:25:48 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-20 23:31:41 -0500
commit2dc2f207fb251666d2396fe1a69272b307ecc333 (patch)
treefb18f4f6ac06050cca0c39c20f075285e88d98fa /net
parent398bcbebb6f721ac308df1e3d658c0029bb74503 (diff)
[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 <hesso@pool.math.tu-berlin.de>. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br_netfilter.c27
1 files changed, 27 insertions, 0 deletions
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 5d8b939eded1..9f78a69d6b8b 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)
142 return skb->nf_bridge; 142 return skb->nf_bridge;
143} 143}
144 144
145static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb)
146{
147 struct nf_bridge_info *nf_bridge = skb->nf_bridge;
148
149 if (atomic_read(&nf_bridge->use) > 1) {
150 struct nf_bridge_info *tmp = nf_bridge_alloc(skb);
151
152 if (tmp) {
153 memcpy(tmp, nf_bridge, sizeof(struct nf_bridge_info));
154 atomic_set(&tmp->use, 1);
155 nf_bridge_put(nf_bridge);
156 }
157 nf_bridge = tmp;
158 }
159 return nf_bridge;
160}
161
145static inline void nf_bridge_push_encap_header(struct sk_buff *skb) 162static inline void nf_bridge_push_encap_header(struct sk_buff *skb)
146{ 163{
147 unsigned int len = nf_bridge_encap_header_len(skb); 164 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,
637 if (!skb->nf_bridge) 654 if (!skb->nf_bridge)
638 return NF_ACCEPT; 655 return NF_ACCEPT;
639 656
657 /* Need exclusive nf_bridge_info since we might have multiple
658 * different physoutdevs. */
659 if (!nf_bridge_unshare(skb))
660 return NF_DROP;
661
640 parent = bridge_parent(out); 662 parent = bridge_parent(out);
641 if (!parent) 663 if (!parent)
642 return NF_DROP; 664 return NF_DROP;
@@ -718,6 +740,11 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff *skb,
718 if (!skb->nf_bridge) 740 if (!skb->nf_bridge)
719 return NF_ACCEPT; 741 return NF_ACCEPT;
720 742
743 /* Need exclusive nf_bridge_info since we might have multiple
744 * different physoutdevs. */
745 if (!nf_bridge_unshare(skb))
746 return NF_DROP;
747
721 nf_bridge = skb->nf_bridge; 748 nf_bridge = skb->nf_bridge;
722 if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT)) 749 if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT))
723 return NF_ACCEPT; 750 return NF_ACCEPT;