Re: [PATCH v2 2/2] libselinux: support multiple context files for the file backend
Stephen Smalley <[email protected]> Thu, 23 Jul 2026 12:06:54 -0400
| Newsgroups | org.kernel.vger.selinux |
|---|---|
| Message-ID | <CAEjxPJ6qSMAHv9=KfM=-AybsWWoE3E+jG7JTgfuFjSgJ3hLgcA@mail.gmail.com> |
On Wed, Jul 22, 2026 at 11: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 is only performed within each context file. The current > behavior expects the ability for a later file to overwrite a previous > declaration (e.g., .local). > > Signed-off-by: Thiébaud Weksteen <[email protected]> > --- > Changes since v1: > - Restore nodups_spec_node, ignore duplicates across files. > - Change num_paths type to uint8_t and check for overflow. > - Check for NULL before strdup any path. > - Use num_paths and num_paths+1 for homedirs and local. > > libselinux/src/label_file.c | 139 ++++++++++++++++++++++++------------ > 1 file changed, 92 insertions(+), 47 deletions(-) > > diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c > index d91e1462..02bd6f5d 100644 > --- a/libselinux/src/label_file.c > +++ b/libselinux/src/label_file.c- > @@ -1484,22 +1491,30 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > unsigned n) > { > struct saved_data *data = rec->data; > - const char *path = NULL; > + uint8_t num_paths = 0, i; > + char **paths = NULL; > const char *prefix = NULL; > - int status = -1, baseonly = 0; > + int status = -1; > + bool baseonly = false, path_provided = false; > > /* Process arguments */ > - while (n) { > - n--; > - switch (opts[n].type) { > + for (i = 0; i < n; i++) { uint8_t i versus unsigned n; if n >= 256, i will wrap and we'll loop forever. Unlikely I know but should either use unsigned for i or cap n up front. > + switch (opts[i].type) { > case SELABEL_OPT_PATH: > - path = opts[n].value; > + if (opts[i].value) { > + num_paths++; > + if (num_paths == UINT8_MAX) { > + errno = EINVAL; > + return -1; > + } > + path_provided = true; > + } > break; > case SELABEL_OPT_SUBSET: > - prefix = opts[n].value; > + prefix = opts[i].value; > break; > case SELABEL_OPT_BASEONLY: > - baseonly = !!opts[n].value; > + baseonly = !!opts[i].value; > break; > case SELABEL_OPT_UNUSED: > case SELABEL_OPT_VALIDATE: > @@ -1511,10 +1526,50 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > } > } > > + /* If no paths were provided, we will use the default path or fail, > + * depending on the target. */ > + if (!path_provided) { > +#if !defined(BUILD_HOST) && !defined(ANDROID) > + num_paths = 1; > +#else > + selinux_log(SELINUX_ERROR, > + "No path given to file labeling backend\n"); > + errno = EINVAL; > + return -1; > +#endif > + } > + > + /* If !baseonly, we reserve the last 2 indexes for homedirs and local. > + * Let's make sure we don't wrap */ > + if (!baseonly && num_paths > UINT8_MAX - 1) { > + errno = EINVAL; > + return -1; > + } > + > + /* Allocate the paths. */ > + paths = calloc(num_paths, sizeof(*paths)); > + if (paths == NULL) > + goto finish; > + > + rec->spec_files = paths; > + rec->spec_files_len = num_paths; > + > + /* Copy all the paths given. */ > + if (path_provided) { > + for (i = 0; i < n; i++) { > + if (opts[i].type == SELABEL_OPT_PATH && opts[i].value) { > + *paths = strdup(opts[i].value); > + if (*paths == NULL) > + goto finish; > + paths++; > + } > + } > + } > + > #if !defined(BUILD_HOST) && !defined(ANDROID) > char subs_file[PATH_MAX + 1]; > /* Process local and distribution substitution files */ > - if (!path) { > + if (!path_provided) { > status = selabel_subs_init( > selinux_file_context_subs_dist_path(), rec->digest, > &data->dist_subs, &data->dist_subs_num, > @@ -1526,66 +1581,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(*rec->spec_files)); > - 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); > 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); > 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 + 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 >