diff options
Diffstat (limited to 'main/linux-grsec/0012-xfrm-remove-policy-lock-when-accessing-policy-walk.d.patch')
-rw-r--r-- | main/linux-grsec/0012-xfrm-remove-policy-lock-when-accessing-policy-walk.d.patch | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/main/linux-grsec/0012-xfrm-remove-policy-lock-when-accessing-policy-walk.d.patch b/main/linux-grsec/0012-xfrm-remove-policy-lock-when-accessing-policy-walk.d.patch new file mode 100644 index 0000000000..222caaddfd --- /dev/null +++ b/main/linux-grsec/0012-xfrm-remove-policy-lock-when-accessing-policy-walk.d.patch @@ -0,0 +1,105 @@ +From 7a400eb025dd53883c3560d0fdb069542f7ad3db Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi> +Date: Wed, 31 Mar 2010 00:17:05 +0000 +Subject: [PATCH 12/18] xfrm: remove policy lock when accessing policy->walk.dead + +All of the code considers ->dead as a hint that the cached policy +needs to get refreshed. The read side can just drop the read lock +without any side effects. + +The write side needs to make sure that it's written only exactly +once. Only possible race is at xfrm_policy_kill(). This is fixed +by checking result of __xfrm_policy_unlink() when needed. It will +always succeed if the policy object is looked up from the hash +list (so some checks are removed), but it needs to be checked if +we are trying to unlink policy via a reference (appropriate +checks added). + +Since policy->walk.dead is written exactly once, it no longer +needs to be protected with a write lock. + +Signed-off-by: Timo Teras <timo.teras@iki.fi> +Acked-by: Herbert Xu <herbert@gondor.apana.org.au> +Signed-off-by: David S. Miller <davem@davemloft.net> +(backported from commit ea2dea9dacc256fe927857feb423872051642ae7) +--- + net/xfrm/xfrm_policy.c | 20 +++++--------------- + net/xfrm/xfrm_user.c | 6 +----- + 2 files changed, 6 insertions(+), 20 deletions(-) + +diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c +index d75047c..110184f 100644 +--- a/net/xfrm/xfrm_policy.c ++++ b/net/xfrm/xfrm_policy.c +@@ -156,7 +156,7 @@ static void xfrm_policy_timer(unsigned long data) + + read_lock(&xp->lock); + +- if (xp->walk.dead) ++ if (unlikely(xp->walk.dead)) + goto out; + + dir = xfrm_policy_id2dir(xp->index); +@@ -297,17 +297,7 @@ static DECLARE_WORK(xfrm_policy_gc_work, xfrm_policy_gc_task); + + static void xfrm_policy_kill(struct xfrm_policy *policy) + { +- int dead; +- +- write_lock_bh(&policy->lock); +- dead = policy->walk.dead; + policy->walk.dead = 1; +- write_unlock_bh(&policy->lock); +- +- if (unlikely(dead)) { +- WARN_ON(1); +- return; +- } + + spin_lock_bh(&xfrm_policy_gc_lock); + hlist_add_head(&policy->bydst, &xfrm_policy_gc_list); +@@ -1115,6 +1105,9 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) + __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir); + } + if (old_pol) ++ /* Unlinking succeeds always. This is the only function ++ * allowed to delete or replace socket policy. ++ */ + __xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir); + write_unlock_bh(&xfrm_policy_lock); + +@@ -1705,11 +1698,8 @@ restart: + goto error; + } + +- for (pi = 0; pi < npols; pi++) { +- read_lock_bh(&pols[pi]->lock); ++ for (pi = 0; pi < npols; pi++) + pol_dead |= pols[pi]->walk.dead; +- read_unlock_bh(&pols[pi]->lock); +- } + + write_lock_bh(&policy->lock); + if (unlikely(pol_dead || stale_bundle(dst))) { +diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c +index d1e9ee3..f9c56e9 100644 +--- a/net/xfrm/xfrm_user.c ++++ b/net/xfrm/xfrm_user.c +@@ -1617,13 +1617,9 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, + if (xp == NULL) + return -ENOENT; + +- read_lock(&xp->lock); +- if (xp->walk.dead) { +- read_unlock(&xp->lock); ++ if (unlikely(xp->walk.dead)) + goto out; +- } + +- read_unlock(&xp->lock); + err = 0; + if (up->hard) { + uid_t loginuid = NETLINK_CB(skb).loginuid; +-- +1.7.0.2 + |