Re: [PATCH 2/2] libselinux: support multiple contexts for the file backend

Stephen Smalley <[email protected]> Tue, 21 Jul 2026 12:36:39 -0400
Newsgroups org.kernel.vger.selinux
Message-ID <CAEjxPJ7OXC8MjBN_o6jm9W6Ewm6u9gj+sxKfy_wYaxt1+qW6=Q@mail.gmail.com>
On Tue, Jul 21, 2026 at 11:45 AM Stephen Smalley
<[email protected]> wrote:
>
> On Mon, Jul 20, 2026 at 10:11 PM Thiébaud Weksteen <[email protected]> wrote:
> >
> > Update the file labeling backend to support specifying multiple
> > SELABEL_OPT_PATH options in selabel_open().
> >
> > All provided paths are stored in rec->spec_files and processed during
> > initialization. Derived files, such as substitutions (.subs, .subs_dist)
> > and auxiliary contexts (.homedirs, .local), continue to be based on the
> > first path (or default selinux_file_context_path()) when enabled.
> >
> > Duplicate checking logic is refactored into a helper function
> > report_dups(). A duplicate with the same specification result is not
> > fatal anymore, but a warning is still logged.
> >
> > Additionally, duplicate validation is moved after all files (including
> > .homedirs and .local) are loaded and sorted. For single file context setups,
> > the only difference in behavior is that duplicate validation now extends to
> > fc.homedirs and fc.local.
> >
> > A similar multiple files setup has been used in Android for 9+ years.
> >
> > Signed-off-by: Thiébaud Weksteen <[email protected]>
> > ---
> >  libselinux/src/label_file.c | 216 ++++++++++++++++++++++--------------
> >  1 file changed, 132 insertions(+), 84 deletions(-)
> >
> > diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
> > index eb2e66da..902c9a25 100644
> > --- a/libselinux/src/label_file.c
> > +++ b/libselinux/src/label_file.c
> > @@ -1526,66 +1584,56 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
> >                                            &data->subs_num, &data->subs_alloc);
> >                 if (status)
> >                         goto finish;
> > -               path = selinux_file_context_path();
> > +               rec->spec_files[0] = strdup(selinux_file_context_path());
> > +               if (rec->spec_files[0] == NULL)
> > +                       goto finish;
> >         } else {
> > -               snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path);
> > +               snprintf(subs_file, sizeof(subs_file), "%s.subs_dist",
> > +                        rec->spec_files[0]);
> >                 status = selabel_subs_init(subs_file, rec->digest,
> >                                            &data->dist_subs,
> >                                            &data->dist_subs_num,
> >                                            &data->dist_subs_alloc);
> >                 if (status)
> >                         goto finish;
> > -               snprintf(subs_file, sizeof(subs_file), "%s.subs", path);
> > +               snprintf(subs_file, sizeof(subs_file), "%s.subs",
> > +                        rec->spec_files[0]);
> >                 status = selabel_subs_init(subs_file, rec->digest, &data->subs,
> >                                            &data->subs_num, &data->subs_alloc);
> >                 if (status)
> >                         goto finish;
> >         }
> > -
> >  #endif
> >
> > -       if (!path) {
> > -               errno = EINVAL;
> > -               goto finish;
> > -       }
> > -
> > -       rec->spec_files = calloc(1, sizeof(path));
> > -       if (!rec->spec_files)
> > -               goto finish;
> > -       rec->spec_files[0] = strdup(path);
> > -       if (!rec->spec_files[0])
> > -               goto finish;
> > -       rec->spec_files_len = 1;
> > -
> >         /*
> > -        * The do detailed validation of the input and fill the spec array
> > +        * Process each input file.
> >          */
> > -       status = process_file(path, NULL, rec, prefix, rec->digest, 0);
> > -       if (status)
> > -               goto finish;
> > -
> > -       if (rec->validating) {
> > -               sort_specs(data);
> > -
> > -               status = nodups_spec_node(data->root, path);
> > +       for (i = 0; i < num_paths; i++) {
> > +               status = process_file(rec->spec_files[i], NULL, rec, prefix,
> > +                                     rec->digest, i);
>
> process_file() last argument is uint8_t; i and num_paths are size_t.
> Need to cap the max num_paths somewhere or change the type.
>
> >                 if (status)
> >                         goto finish;
> >         }
> >
> >         if (!baseonly) {
> > -               status = process_file(path, "homedirs", rec, prefix,
> > -                                     rec->digest, 1);
> > +               status = process_file(rec->spec_files[0], "homedirs", rec,
> > +                                     prefix, rec->digest, num_paths + 1);
>
> Ditto.

Also, could use num_paths here since i ends at num_paths - 1.

>
> >                 if (status && errno != ENOENT)
> >                         goto finish;
> >
> > -               status = process_file(path, "local", rec, prefix, rec->digest,
> > -                                     2);
> > +               status = process_file(rec->spec_files[0], "local", rec, prefix,
> > +                                     rec->digest, num_paths + 2);
>
> Ditto

And this could be num_path + 1.
>
> >                 if (status && errno != ENOENT)
> >                         goto finish;
> >         }
> >
> > -       if (!rec->validating || !baseonly)
> > -               sort_specs(data);
> > +       sort_specs(data);
> > +
> > +       if (rec->validating) {
> > +               status = nodups_spec_node(rec, data->root);
> > +               if (status)
> > +                       goto finish;
> > +       }
> >
> >         digest_gen_hash(rec->digest);
> >
> > --
> > 2.55.0.229.g6434b31f56-goog
> >