[PATCH] [RFC] SELINUX: Remove obsolete deferred inode security init list.

Paul Moore paul at paul-moore.com
Mon Nov 14 17:45:49 UTC 2022


On Mon, Nov 14, 2022 at 6:19 AM Konstantin Meskhidze
<konstantin.meskhidze at huawei.com> wrote:
> From: Alexander Kozhevnikov <alexander.kozhevnikov at huawei-partners.com>
>
> This patch is a proposed code optimization for SELinux:
>
> 1) Each inode has SELinux security structure attached
>    to it, this one need to be initialized at some point.
> 2) This initialization is done by the function
>    inode_doinit_with_dentry ( ).
> 3) In the kernel releases started from some point in the past
>    this function (2) is always called normally from function
>    __inode_security_revalidate ( ).
> 4) Which in turn is always called  from inode_security ( ), which
>    is a base point for any selinux calls and always called on
>    any access to any inode except a few special cases when
>    _inode_security_novalidate ( ) is used.
> 5) Inode security structure initialization can be done only after
>    SELinux is fully initialized and policy is loaded.
> 6) So, for this purpose there was a special defeferred inode security
>    initialization list protected by a spinlock implemented, which was
>    populated instead of isec initialization in function
>    inode_doinit_with_dentry ( ), if it was called before SELinux full
>    initialization, and processed at the time when SELinux policy load
>    occurred by calling again inode_doinit_with_dentry ( ) on each inode
>    in this list.
> 7) This list was a part of a default initialization logic before (3) was
>    implemented, but now, taking into account new mechanism implemented
>    with current approach of inode security revalidation on each access
>    (4)-(3)-(2), it looks obsolete and not needed anymore.
> 8) So deferred initialization, this list and code associated with it can
>    be safely removed now, as anyway, if inode isec was not initialized
>    before it will be processed on any next inode access.
> 9) There are two possible positive consequences from this removal:
>      a. More clean and simple code, less memory consumption;
>      b. This deferred initialization in some cases (for example SELinux
>         was switched on manually after system was up quite a long time)
>         could take some significant time to process, i.e. system looks
>         hung for some notable time. And now this is avoided.
>
> Signed-off-by: Alexander Kozhevnikov <alexander.kozhevnikov at huawei-partners.com>
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze at huawei.com>
> ---
>  security/selinux/hooks.c          | 70 ++++---------------------------
>  security/selinux/include/objsec.h |  3 --
>  2 files changed, 7 insertions(+), 66 deletions(-)

Hi Konstantin, Alexander,

A few comments below, but can you share what testing you've done with
this?  Specifically what you've done to ensure that inodes allocated
before the policy is loaded are properly initialized/validated after
the policy is loaded?

> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index f553c370397e..c93b5621d735 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -316,27 +316,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr
>
>  static void inode_free_security(struct inode *inode)
>  {
> -       struct inode_security_struct *isec = selinux_inode(inode);
> -       struct superblock_security_struct *sbsec;
> -
> -       if (!isec)
> -               return;
> -       sbsec = selinux_superblock(inode->i_sb);
> -       /*
> -        * As not all inode security structures are in a list, we check for
> -        * empty list outside of the lock to make sure that we won't waste
> -        * time taking a lock doing nothing.
> -        *
> -        * The list_del_init() function can be safely called more than once.
> -        * It should not be possible for this function to be called with
> -        * concurrent list_add(), but for better safety against future changes
> -        * in the code, we use list_empty_careful() here.
> -        */
> -       if (!list_empty_careful(&isec->list)) {
> -               spin_lock(&sbsec->isec_lock);
> -               list_del_init(&isec->list);
> -               spin_unlock(&sbsec->isec_lock);
> -       }
> +/* NOTHING TO DO AFTER DEFERRED LIST REMOVAL */
>  }

We should just remove inode_free_security(), as well as
selinux_inode_free_security(), there is no reason to leave them as
empty functions and/or hooks.

> @@ -551,27 +531,6 @@ static int sb_finish_set_opts(struct super_block *sb)
>         /* Initialize the root inode. */
>         rc = inode_doinit_with_dentry(root_inode, root);
>
> -       /* Initialize any other inodes associated with the superblock, e.g.
> -          inodes created prior to initial policy load or inodes created
> -          during get_sb by a pseudo filesystem that directly
> -          populates itself. */
> -       spin_lock(&sbsec->isec_lock);
> -       while (!list_empty(&sbsec->isec_head)) {
> -               struct inode_security_struct *isec =
> -                               list_first_entry(&sbsec->isec_head,
> -                                          struct inode_security_struct, list);
> -               struct inode *inode = isec->inode;
> -               list_del_init(&isec->list);
> -               spin_unlock(&sbsec->isec_lock);
> -               inode = igrab(inode);
> -               if (inode) {
> -                       if (!IS_PRIVATE(inode))
> -                               inode_doinit_with_dentry(inode, NULL);
> -                       iput(inode);
> -               }
> -               spin_lock(&sbsec->isec_lock);
> -       }
> -       spin_unlock(&sbsec->isec_lock);
>         return rc;
>  }

I would suggest ending sb_finish_set_opts() by returning from the
inode_doinit_with_dentry() call, e.g.:

    /* ... */
    return inode_doinit_with_dentry(root_inode, root);
  }

> @@ -1430,9 +1381,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
>                 if (!dentry) {
>                         /*
>                          * this is can be hit on boot when a file is accessed
> -                        * before the policy is loaded.  When we load policy we
> -                        * may find inodes that have no dentry on the
> -                        * sbsec->isec_head list.  No reason to complain as these
> +                        * before the policy is loaded. No reason to complain as these
>                          * will get fixed up the next time we go through
>                          * inode_doinit with a dentry, before these inodes could
>                          * be used again by userspace.

There are some typos at the start of this comment that are worth
fixing here since you are updating the comment block, e.g.:

  /*
   * This can be hit on boot when a file is accessed
   * before the policy is loaded ...

-- 
paul-moore.com



More information about the Linux-security-module-archive mailing list