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

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

 



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, &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 = (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, &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.



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

  Powered by Linux