Re: [RFC PATCH v4 1/2] selinux: encapsulate policy state, refactor policy load

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 8/7/20 5:41 AM, Paul Moore wrote:
> On Wed, Aug 5, 2020 at 11:52 AM Stephen Smalley
> <stephen.smalley.work@xxxxxxxxx> wrote:
>> Encapsulate the policy state in its own structure (struct
>> selinux_policy) that is separately allocated but referenced from the
>> selinux_ss structure.  The policy state includes the SID table
>> (particularly the context structures), the policy database, and the
>> mapping between the kernel classes/permissions and the policy values.
>> Refactor the security server portion of the policy load logic to
>> cleanly separate loading of the new structures from committing the new
>> policy.  Unify the initial policy load and reload code paths as much
>> as possible, avoiding duplicated code.  Make sure we are taking the
>> policy read-lock prior to any dereferencing of the policy.  Move the
>> copying of the policy capability booleans into the state structure
>> outside of the policy write-lock because they are separate from the
>> policy and are read outside of any policy lock; possibly they should
>> be using at least READ_ONCE/WRITE_ONCE or smp_load_acquire/store_release.
>> Restore the load mutex that was previously removed by
>> commit 89abd0acf033 ("SELinux: drop load_mutex in security_load_policy")
>> to make explicit the exclusion even though it is currently redundant
>> with the fsi->mutex held by selinuxfs; this makes clear that we do
>> not need to take the policy read-lock across sidtab_convert() and will
>> be useful in the future for lockdep checking.
>>
>> These changes simplify the policy loading logic, reduce the size of
>> the critical section while holding the policy write-lock, and should
>> facilitate future changes to e.g. refactor the entire policy reload
>> logic including the selinuxfs code to make the updating of the policy
>> and the selinuxfs directory tree atomic and/or to convert the policy
>> read-write lock to RCU.
>>
>> Signed-off-by: Stephen Smalley <stephen.smalley.work@xxxxxxxxx>
>> ---
>> v4 does not take the policy read-lock across sidtab_convert() and
>> therefore does not require changing allocations by it to be atomic
>> or dropping the cond_resched() call.  To make obvious that taking
>> the policy read-lock is not necessary in security_load_policy(), restore
>> the load mutex to security_load_policy() that was removed back in
>> commit 89abd0acf033 ("SELinux: drop load_mutex in security_load_policy").
>> However, since we have refactored security_load_policy() in this change
>> to split out selinux_policy_commit(), we need to take the mutex in
>> security_load_policy() and release it in selinux_policy_commit().
> I'm not in love with the idea of splitting the lock/unlock across
> different functions, more below in the relevant code section.
>
>>  security/selinux/ss/services.c | 403 +++++++++++++++++----------------
>>  security/selinux/ss/services.h |  11 +-
>>  2 files changed, 220 insertions(+), 194 deletions(-)
> ...
>
>
>> diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
>> index 9e76a80db6e1..6dea93fac9e2 100644
>> --- a/security/selinux/ss/services.c
>> +++ b/security/selinux/ss/services.c
>> @@ -2115,11 +2126,73 @@ static void security_load_policycaps(struct selinux_state *state)
>>                         pr_info("SELinux:  unknown policy capability %u\n",
>>                                 i);
>>         }
>> +
>> +       read_unlock(&state->ss->policy_rwlock);
>>  }
>>
>>  static int security_preserve_bools(struct selinux_state *state,
>>                                    struct policydb *newpolicydb);
>>
>> +static void selinux_policy_free(struct selinux_policy *policy)
>> +{
>> +       if (!policy)
>> +               return;
>> +
>> +       policydb_destroy(&policy->policydb);
>> +       sidtab_destroy(&policy->sidtab);
>> +       kfree(policy->map.mapping);
>> +       kfree(policy);
>> +}
>> +
>> +static void selinux_policy_commit(struct selinux_state *state,
>> +                               struct selinux_policy *newpolicy)
>> +{
>> +       struct selinux_policy *oldpolicy;
>> +       u32 seqno;
>> +
>> +       lockdep_assert_held(&state->ss->load_mutex);
>> +
>> +       /* If switching between different policy types, log MLS status */
>> +       oldpolicy = state->ss->policy;
>> +       if (oldpolicy) {
>> +               if (oldpolicy->policydb.mls_enabled && !newpolicy->policydb.mls_enabled)
>> +                       pr_info("SELinux: Disabling MLS support...\n");
>> +               else if (!oldpolicy->policydb.mls_enabled && newpolicy->policydb.mls_enabled)
>> +                       pr_info("SELinux: Enabling MLS support...\n");
>> +       }
>> +
>> +       /* Install the new policy. */
>> +       write_lock_irq(&state->ss->policy_rwlock);
>> +       state->ss->policy = newpolicy;
>> +       seqno = ++state->ss->latest_granting;
>> +       write_unlock_irq(&state->ss->policy_rwlock);
>> +
>> +       /* Load the policycaps from the new policy */
>> +       security_load_policycaps(state);
>> +
>> +       if (!selinux_initialized(state)) {
>> +               /*
>> +                * After first policy load, the security server is
>> +                * marked as initialized and ready to handle requests and
>> +                * any objects created prior to policy load are then labeled.
>> +                */
>> +               selinux_mark_initialized(state);
>> +               mutex_unlock(&state->ss->load_mutex);
>> +               selinux_complete_init();
>> +       } else
>> +               mutex_unlock(&state->ss->load_mutex);
>> +
>> +       /* Free the old policy */
>> +       selinux_policy_free(oldpolicy);
>> +
>> +       /* Flush external caches and notify userspace of policy load */
>> +       avc_ss_reset(state->avc, seqno);
>> +       selnl_notify_policyload(seqno);
>> +       selinux_status_update_policyload(state, seqno);
>> +       selinux_netlbl_cache_invalidate();
>> +       selinux_xfrm_notify_policyload();
>> +}
> I can somewhat understand if you don't want to have all the old policy
> cleanup, reset, and notify code in security_load_policy(), but I
> really dislike that the mutex lock/unlock is split across the two
> functions.
>
> What if selinux_policy_commit() returned oldpolicy on success and we
> created a new function, selinux_policy_retire() (name?), that would be
> called from security_load_policy and could handle the cleanup, reset,
> and notify code.  The mutex unlock could happen between the calls to
> selinux_policy_commit() and selinux_policy_retire()?
>
> I'm open to other ideas as well.
>
Something similar to that will most likely be needed for a move to rcu lock anyway.





[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux