summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJanis Danisevskis <jdanis@google.com>2016-05-06 16:46:51 +0000
committerandroid-build-merger <android-build-merger@google.com>2016-05-06 16:46:51 +0000
commitef6d09c379f6dddd03fecc862c6ef80321fb8c94 (patch)
treebc4d94dc40817dab5cfa705370e5c6e63518d479
parentd474e5f2da4a25ca21029bb477bbe388291759eb (diff)
parentd2ebe5cfa7b39242bb7c2fb44feecedeef2aa370 (diff)
downloadlibselinux-ef6d09c379f6dddd03fecc862c6ef80321fb8c94.tar.gz
Port libselinux to pcre2 am: 31f5cd6e3e am: c9e48770b5
am: d2ebe5cfa7 * commit 'd2ebe5cfa7b39242bb7c2fb44feecedeef2aa370': Port libselinux to pcre2 Change-Id: I350e5fe32f4cd198b2316323d7c702972f2ddbab
-rw-r--r--Android.mk43
-rw-r--r--src/label_file.c90
-rw-r--r--src/label_file.h51
-rw-r--r--src/regex.c386
-rw-r--r--src/regex.h166
-rw-r--r--utils/sefcontext_compile.c47
6 files changed, 633 insertions, 150 deletions
diff --git a/Android.mk b/Android.mk
index 659232e..edc3b08 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,5 +1,23 @@
LOCAL_PATH:= $(call my-dir)
+# uncomment to build libselinux and related artifacts against PCRE2
+# common_USE_PCRE2 := 1
+
+ifeq ($(common_USE_PCRE2), 1)
+common_LIBRARIES := libpcre2
+common_C_INCLUDES :=
+common_CFLAGS := -DUSE_PCRE2
+
+# Persistently stored patterns (pcre2) are architecture dependent.
+# In particular paterns built on amd64 can not run on devices with armv7
+# (32bit). Therefore, this feature stays off for now.
+common_CFLAGS += -DNO_PERSISTENTLY_STORED_PATTERNS
+else
+common_LIBRARIES := libpcre
+common_C_INCLUDES := external/pcre
+common_CFLAGS :=
+endif
+
common_SRC_FILES := \
src/booleans.c \
src/canonicalize_context.c \
@@ -38,6 +56,7 @@ common_HOST_FILES := \
src/label.c \
src/label_file.c \
src/label_android_property.c \
+ src/regex.c \
src/label_support.c
@@ -47,9 +66,10 @@ LOCAL_MODULE:= libselinux
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libcrypto_static
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_WHOLE_STATIC_LIBRARIES := libpcre libpackagelistparser
+LOCAL_WHOLE_STATIC_LIBRARIES := $(common_LIBRARIES)
+LOCAL_WHOLE_STATIC_LIBRARIES += libpackagelistparser
# 1003 corresponds to auditd, from system/core/logd/event.logtags
-LOCAL_CFLAGS := -DAUDITD_LOG_TAG=1003
+LOCAL_CFLAGS := -DAUDITD_LOG_TAG=1003 $(common_CFLAGS)
# mapping.c has redundant check of array p_in->perms.
LOCAL_CLANG_CFLAGS += -Wno-pointer-bool-conversion
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
@@ -57,7 +77,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_CFLAGS := -DHOST
+LOCAL_CFLAGS := -DHOST $(common_CFLAGS)
ifeq ($(HOST_OS),darwin)
LOCAL_CFLAGS += -DDARWIN
@@ -66,7 +86,7 @@ endif
LOCAL_SRC_FILES := $(common_HOST_FILES)
LOCAL_MODULE:= libselinux
LOCAL_MODULE_TAGS := eng
-LOCAL_WHOLE_STATIC_LIBRARIES := libpcre
+LOCAL_WHOLE_STATIC_LIBRARIES := $(common_LIBRARIES)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -76,9 +96,10 @@ LOCAL_SRC_FILES := $(common_SRC_FILES) $(common_HOST_FILES) src/android.c
LOCAL_MODULE:= libselinux
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := libcrypto liblog libpcre libpackagelistparser
+LOCAL_SHARED_LIBRARIES := $(common_LIBRARIES)
+LOCAL_SHARED_LIBRARIES += libcrypto liblog libpackagelistparser
# 1003 corresponds to auditd, from system/core/logd/event.logtags
-LOCAL_CFLAGS := -DAUDITD_LOG_TAG=1003
+LOCAL_CFLAGS := -DAUDITD_LOG_TAG=1003 $(common_CFLAGS)
# mapping.c has redundant check of array p_in->perms.
LOCAL_CLANG_CFLAGS += -Wno-pointer-bool-conversion
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
@@ -86,7 +107,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_CFLAGS := -DHOST
+LOCAL_CFLAGS := -DHOST $(common_CFLAGS)
ifeq ($(HOST_OS),darwin)
LOCAL_CFLAGS += -DDARWIN
@@ -95,14 +116,14 @@ endif
LOCAL_SRC_FILES := $(common_HOST_FILES)
LOCAL_MODULE:= libselinux
LOCAL_MODULE_TAGS := eng
-LOCAL_WHOLE_STATIC_LIBRARIES := libpcre
+LOCAL_WHOLE_STATIC_LIBRARIES := $(common_LIBRARIES)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_HOST_SHARED_LIBRARY)
#################################
include $(CLEAR_VARS)
-LOCAL_CFLAGS := -DHOST
+LOCAL_CFLAGS := -DHOST $(common_CFLAGS)
ifeq ($(HOST_OS),darwin)
LOCAL_CFLAGS += -DDARWIN
@@ -113,6 +134,6 @@ LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES := ../src/label_file.h
LOCAL_SRC_FILES := utils/sefcontext_compile.c
LOCAL_STATIC_LIBRARIES := libselinux
-LOCAL_WHOLE_STATIC_LIBRARIES := libpcre
-LOCAL_C_INCLUDES := external/pcre
+LOCAL_WHOLE_STATIC_LIBRARIES := $(common_LIBRARIES)
+LOCAL_C_INCLUDES := $(common_C_INCLUDES)
include $(BUILD_HOST_EXECUTABLE)
diff --git a/src/label_file.c b/src/label_file.c
index 9d0a54c..d3e67c0 100644
--- a/src/label_file.c
+++ b/src/label_file.c
@@ -14,7 +14,6 @@
#include <errno.h>
#include <limits.h>
#include <stdint.h>
-#include <pcre.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
@@ -174,7 +173,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)
@@ -196,7 +198,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;
}
@@ -362,46 +364,18 @@ 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);
- 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;
- }
- spec->lsd.study_data = (void *)mmap_area->next_addr;
- spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA;
- rc = next_entry(NULL, mmap_area, entry_len);
+ rc = regex_load_mmap(mmap_area, &spec->regex);
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;
- }
+#ifndef NO_PERSISTENTLY_STORED_PATTERNS
+ spec->regcomp = 1;
+#else
+ spec->regcomp = 0;
+#endif
data->nspec++;
}
+
/* win */
rc = 0;
err:
@@ -569,10 +543,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++) {
@@ -604,13 +575,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;
@@ -637,9 +609,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.
@@ -652,25 +621,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;
@@ -810,16 +773,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/src/label_file.h b/src/label_file.h
index 901a3d9..d0ff254 100644
--- a/src/label_file.h
+++ b/src/label_file.h
@@ -6,6 +6,14 @@
#include <sys/stat.h>
+/*
+ * Android: regex.h/c was 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,14 +77,6 @@ struct saved_data {
struct mmap_area *mmap_areas;
};
-static inline pcre_extra *get_pcre_extra(struct spec *spec)
-{
- if (spec->from_mmap)
- return &spec->lsd;
- else
- return spec->sd;
-}
-
static inline mode_t string_to_mode(char *mode)
{
size_t len;
@@ -328,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 */
@@ -358,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;
}
@@ -391,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) {
@@ -451,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)) {
selinux_log(SELINUX_ERROR,
"%s: line %u has invalid regex %s: %s\n",
path, lineno, regex,
diff --git a/src/regex.c b/src/regex.c
new file mode 100644
index 0000000..7a0d7e9
--- /dev/null
+++ b/src/regex.c
@@ -0,0 +1,386 @@
+#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() {
+#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, info_len;
+
+ 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) {
+ rc = -1;
+ return -1;
+ }
+ *regex = regex_data_create();
+ if (!(*regex))
+ return -1;
+
+ (*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;
+ }
+ (*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;
+ }
+ (*regex)->extra_owned = 0;
+ return 0;
+#endif
+ err: regex_data_free(*regex);
+ *regex = NULL;
+ return -1;
+}
+
+int regex_writef(struct regex_data * regex, FILE * fp) {
+ int rc;
+ size_t len;
+#ifdef USE_PCRE2
+ PCRE2_UCHAR * bytes;
+ PCRE2_SIZE to_write;
+
+#ifndef NO_PERSISTENTLY_STORED_PATTERNS
+ /* encode the patter for serialization */
+ rc = pcre2_serialize_encode(&regex->regex, 1, &bytes, &to_write, NULL);
+ if (rc != 1)
+ return -1;
+
+#else
+ 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;
+
+ /* 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;
+
+ /* determine the size of the pcre study info */
+ rc = pcre_fullinfo(regex->regex, sd, PCRE_INFO_STUDYSIZE, &size);
+ if (rc < 0)
+ return -1;
+
+ /* 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;
+
+ /* 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() {
+ 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) {
+ buffer[0] = '\0';
+ return;
+ }
+ 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 %lu: ",
+ error_data->error_offset);
+#else
+ rc = snprintf(buffer + pos, buf_size - pos, "At offset %d: ",
+ error_data->error_offset);
+#endif
+ if (rc < 0) {
+ buffer[0] = '\0';
+ return;
+ }
+ }
+ 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 < 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;
+ }
+
+}
diff --git a/src/regex.h b/src/regex.h
new file mode 100644
index 0000000..7b74f32
--- /dev/null
+++ b/src/regex.h
@@ -0,0 +1,166 @@
+#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;
+ 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();
+/**
+ * This constructor function allocates a buffer for a regex_data structure.
+ * The buffer is being initialized with zeroes.
+ */
+struct regex_data * regex_data_create();
+/**
+ * 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/utils/sefcontext_compile.c b/utils/sefcontext_compile.c
index 4160632..f88c756 100644
--- a/utils/sefcontext_compile.c
+++ b/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>
@@ -12,6 +11,7 @@
#include <selinux/selinux.h>
#include "../src/label_file.h"
+#include "../src/regex.h"
static int validate_context(char __attribute__ ((unused)) **ctx)
{
@@ -101,12 +101,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;
@@ -144,8 +146,7 @@ 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;
@@ -194,37 +195,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);
- 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;
-
- /* determine the size of the pcre study info */
- rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size);
+ /* Write regex related data */
+ rc = regex_writef(re, bin_file);
if (rc < 0)
goto err;
-
- /* 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;
-
- /* 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;
@@ -247,8 +221,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);