diff mbox series

[RFC] selinux: Use call_rcu for policydb and booleans

Message ID e9c1967d-170f-86f0-2762-7ca36ea08e40@sony.com (mailing list archive)
State Rejected
Headers show
Series [RFC] selinux: Use call_rcu for policydb and booleans | expand

Commit Message

Peter Enderborg Aug. 19, 2020, 8:32 a.m. UTC
From 8184ea3648b18718fdb460a30dfc7f848b7bc6a2 Mon Sep 17 00:00:00 2001
From: Peter Enderborg <peter.enderborg@sony.com>
Date: Wed, 19 Aug 2020 10:20:28 +0200
Subject: [RFC PATCH] selinux: Use call_rcu for policydb and booleans

This patch adds call_rcu that moves sycronize out
out call path. In the callback we can no call
cond_resched so they have to be remvoed.

Signed-off-by: Peter Enderborg <peter.enderborg@sony.com>
---
 security/selinux/ss/policydb.c |  6 -----
 security/selinux/ss/services.c | 43 ++++++++++++++++++++++++++++++----
 2 files changed, 39 insertions(+), 10 deletions(-)

Comments

Stephen Smalley Aug. 19, 2020, 12:06 p.m. UTC | #1
On 8/19/20 4:32 AM, peter enderborg wrote:

>  From 8184ea3648b18718fdb460a30dfc7f848b7bc6a2 Mon Sep 17 00:00:00 2001
> From: Peter Enderborg <peter.enderborg@sony.com>
> Date: Wed, 19 Aug 2020 10:20:28 +0200
> Subject: [RFC PATCH] selinux: Use call_rcu for policydb and booleans
>
> This patch adds call_rcu that moves sycronize out
> out call path. In the callback we can no call
> cond_resched so they have to be remvoed.

If you look at the first version of my patch, I used call_rcu() but in a 
manner that avoided the need to remove cond_resched() or kvfree() calls 
from the freeing code by having the rcu callback just schedule_work() to 
free it later.  That follows the pattern used for freeing user 
namespaces, for example.  However, in re-reading the RCU documentation, 
my understanding is that one should use synchronize_rcu() followed by 
direct freeing whenever possible and this is possible from both the 
policy load and setting booleans.  Neither of them are very frequent 
operations nor so performance-critical that the cost of 
synchronize_rcu() would be considered unacceptable IMHO.  Thus, I don't 
believe we need to do this.
Peter Enderborg Aug. 19, 2020, 12:06 p.m. UTC | #2
This will might even compile! :-)

From f2d5b2a33c97fef896758becfe62e79aed96352d Mon Sep 17 00:00:00 2001
From: Peter Enderborg <peter.enderborg@sony.com>
Date: Wed, 19 Aug 2020 10:20:28 +0200
Subject: [PATCH] selinux: Use call_rcu for policydb and booleans

This patch adds call_rcu that moves sycronize out

out call path. In the callback we can no call
cond_resched so they have to be remvoed.

Signed-off-by: Peter Enderborg <peter.enderborg@sony.com>
---
 security/selinux/ss/policydb.c |  6 -----
 security/selinux/ss/services.c | 43 ++++++++++++++++++++++++++++++----
 2 files changed, 39 insertions(+), 10 deletions(-)

diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 9fccf417006b..bcf49da4d7b2 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -341,7 +341,6 @@ static int filenametr_destroy(void *key, void *datum, void *p)
                kfree(d);
                d = next;
        } while (unlikely(d));
-       cond_resched();
        return 0;
 }
 
@@ -353,7 +352,6 @@ static int range_tr_destroy(void *key, void *datum, void *p)
        ebitmap_destroy(&rt->level[0].cat);
        ebitmap_destroy(&rt->level[1].cat);
        kfree(datum);
-       cond_resched();
        return 0;
 }
 
@@ -791,7 +789,6 @@ void policydb_destroy(struct policydb *p)
        struct role_allow *ra, *lra = NULL;
 
        for (i = 0; i < SYM_NUM; i++) {
-               cond_resched();
                hashtab_map(&p->symtab[i].table, destroy_f[i], NULL);
                hashtab_destroy(&p->symtab[i].table);
        }
@@ -807,7 +804,6 @@ void policydb_destroy(struct policydb *p)
        avtab_destroy(&p->te_avtab);
 
        for (i = 0; i < OCON_NUM; i++) {
-               cond_resched();
                c = p->ocontexts[i];
                while (c) {
                        ctmp = c;
@@ -819,7 +815,6 @@ void policydb_destroy(struct policydb *p)
 
        g = p->genfs;
        while (g) {
-               cond_resched();
                kfree(g->fstype);
                c = g->head;
                while (c) {
@@ -839,7 +834,6 @@ void policydb_destroy(struct policydb *p)
        hashtab_destroy(&p->role_tr);
 
        for (ra = p->role_allow; ra; ra = ra->next) {
-               cond_resched();
                kfree(lra);
                lra = ra;
        }
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ba9347517e5b..61e8296908df 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2184,11 +2184,29 @@ static void selinux_notify_policy_change(struct selinux_state *state,
        selinux_xfrm_notify_policyload();
 }
 
+struct deprecated_policy {
+       struct selinux_policy *policy;
+       int partial;
+       struct rcu_head rcu;
+};
+
+void policy_reclaim(struct rcu_head *rp)
+{
+       struct deprecated_policy *dep = container_of(rp, struct deprecated_policy, rcu);
+
+       if (dep->partial)
+               selinux_policy_cond_free(dep->policy);
+       else
+               selinux_policy_free(dep->policy);
+       kfree(dep);
+}
+
 void selinux_policy_commit(struct selinux_state *state,
                        struct selinux_policy *newpolicy)
 {
        struct selinux_policy *oldpolicy;
        u32 seqno;
+       struct deprecated_policy *dep;
 
        /*
         * NOTE: We do not need to take the rcu read lock
@@ -2231,8 +2249,16 @@ void selinux_policy_commit(struct selinux_state *state,
        }
 
        /* Free the old policy */
-       synchronize_rcu();
-       selinux_policy_free(oldpolicy);
+       /* if cant alloc we need to it the slow way */
+       dep  = kzalloc(sizeof(struct deprecated_policy), GFP_KERNEL);
+       if (dep) {
+               dep->policy = oldpolicy;
+               dep->partial = 0;
+               call_rcu(&dep->rcu, policy_reclaim);
+       } else {
+               synchronize_rcu();
+               selinux_policy_free(oldpolicy);
+       }
 
        /* Notify others of the policy change */
        selinux_notify_policy_change(state, seqno);
@@ -2956,6 +2982,7 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values)
        struct selinux_policy *newpolicy, *oldpolicy;
        int rc;
        u32 i, seqno = 0;
+       struct deprecated_policy *dep;
 
        if (!selinux_initialized(state))
                return -EINVAL;
@@ -3020,8 +3047,16 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values)
         * that were copied for the new policy, and the oldpolicy
         * structure itself but not what it references.
         */
-       synchronize_rcu();
-       selinux_policy_cond_free(oldpolicy);
+       /* if we can not alloc do it the slow way */
+       dep  = kzalloc(sizeof(struct deprecated_policy), GFP_KERNEL);
+       if (dep) {
+               dep->policy = oldpolicy;
+               dep->partial = 1;
+               call_rcu(&dep->rcu, policy_reclaim);
+       } else {
+               synchronize_rcu();
+               selinux_policy_cond_free(oldpolicy);
+       }
 
        /* Notify others of the policy change */
        selinux_notify_policy_change(state, seqno);
Stephen Smalley Aug. 19, 2020, 12:24 p.m. UTC | #3
On 8/19/20 8:06 AM, peter enderborg wrote:

> This will might even compile! :-)
>
>  From f2d5b2a33c97fef896758becfe62e79aed96352d Mon Sep 17 00:00:00 2001
> From: Peter Enderborg <peter.enderborg@sony.com>
> Date: Wed, 19 Aug 2020 10:20:28 +0200
> Subject: [PATCH] selinux: Use call_rcu for policydb and booleans
>
> This patch adds call_rcu that moves sycronize out
>
> out call path. In the callback we can no call
> cond_resched so they have to be remvoed.
I don't see why this is necessary.  My v1 patch used call_rcu() without 
needing these changes, but I didn't think using call_rcu() was justified 
and switched to synchronize_rcu().  What is lacking in my v2 patch that 
you are trying to fix?  If it is a performance concern around calling 
synchronize_rcu() during security_load_policy() and security_set_bool(), 
I don't think that is significant - those are infrequent operations.
>
> Signed-off-by: Peter Enderborg <peter.enderborg@sony.com>
> ---
>   security/selinux/ss/policydb.c |  6 -----
>   security/selinux/ss/services.c | 43 ++++++++++++++++++++++++++++++----
>   2 files changed, 39 insertions(+), 10 deletions(-)
>
> diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
> index 9fccf417006b..bcf49da4d7b2 100644
> --- a/security/selinux/ss/policydb.c
> +++ b/security/selinux/ss/policydb.c
> @@ -341,7 +341,6 @@ static int filenametr_destroy(void *key, void *datum, void *p)
>                  kfree(d);
>                  d = next;
>          } while (unlikely(d));
> -       cond_resched();
>          return 0;
>   }
>   
> @@ -353,7 +352,6 @@ static int range_tr_destroy(void *key, void *datum, void *p)
>          ebitmap_destroy(&rt->level[0].cat);
>          ebitmap_destroy(&rt->level[1].cat);
>          kfree(datum);
> -       cond_resched();
>          return 0;
>   }
>   
> @@ -791,7 +789,6 @@ void policydb_destroy(struct policydb *p)
>          struct role_allow *ra, *lra = NULL;
>   
>          for (i = 0; i < SYM_NUM; i++) {
> -               cond_resched();
>                  hashtab_map(&p->symtab[i].table, destroy_f[i], NULL);
>                  hashtab_destroy(&p->symtab[i].table);
>          }
> @@ -807,7 +804,6 @@ void policydb_destroy(struct policydb *p)
>          avtab_destroy(&p->te_avtab);
>   
>          for (i = 0; i < OCON_NUM; i++) {
> -               cond_resched();
>                  c = p->ocontexts[i];
>                  while (c) {
>                          ctmp = c;
> @@ -819,7 +815,6 @@ void policydb_destroy(struct policydb *p)
>   
>          g = p->genfs;
>          while (g) {
> -               cond_resched();
>                  kfree(g->fstype);
>                  c = g->head;
>                  while (c) {
> @@ -839,7 +834,6 @@ void policydb_destroy(struct policydb *p)
>          hashtab_destroy(&p->role_tr);
>   
>          for (ra = p->role_allow; ra; ra = ra->next) {
> -               cond_resched();
>                  kfree(lra);
>                  lra = ra;
>          }
> diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
> index ba9347517e5b..61e8296908df 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -2184,11 +2184,29 @@ static void selinux_notify_policy_change(struct selinux_state *state,
>          selinux_xfrm_notify_policyload();
>   }
>   
> +struct deprecated_policy {
> +       struct selinux_policy *policy;
> +       int partial;
> +       struct rcu_head rcu;
> +};
> +
> +void policy_reclaim(struct rcu_head *rp)
> +{
> +       struct deprecated_policy *dep = container_of(rp, struct deprecated_policy, rcu);
> +
> +       if (dep->partial)
> +               selinux_policy_cond_free(dep->policy);
> +       else
> +               selinux_policy_free(dep->policy);
> +       kfree(dep);
> +}
> +
>   void selinux_policy_commit(struct selinux_state *state,
>                          struct selinux_policy *newpolicy)
>   {
>          struct selinux_policy *oldpolicy;
>          u32 seqno;
> +       struct deprecated_policy *dep;
>   
>          /*
>           * NOTE: We do not need to take the rcu read lock
> @@ -2231,8 +2249,16 @@ void selinux_policy_commit(struct selinux_state *state,
>          }
>   
>          /* Free the old policy */
> -       synchronize_rcu();
> -       selinux_policy_free(oldpolicy);
> +       /* if cant alloc we need to it the slow way */
> +       dep  = kzalloc(sizeof(struct deprecated_policy), GFP_KERNEL);
> +       if (dep) {
> +               dep->policy = oldpolicy;
> +               dep->partial = 0;
> +               call_rcu(&dep->rcu, policy_reclaim);
> +       } else {
> +               synchronize_rcu();
> +               selinux_policy_free(oldpolicy);
> +       }
>   
>          /* Notify others of the policy change */
>          selinux_notify_policy_change(state, seqno);
> @@ -2956,6 +2982,7 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values)
>          struct selinux_policy *newpolicy, *oldpolicy;
>          int rc;
>          u32 i, seqno = 0;
> +       struct deprecated_policy *dep;
>   
>          if (!selinux_initialized(state))
>                  return -EINVAL;
> @@ -3020,8 +3047,16 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values)
>           * that were copied for the new policy, and the oldpolicy
>           * structure itself but not what it references.
>           */
> -       synchronize_rcu();
> -       selinux_policy_cond_free(oldpolicy);
> +       /* if we can not alloc do it the slow way */
> +       dep  = kzalloc(sizeof(struct deprecated_policy), GFP_KERNEL);
> +       if (dep) {
> +               dep->policy = oldpolicy;
> +               dep->partial = 1;
> +               call_rcu(&dep->rcu, policy_reclaim);
> +       } else {
> +               synchronize_rcu();
> +               selinux_policy_cond_free(oldpolicy);
> +       }
>   
>          /* Notify others of the policy change */
>          selinux_notify_policy_change(state, seqno);
Paul E. McKenney Aug. 19, 2020, 1:15 p.m. UTC | #4
On Wed, Aug 19, 2020 at 08:06:25AM -0400, Stephen Smalley wrote:
> On 8/19/20 4:32 AM, peter enderborg wrote:
> 
> >  From 8184ea3648b18718fdb460a30dfc7f848b7bc6a2 Mon Sep 17 00:00:00 2001
> > From: Peter Enderborg <peter.enderborg@sony.com>
> > Date: Wed, 19 Aug 2020 10:20:28 +0200
> > Subject: [RFC PATCH] selinux: Use call_rcu for policydb and booleans
> > 
> > This patch adds call_rcu that moves sycronize out
> > out call path. In the callback we can no call
> > cond_resched so they have to be remvoed.
> 
> If you look at the first version of my patch, I used call_rcu() but in a
> manner that avoided the need to remove cond_resched() or kvfree() calls from
> the freeing code by having the rcu callback just schedule_work() to free it
> later.  That follows the pattern used for freeing user namespaces, for
> example.  However, in re-reading the RCU documentation, my understanding is
> that one should use synchronize_rcu() followed by direct freeing whenever
> possible and this is possible from both the policy load and setting
> booleans.  Neither of them are very frequent operations nor so
> performance-critical that the cost of synchronize_rcu() would be considered
> unacceptable IMHO.  Thus, I don't believe we need to do this.

Indeed, synchronize_rcu() avoids things like callback flooding.
There are nevertheless cases where synchronize_rcu() can be a problem,
for example due to its relatively long latency (milliseconds at best).
But if this is an infrequent operation, that should not be an issue.

If there nevertheless is a reason to avoid synchronize_rcu(), then
queue_rcu_work() gets you directly to a sleepable workqueue context
where cond_resched() can be used.

							Thnax, Paul
Peter Enderborg Aug. 19, 2020, 2:32 p.m. UTC | #5
On 8/19/20 2:06 PM, Stephen Smalley wrote:
> On 8/19/20 4:32 AM, peter enderborg wrote:
>
>>  From 8184ea3648b18718fdb460a30dfc7f848b7bc6a2 Mon Sep 17 00:00:00 2001
>> From: Peter Enderborg <peter.enderborg@sony.com>
>> Date: Wed, 19 Aug 2020 10:20:28 +0200
>> Subject: [RFC PATCH] selinux: Use call_rcu for policydb and booleans
>>
>> This patch adds call_rcu that moves sycronize out
>> out call path. In the callback we can no call
>> cond_resched so they have to be remvoed.
>
> If you look at the first version of my patch, I used call_rcu() but in a manner that avoided the need to remove cond_resched() or kvfree() calls from the freeing code by having the rcu callback just schedule_work() to free it later.  That follows the pattern used for freeing user namespaces, for example.  However, in re-reading the RCU documentation, my understanding is that one should use synchronize_rcu() followed by direct freeing whenever possible and this is possible from both the policy load and setting booleans.  Neither of them are very frequent operations nor so performance-critical that the cost of synchronize_rcu() would be considered unacceptable IMHO.  Thus, I don't believe we need to do this.
>
>
Loading policydb should be very rare, and it takes for ever anyway. Booleans I have no idea. It seems to something that turns on-off quickly and there are many of them
so it will be hard to say. However I did a test. Before rcu a boolean cycle is (on my test rig) 15ms, with call_rcu 14ms and with synchronize_rcu 10ms.  Not what I expected.

And there are rcu versions for the kvfree if needed.
Peter Enderborg Aug. 19, 2020, 3:05 p.m. UTC | #6
On 8/19/20 3:15 PM, Paul E. McKenney wrote:
> On Wed, Aug 19, 2020 at 08:06:25AM -0400, Stephen Smalley wrote:
>> On 8/19/20 4:32 AM, peter enderborg wrote:
>>
>>>  From 8184ea3648b18718fdb460a30dfc7f848b7bc6a2 Mon Sep 17 00:00:00 2001
>>> From: Peter Enderborg <peter.enderborg@sony.com>
>>> Date: Wed, 19 Aug 2020 10:20:28 +0200
>>> Subject: [RFC PATCH] selinux: Use call_rcu for policydb and booleans
>>>
>>> This patch adds call_rcu that moves sycronize out
>>> out call path. In the callback we can no call
>>> cond_resched so they have to be remvoed.
>> If you look at the first version of my patch, I used call_rcu() but in a
>> manner that avoided the need to remove cond_resched() or kvfree() calls from
>> the freeing code by having the rcu callback just schedule_work() to free it
>> later.  That follows the pattern used for freeing user namespaces, for
>> example.  However, in re-reading the RCU documentation, my understanding is
>> that one should use synchronize_rcu() followed by direct freeing whenever
>> possible and this is possible from both the policy load and setting
>> booleans.  Neither of them are very frequent operations nor so
>> performance-critical that the cost of synchronize_rcu() would be considered
>> unacceptable IMHO.  Thus, I don't believe we need to do this.
> Indeed, synchronize_rcu() avoids things like callback flooding.
> There are nevertheless cases where synchronize_rcu() can be a problem,
> for example due to its relatively long latency (milliseconds at best).
> But if this is an infrequent operation, that should not be an issue.
>
> If there nevertheless is a reason to avoid synchronize_rcu(), then
> queue_rcu_work() gets you directly to a sleepable workqueue context
> where cond_resched() can be used.
>
> 							Thnax, Paul

From what I have seen in usage of booleans the usage is

switch-state, do-something, switch-back. So if there is anything
security related you want switch time to be fast. But when I measured
it was faster with synchronize_rcu than call_rcu.  booleans  work are
smaller than a total policy load.
Paul E. McKenney Aug. 19, 2020, 3:25 p.m. UTC | #7
On Wed, Aug 19, 2020 at 05:05:28PM +0200, peter enderborg wrote:
> On 8/19/20 3:15 PM, Paul E. McKenney wrote:
> > On Wed, Aug 19, 2020 at 08:06:25AM -0400, Stephen Smalley wrote:
> >> On 8/19/20 4:32 AM, peter enderborg wrote:
> >>
> >>>  From 8184ea3648b18718fdb460a30dfc7f848b7bc6a2 Mon Sep 17 00:00:00 2001
> >>> From: Peter Enderborg <peter.enderborg@sony.com>
> >>> Date: Wed, 19 Aug 2020 10:20:28 +0200
> >>> Subject: [RFC PATCH] selinux: Use call_rcu for policydb and booleans
> >>>
> >>> This patch adds call_rcu that moves sycronize out
> >>> out call path. In the callback we can no call
> >>> cond_resched so they have to be remvoed.
> >> If you look at the first version of my patch, I used call_rcu() but in a
> >> manner that avoided the need to remove cond_resched() or kvfree() calls from
> >> the freeing code by having the rcu callback just schedule_work() to free it
> >> later.  That follows the pattern used for freeing user namespaces, for
> >> example.  However, in re-reading the RCU documentation, my understanding is
> >> that one should use synchronize_rcu() followed by direct freeing whenever
> >> possible and this is possible from both the policy load and setting
> >> booleans.  Neither of them are very frequent operations nor so
> >> performance-critical that the cost of synchronize_rcu() would be considered
> >> unacceptable IMHO.  Thus, I don't believe we need to do this.
> > Indeed, synchronize_rcu() avoids things like callback flooding.
> > There are nevertheless cases where synchronize_rcu() can be a problem,
> > for example due to its relatively long latency (milliseconds at best).
> > But if this is an infrequent operation, that should not be an issue.
> >
> > If there nevertheless is a reason to avoid synchronize_rcu(), then
> > queue_rcu_work() gets you directly to a sleepable workqueue context
> > where cond_resched() can be used.
> 
> From what I have seen in usage of booleans the usage is
> switch-state, do-something, switch-back. So if there is anything
> security related you want switch time to be fast. But when I measured
> it was faster with synchronize_rcu than call_rcu.  booleans  work are
> smaller than a total policy load.

That sounds to me like a good argument for synchronize_rcu(), but I at the
end of the day, I must defer to you guys given your knowledge of this code.

								Thanx, Paul
diff mbox series

Patch

diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 9fccf417006b..bcf49da4d7b2 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -341,7 +341,6 @@  static int filenametr_destroy(void *key, void *datum, void *p)
                kfree(d);
                d = next;
        } while (unlikely(d));
-       cond_resched();
        return 0;
 }
 
@@ -353,7 +352,6 @@  static int range_tr_destroy(void *key, void *datum, void *p)
        ebitmap_destroy(&rt->level[0].cat);
        ebitmap_destroy(&rt->level[1].cat);
        kfree(datum);
-       cond_resched();
        return 0;
 }
 
@@ -791,7 +789,6 @@  void policydb_destroy(struct policydb *p)
        struct role_allow *ra, *lra = NULL;
 
        for (i = 0; i < SYM_NUM; i++) {
-               cond_resched();
                hashtab_map(&p->symtab[i].table, destroy_f[i], NULL);
                hashtab_destroy(&p->symtab[i].table);
        }
@@ -807,7 +804,6 @@  void policydb_destroy(struct policydb *p)
        avtab_destroy(&p->te_avtab);
 
        for (i = 0; i < OCON_NUM; i++) {
-               cond_resched();
                c = p->ocontexts[i];
                while (c) {
                        ctmp = c;
@@ -819,7 +815,6 @@  void policydb_destroy(struct policydb *p)
 
        g = p->genfs;
        while (g) {
-               cond_resched();
                kfree(g->fstype);
                c = g->head;
                while (c) {
@@ -839,7 +834,6 @@  void policydb_destroy(struct policydb *p)
        hashtab_destroy(&p->role_tr);
 
        for (ra = p->role_allow; ra; ra = ra->next) {
-               cond_resched();
                kfree(lra);
                lra = ra;
        }
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ba9347517e5b..11eff3a98ef8 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2184,11 +2184,29 @@  static void selinux_notify_policy_change(struct selinux_state *state,
        selinux_xfrm_notify_policyload();
 }
 
+struct deprecated_policy {
+       struct selinux_policy *policy;
+       int partial;
+       struct rcu_head rcu;
+};
+
+void policy_reclaim(struct rcu_head *rp)
+{
+       struct deprecated_policy *dep = container_of(rp, struct dep_policy, rcu);
+
+       if (dep->partial)
+               selinux_policy_cond_free(dep->policy);
+       else
+               selinux_policy_free(dep->policy);
+       kfree(dep);
+}
+
 void selinux_policy_commit(struct selinux_state *state,
                        struct selinux_policy *newpolicy)
 {
        struct selinux_policy *oldpolicy;
        u32 seqno;
+       struct deprecated_policy *dep;
 
        /*
         * NOTE: We do not need to take the rcu read lock
@@ -2231,8 +2249,16 @@  void selinux_policy_commit(struct selinux_state *state,
        }
 
        /* Free the old policy */
-       synchronize_rcu();
-       selinux_policy_free(oldpolicy);
+       /* if cant alloc we need to it the slow way */
+       dep  = kzalloc(sizeof(struct deprecated_policy), GFP_KERNEL);
+       if (dep) {
+               dep->policy = oldpolicy;
+               dep->partial = 0;
+               call_rcu(&dep->rcu, policy_reclaim);
+       } else {
+               synchronize_rcu();
+               selinux_policy_free(oldpolicy);
+       }
 
        /* Notify others of the policy change */
        selinux_notify_policy_change(state, seqno);
@@ -2956,6 +2982,7 @@  int security_set_bools(struct selinux_state *state, u32 len, int *values)
        struct selinux_policy *newpolicy, *oldpolicy;
        int rc;
        u32 i, seqno = 0;
+       struct deprecated_policy *dep;
 
        if (!selinux_initialized(state))
                return -EINVAL;
@@ -3020,8 +3047,16 @@  int security_set_bools(struct selinux_state *state, u32 len, int *values)
         * that were copied for the new policy, and the oldpolicy
         * structure itself but not what it references.
         */
-       synchronize_rcu();
-       selinux_policy_cond_free(oldpolicy);
+       /* if we can not alloc do it the slow way */
+       dep  = kzalloc(sizeof(struct deprecated_policy), GFP_KERNEL);
+       if (dep) {
+               dep->policy = oldpolicy;
+               dep->partial = 1;
+               call_rcu(&dep->rcu, policy_reclaim);
+       } else {
+               synchronize_rcu();
+               selinux_policy_cond_free(oldpolicy);
+       }
 
        /* Notify others of the policy change */
        selinux_notify_policy_change(state, seqno);