Re: [PATCH 1/2] libselinux: add support for pcre2

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Ok, thanks. 

On Wed, Sep 7, 2016 at 4:37 PM William Roberts <bill.c.roberts@xxxxxxxxx> wrote:
On Wed, Sep 7, 2016 at 8:02 AM, Stephen Smalley <sds@xxxxxxxxxxxxx> wrote:
> 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

The leak patch was only applied and tested as part of the Android build,
I am OK with that one being squashed down, there is no point in it being
separate. I can send an email from my @intel once I get my VM back
online.

I would through ASAN options into CFLAGS and build with that, its a bit faster
than valgrind.

>
> 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 = "" 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, &regex_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 = "" 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, &regex, &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 **)&regex->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 ? &regex->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 : &regex->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(&section_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.



--
Respectfully,

William C Roberts
_______________________________________________
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.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux