On Thu, Dec 12, 2024 at 4:15 PM Christian Göttsche <cgoettsche@xxxxxxxxxxxxx> wrote: > > From: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> > > Prior the recent selabel_file(5) rework regular expressions for a > certain stem where matched in the order given by the input. > The Reference and Fedora Policy as well as CIL and libsemanage pre-sort > the file context definitions based on the prefix stem length, so this > ordering was adopted. > > Do not alter the order by the input of regex specifications, and search > on matches on regex specifications in in parent nodes, which might > contain specifications with definitions defined later in the source > file. > This restores backward compatibility, especially for Android. > > Reported-by: Takaya Saeki <takayas@xxxxxxxxxxxx> > Closes: https://lore.kernel.org/selinux/CAH9xa6eFO6BNeGko90bsq8CuDba9eO+qdDoF+7zfyAUHEDpH9g@xxxxxxxxxxxxxx/ > Fixes: 92306da ("libselinux: rework selabel_file(5) database") > Signed-off-by: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> > --- > v2: > - search parent nodes for later regex specs > The pre-compiled fcontext format changed, due to the addition of the > line number for regex specs. Thus files generated with 3.8-rc1 > will fail to load, but it won't result in wrong lookup results like > the v1 patch. > Signed-off-by: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> Acked-by: James Carter <jwcart2@xxxxxxxxx> > --- > libselinux/src/label_file.c | 331 ++++++++++++++------------ > libselinux/src/label_file.h | 47 +--- > libselinux/utils/sefcontext_compile.c | 11 +- > 3 files changed, 202 insertions(+), 187 deletions(-) > > diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c > index 80a7c5ab..56e20949 100644 > --- a/libselinux/src/label_file.c > +++ b/libselinux/src/label_file.c > @@ -87,14 +87,14 @@ void sort_spec_node(struct spec_node *node, struct spec_node *parent) > > node->parent = parent; > > - /* Sort for comparison support and binary search lookup */ > + /* > + * Sort for comparison support and binary search lookup, > + * except for regex specs which are matched in reverse input order. > + */ > > if (node->literal_specs_num > 1) > qsort(node->literal_specs, node->literal_specs_num, sizeof(struct literal_spec), compare_literal_spec); > > - if (node->regex_specs_num > 1) > - qsort(node->regex_specs, node->regex_specs_num, sizeof(struct regex_spec), compare_regex_spec); > - > if (node->children_num > 1) > qsort(node->children, node->children_num, sizeof(struct spec_node), compare_spec_node); > > @@ -144,36 +144,38 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) > > if (node->regex_specs_num > 1) { > for (uint32_t i = 0; i < node->regex_specs_num - 1; i++) { > - const struct regex_spec *node1 = &node->regex_specs[i]; > - const struct regex_spec *node2 = &node->regex_specs[i+1]; > + for (uint32_t j = i; j < node->regex_specs_num - 1; j++) { > + const struct regex_spec *node1 = &node->regex_specs[i]; > + const struct regex_spec *node2 = &node->regex_specs[j + 1]; > > - if (node1->prefix_len != node2->prefix_len) > - continue; > + if (node1->prefix_len != node2->prefix_len) > + continue; > > - if (strcmp(node1->regex_str, node2->regex_str) != 0) > - continue; > + if (strcmp(node1->regex_str, node2->regex_str) != 0) > + continue; > > - if (node1->file_kind != LABEL_FILE_KIND_ALL && node2->file_kind != LABEL_FILE_KIND_ALL && node1->file_kind != node2->file_kind) > - continue; > + if (node1->file_kind != LABEL_FILE_KIND_ALL && node2->file_kind != LABEL_FILE_KIND_ALL && node1->file_kind != node2->file_kind) > + continue; > > - rc = -1; > - errno = EINVAL; > - if (strcmp(node1->lr.ctx_raw, node2->lr.ctx_raw) != 0) { > - COMPAT_LOG > - (SELINUX_ERROR, > - "%s: Multiple different specifications for %s %s (%s and %s).\n", > - path, > - file_kind_to_string(node1->file_kind), > - node1->regex_str, > - node1->lr.ctx_raw, > - node2->lr.ctx_raw); > - } else { > - COMPAT_LOG > - (SELINUX_ERROR, > - "%s: Multiple same specifications for %s %s.\n", > - path, > - file_kind_to_string(node1->file_kind), > - node1->regex_str); > + rc = -1; > + errno = EINVAL; > + if (strcmp(node1->lr.ctx_raw, node2->lr.ctx_raw) != 0) { > + COMPAT_LOG > + (SELINUX_ERROR, > + "%s: Multiple different specifications for %s %s (%s and %s).\n", > + path, > + file_kind_to_string(node1->file_kind), > + node1->regex_str, > + node1->lr.ctx_raw, > + node2->lr.ctx_raw); > + } else { > + COMPAT_LOG > + (SELINUX_ERROR, > + "%s: Multiple same specifications for %s %s.\n", > + path, > + file_kind_to_string(node1->file_kind), > + node1->regex_str); > + } > } > } > } > @@ -190,7 +192,8 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) > } > > FUZZ_EXTERN int process_text_file(FILE *fp, const char *prefix, > - struct selabel_handle *rec, const char *path) > + struct selabel_handle *rec, const char *path, > + uint8_t inputno) > { > int rc; > size_t line_len; > @@ -199,7 +202,7 @@ FUZZ_EXTERN int process_text_file(FILE *fp, const char *prefix, > char *line_buf = NULL; > > while ((nread = getline(&line_buf, &line_len, fp)) > 0) { > - rc = process_line(rec, path, prefix, line_buf, nread, ++lineno); > + rc = process_line(rec, path, prefix, line_buf, nread, inputno, ++lineno); > if (rc) > goto out; > } > @@ -568,9 +571,10 @@ static int load_mmap_literal_spec(struct mmap_area *mmap_area, bool validating, > } > > static int load_mmap_regex_spec(struct mmap_area *mmap_area, bool validating, bool do_load_precompregex, > + uint8_t inputno, > struct regex_spec *rspec, const struct context_array *ctx_array) > { > - uint32_t data_u32, ctx_id; > + uint32_t data_u32, ctx_id, lineno; > uint16_t data_u16, regex_len; > uint8_t data_u8; > int rc; > @@ -600,6 +604,20 @@ static int load_mmap_regex_spec(struct mmap_area *mmap_area, bool validating, bo > rspec->lr.validated = true; > > > + /* > + * Read line number in source file. > + */ > + rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t)); > + if (rc < 0) > + return -1; > + lineno = be32toh(data_u32); > + > + if (lineno == 0 || lineno == UINT32_MAX) > + return -1; > + rspec->lineno = lineno; > + rspec->inputno = inputno; > + > + > /* > * Read original regex > */ > @@ -649,14 +667,14 @@ static int load_mmap_regex_spec(struct mmap_area *mmap_area, bool validating, bo > if (rc < 0) > return -1; > > - __pthread_mutex_init(&rspec->regex_lock, NULL); > > + __pthread_mutex_init(&rspec->regex_lock, NULL); > > return 0; > } > > static int load_mmap_spec_node(struct mmap_area *mmap_area, const char *path, bool validating, bool do_load_precompregex, > - struct spec_node *node, bool is_root, const struct context_array *ctx_array) > + struct spec_node *node, bool is_root, uint8_t inputno, const struct context_array *ctx_array) > { > uint32_t data_u32, lspec_num, rspec_num, children_num; > uint16_t data_u16, stem_len; > @@ -744,7 +762,7 @@ static int load_mmap_spec_node(struct mmap_area *mmap_area, const char *path, bo > node->regex_specs_alloc = rspec_num; > > for (uint32_t i = 0; i < rspec_num; i++) { > - rc = load_mmap_regex_spec(mmap_area, validating, do_load_precompregex, &node->regex_specs[i], ctx_array); > + rc = load_mmap_regex_spec(mmap_area, validating, do_load_precompregex, inputno, &node->regex_specs[i], ctx_array); > if (rc) > return -1; > } > @@ -776,7 +794,7 @@ static int load_mmap_spec_node(struct mmap_area *mmap_area, const char *path, bo > node->children_alloc = children_num; > > for (uint32_t i = 0; i < children_num; i++) { > - rc = load_mmap_spec_node(mmap_area, path, validating, do_load_precompregex, &node->children[i], false, ctx_array); > + rc = load_mmap_spec_node(mmap_area, path, validating, do_load_precompregex, &node->children[i], false, inputno, ctx_array); > if (rc) > return -1; > > @@ -796,7 +814,7 @@ static int load_mmap_spec_node(struct mmap_area *mmap_area, const char *path, bo > } > > FUZZ_EXTERN int load_mmap(FILE *fp, const size_t len, struct selabel_handle *rec, > - const char *path) > + const char *path, uint8_t inputno) > { > struct saved_data *data = rec->data; > struct spec_node *root = NULL; > @@ -952,6 +970,7 @@ end_arch_check: > rc = load_mmap_spec_node(mmap_area, path, rec->validating, > reg_version_matches && reg_arch_matches, > root, true, > + inputno, > &ctx_array); > if (rc) > goto err; > @@ -1142,7 +1161,8 @@ static FILE *open_file(const char *path, const char *suffix, > static int process_file(const char *path, const char *suffix, > struct selabel_handle *rec, > const char *prefix, > - struct selabel_digest *digest) > + struct selabel_digest *digest, > + uint8_t inputno) > { > int rc; > unsigned int i; > @@ -1171,9 +1191,9 @@ static int process_file(const char *path, const char *suffix, > COMPAT_LOG(SELINUX_INFO, "%s: Old compiled fcontext format, skipping\n", found_path); > errno = EINVAL; > } else if (rc == 1) { > - rc = load_mmap(fp, sb.st_size, rec, found_path); > + rc = load_mmap(fp, sb.st_size, rec, found_path, inputno); > } else { > - rc = process_text_file(fp, prefix, rec, found_path); > + rc = process_text_file(fp, prefix, rec, found_path, inputno); > } > > if (!rc) > @@ -1434,7 +1454,7 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > /* > * The do detailed validation of the input and fill the spec array > */ > - status = process_file(path, NULL, rec, prefix, rec->digest); > + status = process_file(path, NULL, rec, prefix, rec->digest, 0); > if (status) > goto finish; > > @@ -1448,12 +1468,12 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > > if (!baseonly) { > status = process_file(path, "homedirs", rec, prefix, > - rec->digest); > + rec->digest, 1); > if (status && errno != ENOENT) > goto finish; > > status = process_file(path, "local", rec, prefix, > - rec->digest); > + rec->digest, 2); > if (status && errno != ENOENT) > goto finish; > } > @@ -1579,77 +1599,84 @@ static struct lookup_result *lookup_check_node(struct spec_node *node, const cha > { > struct lookup_result *result = NULL; > struct lookup_result **next = &result; > + struct lookup_result *child_regex_match = NULL; > + uint8_t child_regex_match_inputno = 0; /* initialize to please GCC */ > + uint32_t child_regex_match_lineno = 1; /* initialize to please GCC */ > size_t key_len = strlen(key); > > assert(!(find_all && buf != NULL)); > > for (struct spec_node *n = node; n; n = n->parent) { > > - uint32_t literal_idx = search_literal_spec(n->literal_specs, n->literal_specs_num, key, key_len, partial); > - if (literal_idx != (uint32_t)-1) { > - do { > - struct literal_spec *lspec = &n->literal_specs[literal_idx]; > + if (n == node) { > + uint32_t literal_idx = search_literal_spec(n->literal_specs, n->literal_specs_num, key, key_len, partial); > + if (literal_idx != (uint32_t)-1) { > + do { > + struct literal_spec *lspec = &n->literal_specs[literal_idx]; > > - if (file_kind == LABEL_FILE_KIND_ALL || lspec->file_kind == LABEL_FILE_KIND_ALL || lspec->file_kind == file_kind) { > - struct lookup_result *r; > + if (file_kind == LABEL_FILE_KIND_ALL || lspec->file_kind == LABEL_FILE_KIND_ALL || lspec->file_kind == file_kind) { > + struct lookup_result *r; > > #ifdef __ATOMIC_RELAXED > - __atomic_store_n(&lspec->any_matches, true, __ATOMIC_RELAXED); > + __atomic_store_n(&lspec->any_matches, true, __ATOMIC_RELAXED); > #else > #error "Please use a compiler that supports __atomic builtins" > #endif > > - if (strcmp(lspec->lr.ctx_raw, "<<none>>") == 0) { > - free_lookup_result(result); > - errno = ENOENT; > - return NULL; > - } > + if (strcmp(lspec->lr.ctx_raw, "<<none>>") == 0) { > + errno = ENOENT; > + goto fail; > + } > > - if (likely(buf)) { > - r = buf; > - } else { > - r = malloc(sizeof(*r)); > - if (!r) { > - free_lookup_result(result); > - return NULL; > + if (likely(buf)) { > + r = buf; > + } else { > + r = malloc(sizeof(*r)); > + if (!r) > + goto fail; > } > - } > > - *r = (struct lookup_result) { > - .regex_str = lspec->regex_str, > - .prefix_len = lspec->prefix_len, > - .file_kind = lspec->file_kind, > - .lr = &lspec->lr, > - .has_meta_chars = false, > - .next = NULL, > - }; > + *r = (struct lookup_result) { > + .regex_str = lspec->regex_str, > + .prefix_len = lspec->prefix_len, > + .file_kind = lspec->file_kind, > + .lr = &lspec->lr, > + .has_meta_chars = false, > + .next = NULL, > + }; > > - if (likely(!find_all)) > - return r; > + if (likely(!find_all)) > + return r; > > - *next = r; > - next = &r->next; > - } > + *next = r; > + next = &r->next; > + } > > - literal_idx++; > - } while (literal_idx < n->literal_specs_num && > - (partial ? (strncmp(n->literal_specs[literal_idx].literal_match, key, key_len) == 0) > - : (strcmp(n->literal_specs[literal_idx].literal_match, key) == 0))); > + literal_idx++; > + } while (literal_idx < n->literal_specs_num && > + (partial ? (strncmp(n->literal_specs[literal_idx].literal_match, key, key_len) == 0) > + : (strcmp(n->literal_specs[literal_idx].literal_match, key) == 0))); > + } > } > > - for (uint32_t i = 0; i < n->regex_specs_num; i++) { > - struct regex_spec *rspec = &n->regex_specs[i]; > + for (uint32_t i = n->regex_specs_num; i > 0; i--) { > + /* search in reverse order */ > + struct regex_spec *rspec = &n->regex_specs[i - 1]; > const char *errbuf = NULL; > int rc; > > + if (child_regex_match && > + (rspec->inputno < child_regex_match_inputno || > + (rspec->inputno == child_regex_match_inputno && rspec->lineno < child_regex_match_lineno))) > + break; > + > if (file_kind != LABEL_FILE_KIND_ALL && rspec->file_kind != LABEL_FILE_KIND_ALL && file_kind != rspec->file_kind) > continue; > > if (compile_regex(rspec, &errbuf) < 0) { > COMPAT_LOG(SELINUX_ERROR, "Failed to compile regular expression '%s': %s\n", > rspec->regex_str, errbuf); > - free_lookup_result(result); > - return NULL; > + goto fail; > } > > rc = regex_match(rspec->regex, key, partial); > @@ -1665,19 +1692,18 @@ static struct lookup_result *lookup_check_node(struct spec_node *node, const cha > } > > if (strcmp(rspec->lr.ctx_raw, "<<none>>") == 0) { > - free_lookup_result(result); > errno = ENOENT; > - return NULL; > + goto fail; > } > > - if (likely(buf)) { > + if (child_regex_match) { > + r = child_regex_match; > + } else if (buf) { > r = buf; > } else { > r = malloc(sizeof(*r)); > - if (!r) { > - free_lookup_result(result); > - return NULL; > - } > + if (!r) > + goto fail; > } > > *r = (struct lookup_result) { > @@ -1689,8 +1715,12 @@ static struct lookup_result *lookup_check_node(struct spec_node *node, const cha > .next = NULL, > }; > > - if (likely(!find_all)) > - return r; > + if (likely(!find_all)) { > + child_regex_match = r; > + child_regex_match_inputno = rspec->inputno; > + child_regex_match_lineno = rspec->lineno; > + goto parent_node; > + } > > *next = r; > next = &r->next; > @@ -1702,15 +1732,28 @@ static struct lookup_result *lookup_check_node(struct spec_node *node, const cha > continue; > > /* else it's an error */ > - free_lookup_result(result); > errno = ENOENT; > - return NULL; > + goto fail; > } > + > + parent_node: > + continue; > } > > + if (child_regex_match) > + return child_regex_match; > + > if (!result) > errno = ENOENT; > return result; > + > + fail: > + if (!find_all && child_regex_match && child_regex_match != buf) > + free(child_regex_match); > + > + free_lookup_result(result); > + > + return NULL; > } > > static struct spec_node* search_child_node(struct spec_node *array, uint32_t size, const char *key, size_t key_len) > @@ -2221,81 +2264,69 @@ static enum selabel_cmp_result spec_node_cmp(const struct spec_node *node1, cons > while (iter1 < node1->regex_specs_num && iter2 < node2->regex_specs_num) { > const struct regex_spec *rspec1 = &node1->regex_specs[iter1]; > const struct regex_spec *rspec2 = &node2->regex_specs[iter2]; > - int cmp; > - > - if (rspec1->prefix_len > rspec2->prefix_len) { > - if (result == SELABEL_EQUAL || result == SELABEL_SUPERSET) { > - result = SELABEL_SUPERSET; > - iter1++; > - continue; > - } > + bool found_successor; > > - return rspec_incomp(node1->stem, rspec1, rspec2, "regex_prefix_length", iter1, iter2); > + if (rspec1->file_kind == rspec2->file_kind && strcmp(rspec1->regex_str, rspec2->regex_str) == 0) { > + iter1++; > + iter2++; > + continue; > } > > - if (rspec1->prefix_len < rspec2->prefix_len) { > - if (result == SELABEL_EQUAL || result == SELABEL_SUBSET) { > - result = SELABEL_SUBSET; > - iter2++; > - continue; > - } > + if (result == SELABEL_SUPERSET) { > + iter1++; > + continue; > + } > > - return rspec_incomp(node1->stem, rspec1, rspec2, "regex_prefix_length", iter1, iter2); > + if (result == SELABEL_SUBSET) { > + iter2++; > + continue; > } > > - /* If prefix length is equal compare regex string */ > + assert(result == SELABEL_EQUAL); > > - cmp = strcmp(rspec1->regex_str, rspec2->regex_str); > - if (cmp < 0) { > - if (result == SELABEL_EQUAL || result == SELABEL_SUPERSET) { > - result = SELABEL_SUPERSET; > - iter1++; > - continue; > - } > + found_successor = false; > > - return rspec_incomp(node1->stem, rspec1, rspec2, "regex_str", iter1, iter2); > - } > + for (uint32_t i = iter2; i < node2->regex_specs_num; i++) { > + const struct regex_spec *successor = &node2->regex_specs[i]; > > - if (cmp > 0) { > - if (result == SELABEL_EQUAL || result == SELABEL_SUBSET) { > + if (rspec1->file_kind == successor->file_kind && strcmp(rspec1->regex_str, successor->regex_str) == 0) { > result = SELABEL_SUBSET; > - iter2++; > - continue; > + iter1++; > + iter2 = i + 1; > + found_successor = true; > + break; > } > - > - return rspec_incomp(node1->stem, rspec1, rspec2, "regex_str", iter1, iter2); > } > > - /* If literal match is equal compare file kind */ > + if (found_successor) > + continue; > > - if (rspec1->file_kind > rspec2->file_kind) { > - if (result == SELABEL_EQUAL || result == SELABEL_SUPERSET) { > - result = SELABEL_SUPERSET; > - iter1++; > - continue; > - } > + for (uint32_t i = iter1; i < node1->regex_specs_num; i++) { > + const struct regex_spec *successor = &node1->regex_specs[i]; > > - return rspec_incomp(node1->stem, rspec1, rspec2, "file_kind", iter1, iter2); > - } > - > - if (rspec1->file_kind < rspec2->file_kind) { > - if (result == SELABEL_EQUAL || result == SELABEL_SUBSET) { > - result = SELABEL_SUBSET; > + if (successor->file_kind == rspec2->file_kind && strcmp(successor->regex_str, rspec2->regex_str) == 0) { > + result = SELABEL_SUPERSET; > + iter1 = i + 1; > iter2++; > - continue; > + found_successor = true; > + break; > } > - > - return rspec_incomp(node1->stem, rspec1, rspec2, "file_kind", iter1, iter2); > } > > - iter1++; > - iter2++; > + if (found_successor) > + continue; > + > + return rspec_incomp(node1->stem, rspec1, rspec2, "regex", iter1, iter2); > } > if (iter1 != node1->regex_specs_num) { > if (result == SELABEL_EQUAL || result == SELABEL_SUPERSET) { > result = SELABEL_SUPERSET; > } else { > - selinux_log(SELINUX_INFO, "selabel_cmp: mismatch regex_str left remnant in stem %s\n", fmt_stem(node1->stem)); > + const struct regex_spec *rspec1 = &node1->regex_specs[iter1]; > + > + selinux_log(SELINUX_INFO, "selabel_cmp: mismatch regex left remnant in stem %s entry %u: (%s, %s, %s)\n", > + fmt_stem(node1->stem), > + iter1, rspec1->regex_str, file_kind_to_string(rspec1->file_kind), rspec1->lr.ctx_raw); > return SELABEL_INCOMPARABLE; > } > } > @@ -2303,7 +2334,11 @@ static enum selabel_cmp_result spec_node_cmp(const struct spec_node *node1, cons > if (result == SELABEL_EQUAL || result == SELABEL_SUBSET) { > result = SELABEL_SUBSET; > } else { > - selinux_log(SELINUX_INFO, "selabel_cmp: mismatch regex_str right remnant in stem %s\n", fmt_stem(node1->stem)); > + const struct regex_spec *rspec2 = &node2->regex_specs[iter2]; > + > + selinux_log(SELINUX_INFO, "selabel_cmp: mismatch regex right remnant in stem %s entry %u: (%s, %s, %s)\n", > + fmt_stem(node1->stem), > + iter2, rspec2->regex_str, file_kind_to_string(rspec2->file_kind), rspec2->lr.ctx_raw); > return SELABEL_INCOMPARABLE; > } > } > diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h > index c7fe3a48..2506f9b5 100644 > --- a/libselinux/src/label_file.h > +++ b/libselinux/src/label_file.h > @@ -61,7 +61,7 @@ struct lookup_result { > }; > #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION > extern int load_mmap(FILE *fp, const size_t len, struct selabel_handle *rec, const char *path); > -extern int process_text_file(FILE *fp, const char *prefix, struct selabel_handle *rec, const char *path); > +extern int process_text_file(FILE *fp, const char *prefix, struct selabel_handle *rec, const char *path, uint8_t inputno); > extern void free_lookup_result(struct lookup_result *result); > extern struct lookup_result *lookup_all(struct selabel_handle *rec, const char *key, int type, bool partial, bool find_all); > extern enum selabel_cmp_result cmp(const struct selabel_handle *h1, const struct selabel_handle *h2); > @@ -81,7 +81,9 @@ struct regex_spec { > char *regex_str; /* original regular expression string for diagnostics */ > struct regex_data *regex; /* backend dependent regular expression data */ > pthread_mutex_t regex_lock; /* lock for lazy compilation of regex */ > + uint32_t lineno; /* Line number in source file */ > uint16_t prefix_len; /* length of fixed path prefix */ > + uint8_t inputno; /* Input number of source file */ > uint8_t file_kind; /* file type */ > bool regex_compiled; /* whether the regex is compiled */ > bool any_matches; /* whether any pathname match */ > @@ -123,7 +125,7 @@ struct spec_node { > uint32_t literal_specs_num, literal_specs_alloc; > > /* > - * Array of regular expression specifications (ordered from most to least specific) > + * Array of regular expression specifications (order preserved from input) > */ > struct regex_spec *regex_specs; > uint32_t regex_specs_num, regex_specs_alloc; > @@ -369,38 +371,6 @@ static inline int compare_literal_spec(const void *p1, const void *p2) > return (l1->file_kind < l2->file_kind) - (l1->file_kind > l2->file_kind); > } > > -static inline int compare_regex_spec(const void *p1, const void *p2) > -{ > - const struct regex_spec *r1 = p1; > - const struct regex_spec *r2 = p2; > - size_t regex_len1, regex_len2; > - int ret; > - > - /* Order from high prefix length to low */ > - ret = (r1->prefix_len < r2->prefix_len) - (r1->prefix_len > r2->prefix_len); > - if (ret) > - return ret; > - > - /* Order from long total regex length to short */ > - regex_len1 = strlen(r1->regex_str); > - regex_len2 = strlen(r2->regex_str); > - ret = (regex_len1 < regex_len2) - (regex_len1 > regex_len2); > - if (ret) > - return ret; > - > - /* > - * Order for no-duplicates check. > - * Use reverse alphabetically order to retain the Fedora ordering of > - * `/usr/(.* /)?lib(/.*)?` before `/usr/(.* /)?bin(/.*)?`. > - */ > - ret = strcmp(r1->regex_str, r2->regex_str); > - if (ret) > - return -ret; > - > - /* Order wildcard mode (0) last */ > - return (r1->file_kind < r2->file_kind) - (r1->file_kind > r2->file_kind); > -} > - > static inline int compare_spec_node(const void *p1, const void *p2) > { > const struct spec_node *n1 = p1; > @@ -531,7 +501,7 @@ static inline int compile_regex(struct regex_spec *spec, const char **errbuf) > > static int insert_spec(const struct selabel_handle *rec, struct saved_data *data, > const char *prefix, char *regex, uint8_t file_kind, char *context, > - const char *path, unsigned int lineno) > + const char *path, uint8_t inputno, uint32_t lineno) > { > size_t prefix_len; > bool has_meta; > @@ -642,6 +612,8 @@ static int insert_spec(const struct selabel_handle *rec, struct saved_data *data > .regex_lock = PTHREAD_MUTEX_INITIALIZER, > .file_kind = file_kind, > .any_matches = false, > + .inputno = inputno, > + .lineno = lineno, > .lr.ctx_raw = context, > .lr.ctx_trans = NULL, > .lr.lineno = lineno, > @@ -816,7 +788,8 @@ static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes) > * utils/sefcontext_compile.c */ > static inline int process_line(struct selabel_handle *rec, > const char *path, const char *prefix, > - char *line_buf, size_t nread, unsigned lineno) > + char *line_buf, size_t nread, > + uint8_t inputno, uint32_t lineno) > { > int items; > char *regex = NULL, *type = NULL, *context = NULL; > @@ -886,7 +859,7 @@ static inline int process_line(struct selabel_handle *rec, > free(type); > } > > - return insert_spec(rec, data, prefix, regex, file_kind, context, path, lineno); > + return insert_spec(rec, data, prefix, regex, file_kind, context, path, inputno, lineno); > } > > #endif /* _SELABEL_FILE_H_ */ > diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c > index b4445a1f..811b2a1a 100644 > --- a/libselinux/utils/sefcontext_compile.c > +++ b/libselinux/utils/sefcontext_compile.c > @@ -31,7 +31,7 @@ static int validate_context(char **ctxp) > > static int process_file(struct selabel_handle *rec, const char *filename) > { > - unsigned int line_num; > + uint32_t line_num; > int rc; > char *line_buf = NULL; > size_t line_len = 0; > @@ -48,7 +48,7 @@ static int process_file(struct selabel_handle *rec, const char *filename) > line_num = 0; > rc = 0; > while ((nread = getline(&line_buf, &line_len, context_file)) > 0) { > - rc = process_line(rec, filename, prefix, line_buf, nread, ++line_num); > + rc = process_line(rec, filename, prefix, line_buf, nread, 0, ++line_num); > if (rc || ctx_err) { > /* With -p option need to check and fail if ctx err as > * process_line() context validation on Linux does not > @@ -160,6 +160,7 @@ static int create_sidtab(const struct saved_data *data, struct sidtab *stab) > * Regular Expression Specification Format (RSpec) > * > * u32 - context table index for raw context (1-based) > + * u32 - line number in source file > * u16 - length of upcoming regex_str INCLUDING nul > * [char] - char array of the original regex string including the stem INCLUDING nul > * u16 - length of the fixed path prefix > @@ -307,6 +308,12 @@ static int write_regex_spec(FILE *bin_file, bool do_write_precompregex, const st > if (len != 1) > return -1; > > + /* write line number */ > + data_u32 = htobe32(rspec->lineno); > + len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file); > + if (len != 1) > + return -1; > + > /* write regex string */ > regex = rspec->regex_str; > regex_len = strlen(regex); > -- > 2.45.2 > >