From 995110143880fd9cb255fa5df05f8950c56fb43a Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 19 Feb 2006 22:11:50 -0800 Subject: [XFRM]: Fix policy double put The policy is put once immediately and once at the error label, which results in the following Oops: kernel BUG at net/xfrm/xfrm_policy.c:250! invalid opcode: 0000 [#2] PREEMPT [...] CPU: 0 EIP: 0060:[] Not tainted VLI EFLAGS: 00210246 (2.6.16-rc3 #39) EIP is at __xfrm_policy_destroy+0xf/0x46 eax: d49f2000 ebx: d49f2000 ecx: f74bd880 edx: f74bd280 esi: d49f2000 edi: 00000001 ebp: cd506dcc esp: cd506dc8 ds: 007b es: 007b ss: 0068 Process ssh (pid: 31970, threadinfo=cd506000 task=cfb04a70) Stack: <0>cd506000 cd506e34 c028e92b ebde7280 cd506e58 cd506ec0 f74bd280 00000000 00000214 0000000a 0000000a 00000000 00000002 f7ae6000 00000000 cd506e58 cd506e14 c0299e36 f74bd280 e873fe00 c02943fd cd506ec0 ebde7280 f271f440 Call Trace: [] show_stack_log_lvl+0xaa/0xb5 [] show_registers+0x126/0x18c [] die+0x14e/0x1db [] do_trap+0x7c/0x96 [] do_invalid_op+0x89/0x93 [] error_code+0x4f/0x54 [] xfrm_lookup+0x349/0x3c2 [] ip6_datagram_connect+0x317/0x452 [] inet_dgram_connect+0x49/0x54 [] sys_connect+0x51/0x68 [] sys_socketcall+0x6f/0x166 [] syscall_call+0x7/0xb Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/xfrm/xfrm_policy.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net/xfrm') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 98ec53bd3a..5e6b05ac12 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -885,8 +885,6 @@ restart: * We can't enlist stable bundles either. */ write_unlock_bh(&policy->lock); - - xfrm_pol_put(policy); if (dst) dst_free(dst); -- cgit v1.2.2 From 42cf93cd464e0df3c85d298c647411bae6d99e6e Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 21 Feb 2006 13:37:35 -0800 Subject: [NETFILTER]: Fix bridge netfilter related in xfrm_lookup The bridge-netfilter code attaches a fake dst_entry with dst->ops == NULL to purely bridged packets. When these packets are SNATed and a policy lookup is done, xfrm_lookup crashes because it tries to dereference dst->ops. Change xfrm_lookup not to dereference dst->ops before checking for the DST_NOXFRM flag and set this flag in the fake dst_entry. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/xfrm/xfrm_policy.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net/xfrm') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 5e6b05ac12..8206025d8e 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -782,7 +782,7 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, int nx = 0; int err; u32 genid; - u16 family = dst_orig->ops->family; + u16 family; u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT); u32 sk_sid = security_sk_sid(sk, fl, dir); restart: @@ -796,13 +796,14 @@ restart: if ((dst_orig->flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT]) return 0; - policy = flow_cache_lookup(fl, sk_sid, family, dir, - xfrm_policy_lookup); + policy = flow_cache_lookup(fl, sk_sid, dst_orig->ops->family, + dir, xfrm_policy_lookup); } if (!policy) return 0; + family = dst_orig->ops->family; policy->curlft.use_time = (unsigned long)xtime.tv_sec; switch (policy->action) { -- cgit v1.2.2 From 21380b81ef8699179b535e197a95b891a7badac7 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 22 Feb 2006 14:47:13 -0800 Subject: [XFRM]: Eliminate refcounting confusion by creating __xfrm_state_put(). We often just do an atomic_dec(&x->refcnt) on an xfrm_state object because we know there is more than 1 reference remaining and thus we can elide the heavier xfrm_state_put() call. Do this behind an inline function called __xfrm_state_put() so that is more obvious and also to allow us to more cleanly add refcount debugging later. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- net/xfrm/xfrm_state.c | 8 ++++---- net/xfrm/xfrm_user.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net/xfrm') diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index e12d0be5f9..c656cbaf35 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -220,14 +220,14 @@ static int __xfrm_state_delete(struct xfrm_state *x) x->km.state = XFRM_STATE_DEAD; spin_lock(&xfrm_state_lock); list_del(&x->bydst); - atomic_dec(&x->refcnt); + __xfrm_state_put(x); if (x->id.spi) { list_del(&x->byspi); - atomic_dec(&x->refcnt); + __xfrm_state_put(x); } spin_unlock(&xfrm_state_lock); if (del_timer(&x->timer)) - atomic_dec(&x->refcnt); + __xfrm_state_put(x); /* The number two in this test is the reference * mentioned in the comment below plus the reference @@ -243,7 +243,7 @@ static int __xfrm_state_delete(struct xfrm_state *x) * The xfrm_state_alloc call gives a reference, and that * is what we are dropping here. */ - atomic_dec(&x->refcnt); + __xfrm_state_put(x); err = 0; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index ac87a09ba8..7de1755924 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -345,7 +345,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) if (err < 0) { x->km.state = XFRM_STATE_DEAD; - xfrm_state_put(x); + __xfrm_state_put(x); goto out; } -- cgit v1.2.2