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

Stephen Smalley <[email protected]> Tue, 21 Jul 2026 11:45:13 -0400
Newsgroups org.kernel.vger.selinux
Message-ID <CAEjxPJ6zcYW7_mGPhnTNV5oq-8uxsmDJxnXFoZjxyYYg7ce76A@mail.gmail.com>
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.

>                 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

>                 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
>