On 09/07/2016 04:08 AM, Janis Danisevskis wrote: > From: Janis Danisevskis <jdanis@xxxxxxxxxx> > > This patch moves all pcre1/2 dependencies into the new files regex.h > and regex.c implementing the common denominator of features needed > by libselinux. The compiler flag -DUSE_PCRE2 toggles between the > used implementations. > > As of this patch libselinux supports either pcre or pcre2 but not > both at the same time. The persistently stored file contexts > information differs. This means libselinux can only load file > context files generated by sefcontext_compile build with the > same pcre variant. Shouldn't the pcre variant be encoded in some manner in the file_contexts.bin file so that libselinux can tell immediately whether it is supported? > Also, for pcre2 the persistent format is architecture dependant. > Stored precompiled regular expressions can only be used on the > same architecture they were generated on. If pcre2 is used and > sefcontext_compile shall generate portable output, it and libselinux > must be compiled with -DNO_PERSISTENTLY_STORED_PATTERNS, at the > cost of having to recompile the regular expressions at load time. Is it worth supporting the -DNO_PERSISTENTLY_STORED_PATTERNS case? The point of using file_contexts.bin was to move the cost of compiling the regexes to build time rather than load time; if we cannot do that, then how much do we gain from using file_contexts.bin instead of just falling back to file_contexts? The #ifdef maze makes it very hard to read and maintain this code; that needs to be refactored. valgrind is reporting numerous errors, including both use of uninitialised values and memory leaks with both patches applied. Try: make DESTDIR=~/obj CFLAGS+=-g clean install LD_LIBRARY_PATH=~/obj/lib valgrind --leak-check=full ~/obj/usr/sbin/matchpathcon /etc On x86_64. Will provide review of the code itself later... > > Signed-off-by: Janis Danisevskis <jdanis@xxxxxxxxxx> > --- > libselinux/Makefile | 13 ++ > libselinux/src/Makefile | 4 +- > libselinux/src/label_file.c | 91 ++------ > libselinux/src/label_file.h | 54 ++--- > libselinux/src/regex.c | 405 ++++++++++++++++++++++++++++++++++ > libselinux/src/regex.h | 168 ++++++++++++++ > libselinux/utils/Makefile | 4 +- > libselinux/utils/sefcontext_compile.c | 53 +---- > 8 files changed, 637 insertions(+), 155 deletions(-) > create mode 100644 libselinux/src/regex.c > create mode 100644 libselinux/src/regex.h > > diff --git a/libselinux/Makefile b/libselinux/Makefile > index 6142b60..15d051e 100644 > --- a/libselinux/Makefile > +++ b/libselinux/Makefile > @@ -24,6 +24,19 @@ ifeq ($(DISABLE_SETRANS),y) > endif > export DISABLE_AVC DISABLE_SETRANS DISABLE_RPM DISABLE_BOOL EMFLAGS > > +USE_PCRE2 ?= n > +DISABLE_PERSISTENTLY_STORED_REGEX_PATTERNS ?= n > +ifeq ($(USE_PCRE2),y) > + PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8 > + ifeq ($(DISABLE_PERSISTENTLY_STORED_REGEX_PATTERNS), y) > + PCRE_CFLAGS += -DNO_PERSISTENTLY_STORED_PATTERNS > + endif > + PCRE_LDFLAGS := -lpcre2-8 > +else > + PCRE_LDFLAGS := -lpcre > +endif > +export PCRE_CFLAGS PCRE_LDFLAGS > + > all install relabel clean distclean indent: > @for subdir in $(SUBDIRS); do \ > (cd $$subdir && $(MAKE) $@) || exit 1; \ > diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile > index 37d01af..66687e6 100644 > --- a/libselinux/src/Makefile > +++ b/libselinux/src/Makefile > @@ -74,7 +74,7 @@ CFLAGS ?= -O -Wall -W -Wundef -Wformat-y2k -Wformat-security -Winit-self -Wmissi > -fipa-pure-const -Wno-suggest-attribute=pure -Wno-suggest-attribute=const \ > -Werror -Wno-aggregate-return -Wno-redundant-decls > > -override CFLAGS += -I../include -I$(INCLUDEDIR) -D_GNU_SOURCE $(EMFLAGS) > +override CFLAGS += -I../include -I$(INCLUDEDIR) -D_GNU_SOURCE $(EMFLAGS) $(PCRE_CFLAGS) > > SWIG_CFLAGS += -Wno-error -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-parameter \ > -Wno-shadow -Wno-uninitialized -Wno-missing-prototypes -Wno-missing-declarations > @@ -113,7 +113,7 @@ $(LIBA): $(OBJS) > $(RANLIB) $@ > > $(LIBSO): $(LOBJS) > - $(CC) $(CFLAGS) -shared -o $@ $^ -lpcre -ldl $(LDFLAGS) -L$(LIBDIR) -Wl,-soname,$(LIBSO),-z,defs,-z,relro > + $(CC) $(CFLAGS) -shared -o $@ $^ $(PCRE_LDFLAGS) -ldl $(LDFLAGS) -L$(LIBDIR) -Wl,-soname,$(LIBSO),-z,defs,-z,relro > ln -sf $@ $(TARGET) > > $(LIBPC): $(LIBPC).in ../VERSION > diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c > index c89bb35..6698624 100644 > --- a/libselinux/src/label_file.c > +++ b/libselinux/src/label_file.c > @@ -15,7 +15,6 @@ > #include <errno.h> > #include <limits.h> > #include <stdint.h> > -#include <pcre.h> > #include <unistd.h> > #include <sys/mman.h> > #include <sys/types.h> > @@ -176,7 +175,10 @@ static int load_mmap(struct selabel_handle *rec, const char *path, > return -1; > > if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) { > - len = strlen(pcre_version()); > + if (!regex_version()) { > + return -1; > + } > + len = strlen(regex_version()); > > rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); > if (rc < 0) > @@ -198,7 +200,7 @@ static int load_mmap(struct selabel_handle *rec, const char *path, > } > > str_buf[entry_len] = '\0'; > - if ((strcmp(str_buf, pcre_version()) != 0)) { > + if ((strcmp(str_buf, regex_version()) != 0)) { > free(str_buf); > return -1; > } > @@ -278,7 +280,11 @@ static int load_mmap(struct selabel_handle *rec, const char *path, > > spec = &data->spec_arr[data->nspec]; > spec->from_mmap = 1; > +#if defined USE_PCRE2 && defined NO_PERSISTENTLY_STORED_PATTERNS > + spec->regcomp = 0; > +#else > spec->regcomp = 1; > +#endif > > /* Process context */ > rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); > @@ -364,47 +370,10 @@ static int load_mmap(struct selabel_handle *rec, const char *path, > spec->prefix_len = prefix_len; > } > > - /* Process regex and study_data entries */ > - rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); > - if (rc < 0 || !entry_len) { > - rc = -1; > - goto err; > - } > - spec->regex = (pcre *)mmap_area->next_addr; > - rc = next_entry(NULL, mmap_area, entry_len); > + rc = regex_load_mmap(mmap_area, &spec->regex); > if (rc < 0) > goto err; > > - /* Check that regex lengths match. pcre_fullinfo() > - * also validates its magic number. */ > - rc = pcre_fullinfo(spec->regex, NULL, PCRE_INFO_SIZE, &len); > - if (rc < 0 || len != entry_len) { > - rc = -1; > - goto err; > - } > - > - rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); > - if (rc < 0 || !entry_len) { > - rc = -1; > - goto err; > - } > - > - if (entry_len) { > - spec->lsd.study_data = (void *)mmap_area->next_addr; > - spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA; > - rc = next_entry(NULL, mmap_area, entry_len); > - if (rc < 0) > - goto err; > - > - /* Check that study data lengths match. */ > - rc = pcre_fullinfo(spec->regex, &spec->lsd, > - PCRE_INFO_STUDYSIZE, &len); > - if (rc < 0 || len != entry_len) { > - rc = -1; > - goto err; > - } > - } > - > data->nspec++; > } > > @@ -609,10 +578,7 @@ static void closef(struct selabel_handle *rec) > continue; > free(spec->regex_str); > free(spec->type_str); > - if (spec->regcomp) { > - pcre_free(spec->regex); > - pcre_free_study(spec->sd); > - } > + regex_data_free(spec->regex); > } > > for (i = 0; i < (unsigned int)data->num_stems; i++) { > @@ -644,13 +610,14 @@ static struct spec *lookup_common(struct selabel_handle *rec, > { > struct saved_data *data = (struct saved_data *)rec->data; > struct spec *spec_arr = data->spec_arr; > - int i, rc, file_stem, pcre_options = 0; > + int i, rc, file_stem; > mode_t mode = (mode_t)type; > const char *buf; > struct spec *ret = NULL; > char *clean_key = NULL; > const char *prev_slash, *next_slash; > unsigned int sofar = 0; > + struct regex_error_data regex_error_data; > > if (!data->nspec) { > errno = ENOENT; > @@ -677,9 +644,6 @@ static struct spec *lookup_common(struct selabel_handle *rec, > file_stem = find_stem_from_file(data, &buf); > mode &= S_IFMT; > > - if (partial) > - pcre_options |= PCRE_PARTIAL_SOFT; > - > /* > * Check for matching specifications in reverse order, so that > * the last matching specification is used. > @@ -692,25 +656,19 @@ static struct spec *lookup_common(struct selabel_handle *rec, > * a regex check */ > if ((spec->stem_id == -1 || spec->stem_id == file_stem) && > (!mode || !spec->mode || mode == spec->mode)) { > - if (compile_regex(data, spec, NULL) < 0) > + if (compile_regex(data, spec, ®ex_error_data) < 0) > goto finish; > if (spec->stem_id == -1) > - rc = pcre_exec(spec->regex, > - get_pcre_extra(spec), > - key, strlen(key), 0, > - pcre_options, NULL, 0); > + rc = regex_match(spec->regex, key, partial); > else > - rc = pcre_exec(spec->regex, > - get_pcre_extra(spec), > - buf, strlen(buf), 0, > - pcre_options, NULL, 0); > - if (rc == 0) { > + rc = regex_match(spec->regex, buf, partial); > + if (rc == REGEX_MATCH) { > spec->matches++; > break; > - } else if (partial && rc == PCRE_ERROR_PARTIAL) > + } else if (partial && rc == REGEX_MATCH_PARTIAL) > break; > > - if (rc == PCRE_ERROR_NOMATCH) > + if (rc == REGEX_NO_MATCH) > continue; > > errno = ENOENT; > @@ -850,16 +808,9 @@ static enum selabel_cmp_result cmp(struct selabel_handle *h1, > } > > if (spec1->regcomp && spec2->regcomp) { > - size_t len1, len2; > - int rc; > - > - rc = pcre_fullinfo(spec1->regex, NULL, PCRE_INFO_SIZE, &len1); > - assert(rc == 0); > - rc = pcre_fullinfo(spec2->regex, NULL, PCRE_INFO_SIZE, &len2); > - assert(rc == 0); > - if (len1 != len2 || > - memcmp(spec1->regex, spec2->regex, len1)) > + if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){ > return incomp(spec1, spec2, "regex", i, j); > + } > } else { > if (strcmp(spec1->regex_str, spec2->regex_str)) > return incomp(spec1, spec2, "regex_str", i, j); > diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h > index 6d1e890..a2e30e5 100644 > --- a/libselinux/src/label_file.h > +++ b/libselinux/src/label_file.h > @@ -6,6 +6,14 @@ > > #include <sys/stat.h> > > +/* > + * regex.h/c were introduced to hold all dependencies on the regular > + * expression back-end when we started supporting PCRE2. regex.h defines a > + * minimal interface required by libselinux, so that the remaining code > + * can be agnostic about the underlying implementation. > + */ > +#include "regex.h" > + > #include "callbacks.h" > #include "label_internal.h" > > @@ -19,21 +27,12 @@ > > #define SELINUX_COMPILED_FCONTEXT_MAX_VERS SELINUX_COMPILED_FCONTEXT_PREFIX_LEN > > -/* Prior to version 8.20, libpcre did not have pcre_free_study() */ > -#if (PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20)) > -#define pcre_free_study pcre_free > -#endif > - > /* A file security context specification. */ > struct spec { > struct selabel_lookup_rec lr; /* holds contexts for lookup result */ > char *regex_str; /* regular expession string for diagnostics */ > char *type_str; /* type string for diagnostic messages */ > - pcre *regex; /* compiled regular expression */ > - union { > - pcre_extra *sd; /* pointer to extra compiled stuff */ > - pcre_extra lsd; /* used to hold the mmap'd version */ > - }; > + struct regex_data * regex; /* backend dependent regular expression data */ > mode_t mode; /* mode format value */ > int matches; /* number of matching pathnames */ > int stem_id; /* indicates which stem-compression item */ > @@ -78,17 +77,6 @@ struct saved_data { > struct mmap_area *mmap_areas; > }; > > -static inline pcre_extra *get_pcre_extra(struct spec *spec) > -{ > - if (spec->from_mmap) { > - if (spec->lsd.study_data) > - return &spec->lsd; > - else > - return NULL; > - } else > - return spec->sd; > -} > - > static inline mode_t string_to_mode(char *mode) > { > size_t len; > @@ -331,13 +319,12 @@ static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes) > } > > static inline int compile_regex(struct saved_data *data, struct spec *spec, > - const char **errbuf) > + struct regex_error_data * error_data) > { > - const char *tmperrbuf; > char *reg_buf, *anchored_regex, *cp; > struct stem *stem_arr = data->stem_arr; > size_t len; > - int erroff; > + int rc; > > if (spec->regcomp) > return 0; /* already done */ > @@ -361,19 +348,9 @@ static inline int compile_regex(struct saved_data *data, struct spec *spec, > *cp = '\0'; > > /* Compile the regular expression. */ > - spec->regex = pcre_compile(anchored_regex, PCRE_DOTALL, &tmperrbuf, > - &erroff, NULL); > + rc = regex_prepare_data(&spec->regex, anchored_regex, error_data); > free(anchored_regex); > - if (!spec->regex) { > - if (errbuf) > - *errbuf = tmperrbuf; > - return -1; > - } > - > - spec->sd = pcre_study(spec->regex, 0, &tmperrbuf); > - if (!spec->sd && tmperrbuf) { > - if (errbuf) > - *errbuf = tmperrbuf; > + if (rc < 0) { > return -1; > } > > @@ -394,7 +371,8 @@ static inline int process_line(struct selabel_handle *rec, > struct saved_data *data = (struct saved_data *)rec->data; > struct spec *spec_arr; > unsigned int nspec = data->nspec; > - const char *errbuf = NULL; > + char const *errbuf; > + struct regex_error_data error_data; > > items = read_spec_entries(line_buf, &errbuf, 3, ®ex, &type, &context); > if (items < 0) { > @@ -454,7 +432,7 @@ static inline int process_line(struct selabel_handle *rec, > data->nspec++; > > if (rec->validating && > - compile_regex(data, &spec_arr[nspec], &errbuf)) { > + compile_regex(data, &spec_arr[nspec], &error_data)) { > COMPAT_LOG(SELINUX_ERROR, > "%s: line %u has invalid regex %s: %s\n", > path, lineno, regex, > diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c > new file mode 100644 > index 0000000..6b92b04 > --- /dev/null > +++ b/libselinux/src/regex.c > @@ -0,0 +1,405 @@ > +#include <assert.h> > +#include <stdint.h> > +#include <stdio.h> > +#include <string.h> > + > +#include "regex.h" > +#include "label_file.h" > + > +int regex_prepare_data(struct regex_data ** regex, char const * pattern_string, > + struct regex_error_data * errordata) { > + memset(errordata, 0, sizeof(struct regex_error_data)); > + *regex = regex_data_create(); > + if (!(*regex)) > + return -1; > +#ifdef USE_PCRE2 > + (*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string, > + PCRE2_ZERO_TERMINATED, > + PCRE2_DOTALL, > + &errordata->error_code, > + &errordata->error_offset, NULL); > +#else > + (*regex)->regex = pcre_compile(pattern_string, PCRE_DOTALL, > + &errordata->error_buffer, > + &errordata->error_offset, NULL); > +#endif > + if (!(*regex)->regex) { > + goto err; > + } > + > +#ifdef USE_PCRE2 > + (*regex)->match_data = > + pcre2_match_data_create_from_pattern((*regex)->regex, NULL); > + if (!(*regex)->match_data) { > + goto err; > + } > +#else > + (*regex)->sd = pcre_study((*regex)->regex, 0, &errordata->error_buffer); > + if (!(*regex)->sd && errordata->error_buffer) { > + goto err; > + } > + (*regex)->extra_owned = !!(*regex)->sd; > +#endif > + return 0; > + > +err: regex_data_free(*regex); > + *regex = NULL; > + return -1; > +} > + > +char const * regex_version(void) { > +#ifdef USE_PCRE2 > + static int initialized = 0; > + static char * version_string = NULL; > + size_t version_string_len; > + if (!initialized) { > + version_string_len = pcre2_config(PCRE2_CONFIG_VERSION, NULL); > + version_string = (char*) malloc(version_string_len); > + if (!version_string) { > + return NULL; > + } > + pcre2_config(PCRE2_CONFIG_VERSION, version_string); > + initialized = 1; > + } > + return version_string; > +#else > + return pcre_version(); > +#endif > +} > + > +int regex_load_mmap(struct mmap_area * mmap_area, struct regex_data ** regex) { > + int rc; > + size_t entry_len; > +#ifndef USE_PCRE2 > + size_t info_len; > +#endif > + > + rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); > +#ifdef USE_PCRE2 > + if (rc < 0) > + return -1; > + > +#ifndef NO_PERSISTENTLY_STORED_PATTERNS > + /* this should yield exactly one because we store one pattern at a time > + */ > + rc = pcre2_serialize_get_number_of_codes(mmap_area->next_addr); > + if (rc != 1) > + return -1; > + > + *regex = regex_data_create(); > + if (!*regex) > + return -1; > + > + rc = pcre2_serialize_decode(&(*regex)->regex, 1, > + (PCRE2_SPTR)mmap_area->next_addr, NULL); > + if (rc != 1) > + goto err; > + > + (*regex)->match_data = > + pcre2_match_data_create_from_pattern((*regex)->regex, NULL); > + if (!(*regex)->match_data) > + goto err; > + > +#endif /* NO_PERSISTENTLY_STORED_PATTERNS */ > + /* and skip the decoded bit */ > + rc = next_entry(NULL, mmap_area, entry_len); > + if (rc < 0) > + goto err; > + > + return 0; > +#else > + if (rc < 0 || !entry_len) { > + return -1; > + } > + *regex = regex_data_create(); > + if (!(*regex)) > + return -1; > + > + (*regex)->extra_owned = 0; > + (*regex)->regex = (pcre *) mmap_area->next_addr; > + rc = next_entry(NULL, mmap_area, entry_len); > + if (rc < 0) > + goto err; > + > + /* Check that regex lengths match. pcre_fullinfo() > + * also validates its magic number. */ > + rc = pcre_fullinfo((*regex)->regex, NULL, PCRE_INFO_SIZE, &info_len); > + if (rc < 0 || info_len != entry_len) { > + goto err; > + } > + > + rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); > + if (rc < 0 || !entry_len) { > + goto err; > + } > + > + if (entry_len) { > + (*regex)->lsd.study_data = (void *) mmap_area->next_addr; > + (*regex)->lsd.flags |= PCRE_EXTRA_STUDY_DATA; > + rc = next_entry(NULL, mmap_area, entry_len); > + if (rc < 0) > + goto err; > + > + /* Check that study data lengths match. */ > + rc = pcre_fullinfo((*regex)->regex, &(*regex)->lsd, > + PCRE_INFO_STUDYSIZE, &info_len); > + if (rc < 0 || info_len != entry_len) > + goto err; > + } > + return 0; > +#endif > +err: > + regex_data_free(*regex); > + *regex = NULL; > + return -1; > +} > + > +int regex_writef(struct regex_data * regex, FILE * fp) { > +#if !defined USE_PCRE2 || !defined NO_PERSISTENTLY_STORED_PATTERNS > + int rc; > +#endif > + size_t len; > +#ifdef USE_PCRE2 > + PCRE2_SIZE to_write; > +#ifndef NO_PERSISTENTLY_STORED_PATTERNS > + PCRE2_UCHAR * bytes; > + > + /* encode the patter for serialization */ > + rc = pcre2_serialize_encode((const pcre2_code **)®ex->regex, 1, > + &bytes, &to_write, NULL); > + if (rc != 1) > + return -1; > + > +#else > + (void)regex; // silence unused parameter warning > + to_write = 0; > +#endif > + /* write serialized pattern's size */ > + len = fwrite(&to_write, sizeof(uint32_t), 1, fp); > + if (len != 1) { > +#ifndef NO_PERSISTENTLY_STORED_PATTERNS > + pcre2_serialize_free(bytes); > +#endif > + return -1; > + } > + > +#ifndef NO_PERSISTENTLY_STORED_PATTERNS > + /* write serialized pattern */ > + len = fwrite(bytes, 1, to_write, fp); > + if (len != to_write) { > + pcre2_serialize_free(bytes); > + return -1; > + } > + pcre2_serialize_free(bytes); > +#endif > +#else > + uint32_t to_write; > + size_t size; > + pcre_extra * sd = regex->extra_owned ? regex->sd : > + (regex->lsd.study_data ? ®ex->lsd : NULL); > + > + /* determine the size of the pcre data in bytes */ > + rc = pcre_fullinfo(regex->regex, NULL, PCRE_INFO_SIZE, &size); > + if (rc < 0) > + return -1; > + > + /* write the number of bytes in the pcre data */ > + to_write = size; > + len = fwrite(&to_write, sizeof(uint32_t), 1, fp); > + if (len != 1) > + return -1; > + > + /* write the actual pcre data as a char array */ > + len = fwrite(regex->regex, 1, to_write, fp); > + if (len != to_write) > + return -1; > + > + if (sd) { > + /* determine the size of the pcre study info */ > + rc = pcre_fullinfo(regex->regex, sd, PCRE_INFO_STUDYSIZE, > + &size); > + if (rc < 0) > + return -1; > + } else > + size = 0; > + > + /* write the number of bytes in the pcre study data */ > + to_write = size; > + len = fwrite(&to_write, sizeof(uint32_t), 1, fp); > + if (len != 1) > + return -1; > + > + if (sd) { > + /* write the actual pcre study data as a char array */ > + len = fwrite(sd->study_data, 1, to_write, fp); > + if (len != to_write) > + return -1; > + } > +#endif > + return 0; > +} > + > +struct regex_data * regex_data_create(void) { > + struct regex_data * dummy = (struct regex_data*) malloc( > + sizeof(struct regex_data)); > + if (dummy) { > + memset(dummy, 0, sizeof(struct regex_data)); > + } > + return dummy; > +} > + > +void regex_data_free(struct regex_data * regex) { > + if (regex) { > +#ifdef USE_PCRE2 > + if (regex->regex) { > + pcre2_code_free(regex->regex); > + } > + if (regex->match_data) { > + pcre2_match_data_free(regex->match_data); > + } > +#else > + if (regex->regex) > + pcre_free(regex->regex); > + if (regex->extra_owned && regex->sd) { > + pcre_free_study(regex->sd); > + } > +#endif > + free(regex); > + } > +} > + > +int regex_match(struct regex_data * regex, char const * subject, int partial) { > + int rc; > +#ifdef USE_PCRE2 > + rc = pcre2_match(regex->regex, > + (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0, > + partial ? PCRE2_PARTIAL_SOFT : 0, regex->match_data, > + NULL); > + if (rc > 0) > + return REGEX_MATCH; > + switch (rc) { > + case PCRE2_ERROR_PARTIAL: > + return REGEX_MATCH_PARTIAL; > + case PCRE2_ERROR_NOMATCH: > + return REGEX_NO_MATCH; > + default: > + return REGEX_ERROR; > + } > +#else > + rc = pcre_exec(regex->regex, > + regex->extra_owned ? regex->sd : ®ex->lsd, subject, > + strlen(subject), 0, partial ? PCRE_PARTIAL_SOFT : 0, > + NULL, > + 0); > + switch (rc) { > + case 0: > + return REGEX_MATCH; > + case PCRE_ERROR_PARTIAL: > + return REGEX_MATCH_PARTIAL; > + case PCRE_ERROR_NOMATCH: > + return REGEX_NO_MATCH; > + default: > + return REGEX_ERROR; > + } > +#endif > +} > + > +/* TODO Replace this compare function with something that actually compares the > + * regular expressions. > + * This compare function basically just compares the binary representations of > + * the automatons, and because this representation contains pointers and > + * metadata, it can only return a match if regex1 == regex2. > + * Preferably, this function would be replaced with an algorithm that computes > + * the equivalence of the automatons systematically. > + */ > +int regex_cmp(struct regex_data * regex1, struct regex_data * regex2) { > + int rc; > + size_t len1, len2; > +#ifdef USE_PCRE2 > + rc = pcre2_pattern_info(regex1->regex, PCRE2_INFO_SIZE, &len1); > + assert(rc == 0); > + rc = pcre2_pattern_info(regex2->regex, PCRE2_INFO_SIZE, &len2); > + assert(rc == 0); > + if (len1 != len2 || memcmp(regex1->regex, regex2->regex, len1)) > + return SELABEL_INCOMPARABLE; > +#else > + rc = pcre_fullinfo(regex1->regex, NULL, PCRE_INFO_SIZE, &len1); > + assert(rc == 0); > + rc = pcre_fullinfo(regex2->regex, NULL, PCRE_INFO_SIZE, &len2); > + assert(rc == 0); > + if (len1 != len2 || memcmp(regex1->regex, regex2->regex, len1)) > + return SELABEL_INCOMPARABLE; > +#endif > + return SELABEL_EQUAL; > +} > + > +void regex_format_error(struct regex_error_data const * error_data, > + char * buffer, size_t buf_size) { > + unsigned the_end_length = buf_size > 4 ? 4 : buf_size; > + char * ptr = &buffer[buf_size - the_end_length]; > + int rc = 0; > + size_t pos = 0; > + if (!buffer || !buf_size) > + return; > + rc = snprintf(buffer, buf_size, "REGEX back-end error: "); > + if (rc < 0) > + /* If snprintf fails it constitutes a logical error that needs > + * fixing. > + */ > + abort(); > + > + pos += rc; > + if (pos >= buf_size) > + goto truncated; > + > + if (error_data->error_offset > 0) { > +#ifdef USE_PCRE2 > + rc = snprintf(buffer + pos, buf_size - pos, "At offset %zu: ", > + error_data->error_offset); > +#else > + rc = snprintf(buffer + pos, buf_size - pos, "At offset %d: ", > + error_data->error_offset); > +#endif > + if (rc < 0) > + abort(); > + > + } > + pos += rc; > + if (pos >= buf_size) > + goto truncated; > + > +#ifdef USE_PCRE2 > + rc = pcre2_get_error_message(error_data->error_code, > + (PCRE2_UCHAR*)(buffer + pos), > + buf_size - pos); > + if (rc == PCRE2_ERROR_NOMEMORY) > + goto truncated; > +#else > + rc = snprintf(buffer + pos, buf_size - pos, "%s", > + error_data->error_buffer); > + if (rc < 0) > + abort(); > + > + if ((size_t)rc < strlen(error_data->error_buffer)) > + goto truncated; > +#endif > + > + return; > + > +truncated: > + /* replace end of string with "..." to indicate that it was truncated */ > + switch (the_end_length) { > + /* no break statements, fall-through is intended */ > + case 4: > + *ptr++ = '.'; > + case 3: > + *ptr++ = '.'; > + case 2: > + *ptr++ = '.'; > + case 1: > + *ptr++ = '\0'; > + default: > + break; > + } > + return; > +} > diff --git a/libselinux/src/regex.h b/libselinux/src/regex.h > new file mode 100644 > index 0000000..bdc10c0 > --- /dev/null > +++ b/libselinux/src/regex.h > @@ -0,0 +1,168 @@ > +#ifndef SRC_REGEX_H_ > +#define SRC_REGEX_H_ > + > +#include <stdio.h> > + > +#ifdef USE_PCRE2 > +#include <pcre2.h> > +#else > +#include <pcre.h> > +#endif > + > +enum { > + REGEX_MATCH, > + REGEX_MATCH_PARTIAL, > + REGEX_NO_MATCH, > + REGEX_ERROR = -1, > +}; > + > +#ifdef USE_PCRE2 > +struct regex_data { > + pcre2_code * regex; /* compiled regular expression */ > + pcre2_match_data * match_data; /* match data block required for the compiled > + pattern in regex2 */ > +}; > + > +struct regex_error_data { > + int error_code; > + PCRE2_SIZE error_offset; > +}; > + > +/* ^^^^^^ USE_PCRE2 ^^^^^^ */ > +#else > +/* vvvvvv USE_PCRE vvvvvv */ > + > +/* Prior to version 8.20, libpcre did not have pcre_free_study() */ > +#if (PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20)) > +#define pcre_free_study pcre_free > +#endif > + > +struct regex_data { > + pcre *regex; /* compiled regular expression */ > + int extra_owned; /* non zero if pcre_extra is owned by this structure > + * and thus must be freed on destruction. > + */ > + union { > + pcre_extra *sd; /* pointer to extra compiled stuff */ > + pcre_extra lsd; /* used to hold the mmap'd version */ > + }; > +}; > + > +struct regex_error_data { > + char const * error_buffer; > + int error_offset; > +}; > + > +#endif /* USE_PCRE2 */ > + > +struct mmap_area; > + > +/** > + * regex_verison returns the version string of the underlying regular > + * regular expressions library. In the case of PCRE it just returns the > + * result of pcre_version(). In the case of PCRE2, the very first time this > + * function is called it allocates a buffer large enough to hold the version > + * string and reads the PCRE2_CONFIG_VERSION option to fill the buffer. > + * The allocated buffer will linger in memory until the calling process is being > + * reaped. > + * > + * It may return NULL on error. > + */ > +char const * regex_version(void); > +/** > + * This constructor function allocates a buffer for a regex_data structure. > + * The buffer is being initialized with zeroes. > + */ > +struct regex_data * regex_data_create(void); > +/** > + * This complementary destructor function frees the a given regex_data buffer. > + * It also frees any non NULL member pointers with the appropriate pcreX_X_free > + * function. For PCRE this function respects the extra_owned field and frees > + * the pcre_extra data conditionally. Calling this function on a NULL pointer is > + * save. > + */ > +void regex_data_free(struct regex_data * regex); > +/** > + * This function compiles the regular expression. Additionally, it prepares > + * data structures required by the different underlying engines. For PCRE > + * it calls pcre_study to generate optional data required for optimized > + * execution of the compiled pattern. In the case of PCRE2, it allocates > + * a pcre2_match_data structure of appropriate size to hold all possible > + * matches created by the pattern. > + * > + * @arg regex If successful, the structure returned through *regex was allocated > + * with regex_data_create and must be freed with regex_data_free. > + * @arg pattern_string The pattern string that is to be compiled. > + * @arg errordata A pointer to a regex_error_data structure must be passed > + * to this function. This structure depends on the underlying > + * implementation. It can be passed to regex_format_error > + * to generate a human readable error message. > + * @retval 0 on success > + * @retval -1 on error > + */ > +int regex_prepare_data(struct regex_data ** regex, char const * pattern_string, > + struct regex_error_data * errordata); > +/** > + * This function loads a serialized precompiled pattern from a contiguous > + * data region given by map_area. > + * > + * @arg map_area Description of the memory region holding a serialized > + * representation of the precompiled pattern. > + * @arg regex If successful, the structure returned through *regex was allocated > + * with regex_data_create and must be freed with regex_data_free. > + * > + * @retval 0 on success > + * @retval -1 on error > + */ > +int regex_load_mmap(struct mmap_area * map_area, struct regex_data ** regex); > +/** > + * This function stores a precompiled regular expression to a file. > + * In the case of PCRE, it just dumps the binary representation of the > + * precomplied pattern into a file. In the case of PCRE2, it uses the > + * serialization function provided by the library. > + * > + * @arg regex The precomplied regular expression data. > + * @arg fp A file stream specifying the output file. > + */ > +int regex_writef(struct regex_data * regex, FILE * fp); > +/** > + * This function applies a precompiled pattern to a subject string and > + * returns whether or not a match was found. > + * > + * @arg regex The precompiled pattern. > + * @arg subject The subject string. > + * @arg partial Boolean indicating if partial matches are wanted. A nonzero > + * value is equivalent to specifying PCRE[2]_PARTIAL_SOFT as > + * option to pcre_exec of pcre2_match. > + * @retval REGEX_MATCH if a match was found > + * @retval REGEX_MATCH_PARTIAL if a partial match was found > + * @retval REGEX_NO_MATCH if no match was found > + * @retval REGEX_ERROR if an error was encountered during the execution of the > + * regular expression > + */ > +int regex_match(struct regex_data * regex, char const * subject, int partial); > +/** > + * This function compares two compiled regular expressions (regex1 and regex2). > + * It compares the binary representations of the compiled patterns. It is a very > + * crude approximation because the binary representation holds data like > + * reference counters, that has nothing to do with the actual state machine. > + * > + * @retval SELABEL_EQUAL if the pattern's binary representations are exactly > + * the same > + * @retval SELABEL_INCOMPARABLE otherwise > + */ > +int regex_cmp(struct regex_data * regex1, struct regex_data * regex2); > +/** > + * This function takes the error data returned by regex_prepare_data and turns > + * it in to a human readable error message. > + * If the buffer given to hold the error message is to small it truncates the > + * message and indicates the truncation with an ellipsis ("...") at the end of > + * the buffer. > + * > + * @arg error_data Error data as returned by regex_prepare_data. > + * @arg buffer String buffer to hold the formated error string. > + * @arg buf_size Total size of the given bufer in bytes. > + */ > +void regex_format_error(struct regex_error_data const * error_data, > + char * buffer, size_t buf_size); > +#endif /* SRC_REGEX_H_ */ > diff --git a/libselinux/utils/Makefile b/libselinux/utils/Makefile > index 8497cb4..1e7a048 100644 > --- a/libselinux/utils/Makefile > +++ b/libselinux/utils/Makefile > @@ -24,12 +24,12 @@ CFLAGS ?= -O -Wall -W -Wundef -Wformat-y2k -Wformat-security -Winit-self -Wmissi > -fasynchronous-unwind-tables -fdiagnostics-show-option -funit-at-a-time \ > -fipa-pure-const -Wno-suggest-attribute=pure -Wno-suggest-attribute=const \ > -Werror -Wno-aggregate-return -Wno-redundant-decls > -override CFLAGS += -I../include -I$(INCLUDEDIR) -D_GNU_SOURCE $(EMFLAGS) > +override CFLAGS += -I../include -I$(INCLUDEDIR) -D_GNU_SOURCE $(EMFLAGS) $(PCRE_CFLAGS) > LDLIBS += -L../src -lselinux -L$(LIBDIR) > > TARGETS=$(patsubst %.c,%,$(wildcard *.c)) > > -sefcontext_compile: LDLIBS += -lpcre ../src/libselinux.a -lsepol > +sefcontext_compile: LDLIBS += $(PCRE_LDFLAGS) ../src/libselinux.a -lsepol > > selinux_restorecon: LDLIBS += -lsepol > > diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c > index fd6fb78..8ff73f4 100644 > --- a/libselinux/utils/sefcontext_compile.c > +++ b/libselinux/utils/sefcontext_compile.c > @@ -1,6 +1,5 @@ > #include <ctype.h> > #include <errno.h> > -#include <pcre.h> > #include <stdint.h> > #include <stdio.h> > #include <string.h> > @@ -13,6 +12,7 @@ > #include <sepol/sepol.h> > > #include "../src/label_file.h" > +#include "../src/regex.h" > > const char *policy_file; > static int ctx_err; > @@ -119,12 +119,14 @@ static int write_binary_file(struct saved_data *data, int fd) > if (len != 1) > goto err; > > - /* write the pcre version */ > - section_len = strlen(pcre_version()); > + /* write version of the regex back-end */ > + if (!regex_version()) > + goto err; > + section_len = strlen(regex_version()); > len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file); > if (len != 1) > goto err; > - len = fwrite(pcre_version(), sizeof(char), section_len, bin_file); > + len = fwrite(regex_version(), sizeof(char), section_len, bin_file); > if (len != section_len) > goto err; > > @@ -162,10 +164,8 @@ static int write_binary_file(struct saved_data *data, int fd) > mode_t mode = specs[i].mode; > size_t prefix_len = specs[i].prefix_len; > int32_t stem_id = specs[i].stem_id; > - pcre *re = specs[i].regex; > - pcre_extra *sd = get_pcre_extra(&specs[i]); > + struct regex_data *re = specs[i].regex; > uint32_t to_write; > - size_t size; > > /* length of the context string (including nul) */ > to_write = strlen(context) + 1; > @@ -212,42 +212,10 @@ static int write_binary_file(struct saved_data *data, int fd) > if (len != 1) > goto err; > > - /* determine the size of the pcre data in bytes */ > - rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size); > + /* Write regex related data */ > + rc = regex_writef(re, bin_file); > if (rc < 0) > goto err; > - > - /* write the number of bytes in the pcre data */ > - to_write = size; > - len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file); > - if (len != 1) > - goto err; > - > - /* write the actual pcre data as a char array */ > - len = fwrite(re, 1, to_write, bin_file); > - if (len != to_write) > - goto err; > - > - if (sd) { > - /* determine the size of the pcre study info */ > - rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size); > - if (rc < 0) > - goto err; > - } else > - size = 0; > - > - /* write the number of bytes in the pcre study data */ > - to_write = size; > - len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file); > - if (len != 1) > - goto err; > - > - if (sd) { > - /* write the actual pcre study data as a char array */ > - len = fwrite(sd->study_data, 1, to_write, bin_file); > - if (len != to_write) > - goto err; > - } > } > > rc = 0; > @@ -270,8 +238,7 @@ static void free_specs(struct saved_data *data) > free(specs[i].lr.ctx_trans); > free(specs[i].regex_str); > free(specs[i].type_str); > - pcre_free(specs[i].regex); > - pcre_free_study(specs[i].sd); > + regex_data_free(specs[i].regex); > } > free(specs); > > _______________________________________________ Selinux mailing list Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.