Commit e7d8f6cb authored by Steffen Klassert's avatar Steffen Klassert

xfrm: Add refcount handling to queued policies

We need to ensure that policies can't go away as long as the hold timer
is armed, so take a refcont when we arm the timer and drop one if we
delete it.

Bug was introduced with git commit a0073fe1 ("xfrm: Add a state
resolution packet queue")
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
parent cd808fc9
...@@ -334,7 +334,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy) ...@@ -334,7 +334,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
atomic_inc(&policy->genid); atomic_inc(&policy->genid);
del_timer(&policy->polq.hold_timer); if (del_timer(&policy->polq.hold_timer))
xfrm_pol_put(policy);
xfrm_queue_purge(&policy->polq.hold_queue); xfrm_queue_purge(&policy->polq.hold_queue);
if (del_timer(&policy->timer)) if (del_timer(&policy->timer))
...@@ -589,7 +590,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old, ...@@ -589,7 +590,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,
spin_lock_bh(&pq->hold_queue.lock); spin_lock_bh(&pq->hold_queue.lock);
skb_queue_splice_init(&pq->hold_queue, &list); skb_queue_splice_init(&pq->hold_queue, &list);
del_timer(&pq->hold_timer); if (del_timer(&pq->hold_timer))
xfrm_pol_put(old);
spin_unlock_bh(&pq->hold_queue.lock); spin_unlock_bh(&pq->hold_queue.lock);
if (skb_queue_empty(&list)) if (skb_queue_empty(&list))
...@@ -600,7 +602,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old, ...@@ -600,7 +602,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,
spin_lock_bh(&pq->hold_queue.lock); spin_lock_bh(&pq->hold_queue.lock);
skb_queue_splice(&list, &pq->hold_queue); skb_queue_splice(&list, &pq->hold_queue);
pq->timeout = XFRM_QUEUE_TMO_MIN; pq->timeout = XFRM_QUEUE_TMO_MIN;
mod_timer(&pq->hold_timer, jiffies); if (!mod_timer(&pq->hold_timer, jiffies))
xfrm_pol_hold(new);
spin_unlock_bh(&pq->hold_queue.lock); spin_unlock_bh(&pq->hold_queue.lock);
} }
...@@ -1787,8 +1790,9 @@ static void xfrm_policy_queue_process(unsigned long arg) ...@@ -1787,8 +1790,9 @@ static void xfrm_policy_queue_process(unsigned long arg)
goto purge_queue; goto purge_queue;
pq->timeout = pq->timeout << 1; pq->timeout = pq->timeout << 1;
mod_timer(&pq->hold_timer, jiffies + pq->timeout); if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout))
return; xfrm_pol_hold(pol);
goto out;
} }
dst_release(dst); dst_release(dst);
...@@ -1819,11 +1823,14 @@ static void xfrm_policy_queue_process(unsigned long arg) ...@@ -1819,11 +1823,14 @@ static void xfrm_policy_queue_process(unsigned long arg)
err = dst_output(skb); err = dst_output(skb);
} }
out:
xfrm_pol_put(pol);
return; return;
purge_queue: purge_queue:
pq->timeout = 0; pq->timeout = 0;
xfrm_queue_purge(&pq->hold_queue); xfrm_queue_purge(&pq->hold_queue);
xfrm_pol_put(pol);
} }
static int xdst_queue_output(struct sk_buff *skb) static int xdst_queue_output(struct sk_buff *skb)
...@@ -1831,7 +1838,8 @@ static int xdst_queue_output(struct sk_buff *skb) ...@@ -1831,7 +1838,8 @@ static int xdst_queue_output(struct sk_buff *skb)
unsigned long sched_next; unsigned long sched_next;
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
struct xfrm_dst *xdst = (struct xfrm_dst *) dst; struct xfrm_dst *xdst = (struct xfrm_dst *) dst;
struct xfrm_policy_queue *pq = &xdst->pols[0]->polq; struct xfrm_policy *pol = xdst->pols[0];
struct xfrm_policy_queue *pq = &pol->polq;
if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) { if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) {
kfree_skb(skb); kfree_skb(skb);
...@@ -1850,10 +1858,12 @@ static int xdst_queue_output(struct sk_buff *skb) ...@@ -1850,10 +1858,12 @@ static int xdst_queue_output(struct sk_buff *skb)
if (del_timer(&pq->hold_timer)) { if (del_timer(&pq->hold_timer)) {
if (time_before(pq->hold_timer.expires, sched_next)) if (time_before(pq->hold_timer.expires, sched_next))
sched_next = pq->hold_timer.expires; sched_next = pq->hold_timer.expires;
xfrm_pol_put(pol);
} }
__skb_queue_tail(&pq->hold_queue, skb); __skb_queue_tail(&pq->hold_queue, skb);
mod_timer(&pq->hold_timer, sched_next); if (!mod_timer(&pq->hold_timer, sched_next))
xfrm_pol_hold(pol);
spin_unlock_bh(&pq->hold_queue.lock); spin_unlock_bh(&pq->hold_queue.lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment