Subject: [XFRM]: Fix leak of expired xfrm_states

From: Patrick McHardy <[email-masked]>
Date: Mon, 26 Nov 2007 16:05:27 +0100

This patch fixes a xfrm_state leak, which appears to be a
regression from the reference count simplifications.

commit 817252c2a475371f9764883c7d0f0cde63b3cfe8
Author: Patrick McHardy <[email-masked]>
Date:   Mon Nov 26 16:00:50 2007 +0100

    [XFRM]: Fix leak of expired xfrm_states
    
    The xfrm_timer calls __xfrm_state_delete, which drops the final reference
    manually without triggering destruction of the state.
    
    Signed-off-by: Patrick McHardy <[email-masked]>

diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 224b44e..11e9a48 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -416,7 +416,7 @@ static inline unsigned long make_jiffies(long secs)
 
 static void xfrm_timer_handler(unsigned long data)
 {
-	struct xfrm_state *x = (struct xfrm_state*)data;
+	struct xfrm_state *x = (struct xfrm_state*)data, *del = NULL;
 	unsigned long now = get_seconds();
 	long next = LONG_MAX;
 	int warn = 0;
@@ -479,6 +479,8 @@ expired:
 		goto resched;
 	}
 
+	del = x;
+	xfrm_state_hold(del);
 	err = __xfrm_state_delete(x);
 	if (!err && x->id.spi)
 		km_state_expired(x, 1, 0);
@@ -488,6 +490,8 @@ expired:
 
 out:
 	spin_unlock(&x->lock);
+	if (del)
+		xfrm_state_put(del);
 }
 
 static void xfrm_replay_timer_handler(unsigned long data);