Re: [PATCH v4] selinux: remove unused initial SIDs and improve handling

Stephen Smalley <[email protected]> Fri, 31 Oct 2025 08:54:35 -0400
Newsgroups org.kernel.vger.selinux-refpolicy,org.kernel.vger.selinux
Message-ID <CAEjxPJ49oApxCnCdkB7fS6G+pYpRmnnLxro5uZ2WkObAox-aqA@mail.gmail.com>
On Thu, Oct 30, 2025 at 12:37 PM Stephen Smalley
<[email protected]> wrote:
>
> On Mon, Feb 24, 2020 at 8:55 PM Paul Moore <[email protected]> wrote:
> >
> > FWD'ing this to the mailing list while Stephen is having problems posting.
> >
> > ---------- Forwarded message ---------
> > From: Stephen Smalley <[email protected]>
> > Date: Mon, Feb 24, 2020 at 11:09 AM
> > Subject: [PATCH v4] selinux: remove unused initial SIDs and improve handling
> > To: <[email protected]>
> > Cc: <[email protected]>, <[email protected]>, <[email protected]>,
> > Stephen Smalley <[email protected]>
> >
> >
> > Remove initial SIDs that have never been used or are no longer used by
> > the kernel from its string table, which is also used to generate the
> > SECINITSID_* symbols referenced in code.  Update the code to
> > gracefully handle the fact that these can now be NULL. Stop treating
> > it as an error if a policy defines additional initial SIDs unknown to
> > the kernel.  Do not load unused initial SID contexts into the sidtab.
> > Fix the incorrect usage of the name from the ocontext in error
> > messages when loading initial SIDs since these are not presently
> > written to the kernel policy and are therefore always NULL.
> >
> > After this change, it is possible to safely reclaim and reuse some of
> > the unused initial SIDs without compatibility issues.  Specifically,
> > unused initial SIDs that were being assigned the same context as the
> > unlabeled initial SID in policies can be reclaimed and reused for
> > another purpose, with existing policies still treating them as having
> > the unlabeled context and future policies having the option of mapping
> > them to a more specific context.  For example, this could have been
> > used when the infiniband labeling support was introduced to define
> > initial SIDs for the default pkey and endport SIDs similar to the
> > handling of port/netif/node SIDs rather than always using
> > SECINITSID_UNLABELED as the default.
> >
> > The set of safely reclaimable unused initial SIDs across all known
> > policies is igmp_packet (13), icmp_socket (14), tcp_socket (15), kmod
> > (24), policy (25), and scmp_packet (26); these initial SIDs were
> > assigned the same context as unlabeled in all known policies including
> > mls.  If only considering non-mls policies (i.e. assuming that mls
> > users always upgrade policy with their kernels), the set of safely
> > reclaimable unused initial SIDs further includes file_labels (6), init
> > (7), sysctl_modprobe (16), and sysctl_fs (18) through sysctl_dev (23).
> >
> > Adding new initial SIDs beyond SECINITSID_NUM to policy unfortunately
> > became a fatal error in commit 24ed7fdae669 ("selinux: use separate
> > table for initial SID lookup") and even before that it could cause
> > problems on a policy reload (collision between the new initial SID and
> > one allocated at runtime) ever since commit 42596eafdd75 ("selinux:
> > load the initial SIDs upon every policy load") so we cannot safely
> > start adding new initial SIDs to policies beyond SECINITSID_NUM (27)
> > until such a time as all such kernels do not need to be supported and
> > only those that include this commit are relevant. That is not a big
> > deal since we haven't added a new initial SID since 2004 (v2.6.7) and
> > we have plenty of unused ones we can reclaim if we truly need one.
> >
> > If we want to avoid the wasted storage in initial_sid_to_string[]
> > and/or sidtab->isids[] for the unused initial SIDs, we could introduce
> > an indirection between the kernel initial SID values and the policy
> > initial SID values and just map the policy SID values in the ocontexts
> > to the kernel values during policy_load_isids(). Originally I thought
> > we'd do this by preserving the initial SID names in the kernel policy
> > and creating a mapping at load time like we do for the security
> > classes and permissions but that would require a new kernel policy
> > format version and associated changes to libsepol/checkpolicy and I'm
> > not sure it is justified. Simpler approach is just to create a fixed
> > mapping table in the kernel from the existing fixed policy values to
> > the kernel values. Less flexible but probably sufficient.
> >
> > A separate selinux userspace change was applied in
> > https://github.com/SELinuxProject/selinux/commit/8677ce5e8f592950ae6f14cea1b68a20ddc1ac25
> > to enable removal of most of the unused initial SID contexts from
> > policies, but there is no dependency between that change and this one.
> > That change permits removing all of the unused initial SID contexts
> > from policy except for the fs and sysctl SID contexts.  The initial
> > SID declarations themselves would remain in policy to preserve the
> > values of subsequent ones but the contexts can be dropped.  If/when
> > the kernel decides to reuse one of them, future policies can change
> > the name and start assigning a context again without breaking
> > compatibility.
> >
> > Here is how I would envision staging changes to the initial SIDs in a
> > compatible manner after this commit is applied:
> >
> > 1. At any time after this commit is applied, the kernel could choose
> > to reclaim one of the safely reclaimable unused initial SIDs listed
> > above for a new purpose (i.e. replace its NULL entry in the
> > initial_sid_to_string[] table with a new name and start using the
> > newly generated SECINITSID_name symbol in code), and refpolicy could
> > at that time rename its declaration of that initial SID to reflect its
> > new purpose and start assigning it a context going
> > forward. Existing/old policies would map the reclaimed initial SID to
> > the unlabeled context, so that would be the initial default behavior
> > until policies are updated. This doesn't depend on the selinux
> > userspace change; it will work with existing policies and userspace.
> >
> > 2. In 6 months or so we'll have another SELinux userspace release that
> > will include the libsepol/checkpolicy support for omitting unused
> > initial SID contexts.
> >
> > 3. At any time after that release, refpolicy can make that release its
> > minimum build requirement and drop the sid context statements (but not
> > the sid declarations) for all of the unused initial SIDs except for
> > fs and sysctl, which must remain for compatibility on policy
> > reload with old kernels and for compatibility with kernels that were
> > still using SECINITSID_SYSCTL (< 2.6.39). This doesn't depend on this
> > kernel commit; it will work with previous kernels as well.
>
> This never happened AFAICT and I'm not sure if it was even tried.
> Might need/want to retain the sid context statement for the init SID
> too since it was re-activated by a later patch by Ondrej.

I posted a refpolicy patch to remove the unused initial SID contexts
that can be safely removed for any kernel, see:
https://lore.kernel.org/selinux-refpolicy/[email protected]/

This requires libsepol >= 3.1 but should work on any kernel even
without this kernel patch (which was merged for v5.7).

>
> > 4. After N years for some value of N, refpolicy decides that it no
> > longer cares about policy reload compatibility for kernels that
> > predate this kernel commit, and refpolicy drops the fs and sysctl
> > SID contexts from policy too (but retains the declarations).
>
> Checking to see if N == 5 here ;)
>
> > 5. After M years for some value of M, the kernel decides that it no
> > longer cares about compatibility with refpolicies that predate step 4
> > (dropping the fs and sysctl SIDs), and those two SIDs also become
> > safely reclaimable.  This step is optional and need not ever occur unless
> > we decide that the need to reclaim those two SIDs outweighs the
> > compatibility cost.
> >
> > 6. After O years for some value of O, refpolicy decides that it no
> > longer cares about policy load (not just reload) compatibility for
> > kernels that predate this kernel commit, and both kernel and refpolicy
> > can then start adding and using new initial SIDs beyond 27. This does
> > not depend on the previous change (step 5) and can occur independent
> > of it.
> >
> > Fixes: https://github.com/SELinuxProject/selinux-kernel/issues/12
> > Signed-off-by: Stephen Smalley <[email protected]>
> > ---
> > v4 fixes the commit hashes that I cut-and-pasted from the GH issue
> > comments to be the proper length and added the one-line descriptions.
> > Oddly checkpatch.pl didn't catch that originally.
> >
> >  scripts/selinux/genheaders/genheaders.c       | 11 +++-
> >  .../selinux/include/initial_sid_to_string.h   | 57 +++++++++----------
> >  security/selinux/selinuxfs.c                  |  6 +-
> >  security/selinux/ss/policydb.c                | 25 ++++----
> >  security/selinux/ss/services.c                | 26 ++++-----
> >  5 files changed, 66 insertions(+), 59 deletions(-)
> >
> > diff --git a/scripts/selinux/genheaders/genheaders.c
> > b/scripts/selinux/genheaders/genheaders.c
> > index 544ca126a8a8..f355b3e0e968 100644
> > --- a/scripts/selinux/genheaders/genheaders.c
> > +++ b/scripts/selinux/genheaders/genheaders.c
> > @@ -67,8 +67,12 @@ int main(int argc, char *argv[])
> >         }
> >
> >         isids_len = sizeof(initial_sid_to_string) / sizeof (char *);
> > -       for (i = 1; i < isids_len; i++)
> > -               initial_sid_to_string[i] = stoupperx(initial_sid_to_string[i]);
> > +       for (i = 1; i < isids_len; i++) {
> > +               const char *s = initial_sid_to_string[i];
> > +
> > +               if (s)
> > +                       initial_sid_to_string[i] = stoupperx(s);
> > +       }
> >
> >         fprintf(fout, "/* This file is automatically generated.  Do
> > not edit. */\n");
> >         fprintf(fout, "#ifndef _SELINUX_FLASK_H_\n#define
> > _SELINUX_FLASK_H_\n\n");
> > @@ -82,7 +86,8 @@ int main(int argc, char *argv[])
> >
> >         for (i = 1; i < isids_len; i++) {
> >                 const char *s = initial_sid_to_string[i];
> > -               fprintf(fout, "#define SECINITSID_%-39s %2d\n", s, i);
> > +               if (s)
> > +                       fprintf(fout, "#define SECINITSID_%-39s %2d\n", s, i);
> >         }
> >         fprintf(fout, "\n#define SECINITSID_NUM %d\n", i-1);
> >         fprintf(fout, "\nstatic inline bool
> > security_is_socket_class(u16 kern_tclass)\n");
> > diff --git a/security/selinux/include/initial_sid_to_string.h
> > b/security/selinux/include/initial_sid_to_string.h
> > index 4f93f697f71c..5d332aeb8b6c 100644
> > --- a/security/selinux/include/initial_sid_to_string.h
> > +++ b/security/selinux/include/initial_sid_to_string.h
> > @@ -1,34 +1,33 @@
> >  /* SPDX-License-Identifier: GPL-2.0 */
> > -/* This file is automatically generated.  Do not edit. */
> >  static const char *initial_sid_to_string[] =
> >  {
> > -    "null",
> > -    "kernel",
> > -    "security",
> > -    "unlabeled",
> > -    "fs",
> > -    "file",
> > -    "file_labels",
> > -    "init",
> > -    "any_socket",
> > -    "port",
> > -    "netif",
> > -    "netmsg",
> > -    "node",
> > -    "igmp_packet",
> > -    "icmp_socket",
> > -    "tcp_socket",
> > -    "sysctl_modprobe",
> > -    "sysctl",
> > -    "sysctl_fs",
> > -    "sysctl_kernel",
> > -    "sysctl_net",
> > -    "sysctl_net_unix",
> > -    "sysctl_vm",
> > -    "sysctl_dev",
> > -    "kmod",
> > -    "policy",
> > -    "scmp_packet",
> > -    "devnull",
> > +       NULL,
> > +       "kernel",
> > +       "security",
> > +       "unlabeled",
> > +       NULL,
> > +       "file",
> > +       NULL,
> > +       NULL,
> > +       "any_socket",
> > +       "port",
> > +       "netif",
> > +       "netmsg",
> > +       "node",
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       NULL,
> > +       "devnull",
> >  };
> >
> > diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> > index 533ab170ad52..4781314c2510 100644
> > --- a/security/selinux/selinuxfs.c
> > +++ b/security/selinux/selinuxfs.c
> > @@ -1701,7 +1701,11 @@ static int sel_make_initcon_files(struct dentry *dir)
> >         for (i = 1; i <= SECINITSID_NUM; i++) {
> >                 struct inode *inode;
> >                 struct dentry *dentry;
> > -               dentry = d_alloc_name(dir, security_get_initial_sid_context(i));
> > +               const char *s = security_get_initial_sid_context(i);
> > +
> > +               if (!s)
> > +                       continue;
> > +               dentry = d_alloc_name(dir, s);
> >                 if (!dentry)
> >                         return -ENOMEM;
> >
> > diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
> > index 32b3a8acf96f..406fb02d80ae 100644
> > --- a/security/selinux/ss/policydb.c
> > +++ b/security/selinux/ss/policydb.c
> > @@ -867,29 +867,28 @@ int policydb_load_isids(struct policydb *p,
> > struct sidtab *s)
> >
> >         head = p->ocontexts[OCON_ISID];
> >         for (c = head; c; c = c->next) {
> > -               rc = -EINVAL;
> > -               if (!c->context[0].user) {
> > -                       pr_err("SELinux:  SID %s was never defined.\n",
> > -                               c->u.name);
> > -                       sidtab_destroy(s);
> > -                       goto out;
> > -               }
> > -               if (c->sid[0] == SECSID_NULL || c->sid[0] > SECINITSID_NUM) {
> > -                       pr_err("SELinux:  Initial SID %s out of range.\n",
> > -                               c->u.name);
> > +               u32 sid = c->sid[0];
> > +               const char *name = security_get_initial_sid_context(sid);
> > +
> > +               if (sid == SECSID_NULL) {
> > +                       pr_err("SELinux:  SID 0 was assigned a context.\n");
> >                         sidtab_destroy(s);
> >                         goto out;
> >                 }
> > +
> > +               /* Ignore initial SIDs unused by this kernel. */
> > +               if (!name)
> > +                       continue;
> > +
> >                 rc = context_add_hash(p, &c->context[0]);
> >                 if (rc) {
> >                         sidtab_destroy(s);
> >                         goto out;
> >                 }
> > -
> > -               rc = sidtab_set_initial(s, c->sid[0], &c->context[0]);
> > +               rc = sidtab_set_initial(s, sid, &c->context[0]);
> >                 if (rc) {
> >                         pr_err("SELinux:  unable to load initial SID %s.\n",
> > -                               c->u.name);
> > +                              name);
> >                         sidtab_destroy(s);
> >                         goto out;
> >                 }
> > diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
> > index f90e6550eec8..8ad34fd031d1 100644
> > --- a/security/selinux/ss/services.c
> > +++ b/security/selinux/ss/services.c
> > @@ -1322,23 +1322,22 @@ static int security_sid_to_context_core(struct
> > selinux_state *state,
> >         if (!selinux_initialized(state)) {
> >                 if (sid <= SECINITSID_NUM) {
> >                         char *scontextp;
> > +                       const char *s = initial_sid_to_string[sid];
> >
> > -                       *scontext_len = strlen(initial_sid_to_string[sid]) + 1;
> > +                       if (!s)
> > +                               return -EINVAL;
> > +                       *scontext_len = strlen(s) + 1;
> >                         if (!scontext)
> > -                               goto out;
> > -                       scontextp = kmemdup(initial_sid_to_string[sid],
> > -                                           *scontext_len, GFP_ATOMIC);
> > -                       if (!scontextp) {
> > -                               rc = -ENOMEM;
> > -                               goto out;
> > -                       }
> > +                               return 0;
> > +                       scontextp = kmemdup(s, *scontext_len, GFP_ATOMIC);
> > +                       if (!scontextp)
> > +                               return -ENOMEM;
> >                         *scontext = scontextp;
> > -                       goto out;
> > +                       return 0;
> >                 }
> >                 pr_err("SELinux: %s:  called before initial "
> >                        "load_policy on unknown SID %d\n", __func__, sid);
> > -               rc = -EINVAL;
> > -               goto out;
> > +               return -EINVAL;
> >         }
> >         read_lock(&state->ss->policy_rwlock);
> >         policydb = &state->ss->policydb;
> > @@ -1362,7 +1361,6 @@ static int security_sid_to_context_core(struct
> > selinux_state *state,
> >
> >  out_unlock:
> >         read_unlock(&state->ss->policy_rwlock);
> > -out:
> >         return rc;
> >
> >  }
> > @@ -1552,7 +1550,9 @@ static int security_context_to_sid_core(struct
> > selinux_state *state,
> >                 int i;
> >
> >                 for (i = 1; i < SECINITSID_NUM; i++) {
> > -                       if (!strcmp(initial_sid_to_string[i], scontext2)) {
> > +                       const char *s = initial_sid_to_string[i];
> > +
> > +                       if (s && !strcmp(s, scontext2)) {
> >                                 *sid = i;
> >                                 goto out;
> >                         }
> > --
> > 2.24.1
> >
> >
> >
> > --
> > paul moore
> > www.paul-moore.com