aboutsummaryrefslogtreecommitdiffstats
path: root/main/linux-grsec/0012-xfrm-remove-policy-lock-when-accessing-policy-walk.d.patch
diff options
context:
space:
mode:
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.patch105
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
+