diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 1bce96214..eda0c809f 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -15,7 +15,8 @@ jobs: - {python: '3.12', ruby: '3.3', other: 'test-debug'} - {python: '3.12', ruby: '3.3', other: 'linker-bfd'} - {python: '3.12', ruby: '3.3', other: 'linker-gold'} - # Test several Python versions with the latest Ruby version + - {python: '3.12', ruby: '3.3', other: 'pcre2-dlsym'} + # Test several Python versions with the latest Ruby version - {python: '3.11', ruby: '3.3'} - {python: '3.10', ruby: '3.3'} - {python: '3.9', ruby: '3.3'} @@ -88,6 +89,8 @@ jobs: CC="$CC -fuse-ld=bfd" elif [ "${{ matrix.python-ruby-version.other }}" = "linker-gold" ] ; then CC="$CC -fuse-ld=gold" + elif [ "${{ matrix.python-ruby-version.other }}" = "pcre2-dlsym" ] ; then + echo "USE_PCRE2_DLSYM=y" >> $GITHUB_ENV fi # https://bugs.ruby-lang.org/issues/18616 # https://github.com/llvm/llvm-project/issues/49958 diff --git a/libselinux/Makefile b/libselinux/Makefile index 6d9e27364..254e4b264 100644 --- a/libselinux/Makefile +++ b/libselinux/Makefile @@ -24,9 +24,15 @@ endif export DISABLE_SETRANS DISABLE_RPM DISABLE_FLAGS ANDROID_HOST DISABLE_X11 LABEL_BACKEND_ANDROID USE_PCRE2 ?= y +USE_PCRE2_DLSYM ?= n ifeq ($(USE_PCRE2),y) - PCRE_MODULE := libpcre2-8 - PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8 + ifeq ($(USE_PCRE2_DLSYM),n) + PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8 + PCRE_MODULE := libpcre2-8 + else + PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8 -DUSE_PCRE2_DLSYM + PCRE_MODULE := + endif else PCRE_MODULE := libpcre endif diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c index 69d40044c..49c2d4212 100644 --- a/libselinux/src/regex.c +++ b/libselinux/src/regex.c @@ -5,6 +5,7 @@ #include #include "regex.h" +#include "regex_dlsym.h" #include "label_file.h" #include "selinux_internal.h" @@ -81,6 +82,9 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string, { memset(errordata, 0, sizeof(struct regex_error_data)); + if (regex_pcre2_load() < 0) + return -1; + *regex = regex_data_create(); if (!(*regex)) return -1; @@ -110,7 +114,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string, char const *regex_version(void) { static char version_buf[256]; - size_t len = pcre2_config(PCRE2_CONFIG_VERSION, NULL); + size_t len; + + if (regex_pcre2_load() < 0) + return NULL; + + len = pcre2_config(PCRE2_CONFIG_VERSION, NULL); if (len <= 0 || len > sizeof(version_buf)) return NULL; @@ -125,6 +134,10 @@ int regex_load_mmap(struct mmap_area *mmap_area, struct regex_data **regex, uint32_t entry_len; *regex_compiled = false; + + if (regex_pcre2_load() < 0) + return -1; + rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); if (rc < 0) return -1; @@ -178,6 +191,9 @@ int regex_writef(const struct regex_data *regex, FILE *fp, int do_write_precompr uint32_t to_write = 0; PCRE2_UCHAR *bytes = NULL; + if (regex_pcre2_load() < 0) + return -1; + if (do_write_precompregex) { /* encode the pattern for serialization */ rc = pcre2_serialize_encode((const pcre2_code **)®ex->regex, @@ -212,6 +228,9 @@ int regex_writef(const struct regex_data *regex, FILE *fp, int do_write_precompr void regex_data_free(struct regex_data *regex) { + if (regex_pcre2_load() < 0) + return; + if (regex) { if (regex->regex) pcre2_code_free(regex->regex); @@ -230,6 +249,10 @@ int regex_match(struct regex_data *regex, char const *subject, int partial) { int rc; pcre2_match_data *match_data; + + if (regex_pcre2_load() < 0) + return REGEX_ERROR; + __pthread_mutex_lock(®ex->match_mutex); #ifdef AGGRESSIVE_FREE_AFTER_REGEX_MATCH @@ -279,6 +302,10 @@ int regex_cmp(const struct regex_data *regex1, const struct regex_data *regex2) { int rc; size_t len1, len2; + + if (regex_pcre2_load() < 0) + return SELABEL_INCOMPARABLE; + rc = pcre2_pattern_info(regex1->regex, PCRE2_INFO_SIZE, &len1); assert(rc == 0); rc = pcre2_pattern_info(regex2->regex, PCRE2_INFO_SIZE, &len2); @@ -546,6 +573,11 @@ void regex_format_error(struct regex_error_data const *error_data, char *buffer, size_t pos = 0; if (!buffer || !buf_size) return; +#ifdef USE_PCRE2 + rc = regex_pcre2_load(); + if (rc < 0) + return; +#endif rc = snprintf(buffer, buf_size, "REGEX back-end error: "); if (rc < 0) /* diff --git a/libselinux/src/regex_dlsym.c b/libselinux/src/regex_dlsym.c new file mode 100644 index 000000000..c20ad72dc --- /dev/null +++ b/libselinux/src/regex_dlsym.c @@ -0,0 +1,110 @@ +#include "regex_dlsym.h" + +#ifdef USE_PCRE2_DLSYM + +#include "callbacks.h" +#include "selinux_internal.h" + +#include +#include + + +#define DLSYM_FUNC(symbol) typeof(symbol)* sym_##symbol = NULL + +#define DLSYM_RESOLVE(handle, symbol) do { \ + sym_##symbol = dlsym(handle, #symbol); \ + if (!sym_##symbol) { \ + selinux_log(SELINUX_ERROR, "Failed to resolve symbol %s: %s\n", #symbol, dlerror()); \ + goto err; \ + } \ +} while(0) + +DLSYM_FUNC(pcre2_code_free_8); +DLSYM_FUNC(pcre2_compile_8); +DLSYM_FUNC(pcre2_config_8); +DLSYM_FUNC(pcre2_get_error_message_8); +DLSYM_FUNC(pcre2_match_8); +DLSYM_FUNC(pcre2_match_data_create_from_pattern_8); +DLSYM_FUNC(pcre2_match_data_free_8); +DLSYM_FUNC(pcre2_pattern_info_8); +DLSYM_FUNC(pcre2_serialize_decode_8); +DLSYM_FUNC(pcre2_serialize_encode_8); +DLSYM_FUNC(pcre2_serialize_free_8); +DLSYM_FUNC(pcre2_serialize_get_number_of_codes_8); + +static void *libpcre2_handle = NULL; +static pthread_mutex_t libpcre2_lock = PTHREAD_MUTEX_INITIALIZER; + + +static void *load_impl(void) { + void *handle; + + handle = dlopen("libpcre2-8.so", RTLD_LAZY); + if (!handle) { + handle = dlopen("libpcre2-8.so.0", RTLD_LAZY); + if (!handle) { + selinux_log(SELINUX_ERROR, "Failed to load libpcre2-8: %s\n", dlerror()); + return NULL; + } + } + + DLSYM_RESOLVE(handle, pcre2_code_free_8); + DLSYM_RESOLVE(handle, pcre2_compile_8); + DLSYM_RESOLVE(handle, pcre2_config_8); + DLSYM_RESOLVE(handle, pcre2_get_error_message_8); + DLSYM_RESOLVE(handle, pcre2_match_8); + DLSYM_RESOLVE(handle, pcre2_match_data_create_from_pattern_8); + DLSYM_RESOLVE(handle, pcre2_match_data_free_8); + DLSYM_RESOLVE(handle, pcre2_pattern_info_8); + DLSYM_RESOLVE(handle, pcre2_serialize_decode_8); + DLSYM_RESOLVE(handle, pcre2_serialize_encode_8); + DLSYM_RESOLVE(handle, pcre2_serialize_free_8); + DLSYM_RESOLVE(handle, pcre2_serialize_get_number_of_codes_8); + + return handle; + +err: + sym_pcre2_code_free_8 = NULL; + sym_pcre2_compile_8 = NULL; + sym_pcre2_config_8 = NULL; + sym_pcre2_get_error_message_8 = NULL; + sym_pcre2_match_8 = NULL; + sym_pcre2_match_data_create_from_pattern_8 = NULL; + sym_pcre2_match_data_free_8 = NULL; + sym_pcre2_pattern_info_8 = NULL; + sym_pcre2_serialize_decode_8 = NULL; + sym_pcre2_serialize_encode_8 = NULL; + sym_pcre2_serialize_free_8 = NULL; + sym_pcre2_serialize_get_number_of_codes_8 = NULL; + + if (handle) + dlclose(handle); + return NULL; +} + +int regex_pcre2_load(void) { + void *handle; + + handle = __atomic_load_n(&libpcre2_handle, __ATOMIC_ACQUIRE); + if (handle) + return 0; + + __pthread_mutex_lock(&libpcre2_lock); + + /* Check if another thread validated the context while we waited on the mutex */ + handle = __atomic_load_n(&libpcre2_handle, __ATOMIC_ACQUIRE); + if (handle) { + __pthread_mutex_unlock(&libpcre2_lock); + return 0; + } + + handle = load_impl(); + if (handle) + __atomic_store_n(&libpcre2_handle, handle, __ATOMIC_RELEASE); + + __pthread_mutex_unlock(&libpcre2_lock); + + return handle ? 0 : -1; +} + +#endif /* USE_PCRE2_DLSYM */ diff --git a/libselinux/src/regex_dlsym.h b/libselinux/src/regex_dlsym.h new file mode 100644 index 000000000..3f86246c3 --- /dev/null +++ b/libselinux/src/regex_dlsym.h @@ -0,0 +1,65 @@ +#ifndef LIBSELINUX_REGEX_DLSYM_H +#define LIBSELINUX_REGEX_DLSYM_H + +#ifdef USE_PCRE2 + +#ifdef USE_PCRE2_DLSYM + +#include + +#include + + +int regex_pcre2_load(void); + +#define DLSYM_PROTO(symbol) extern typeof(symbol)* sym_##symbol +DLSYM_PROTO(pcre2_code_free_8); +DLSYM_PROTO(pcre2_compile_8); +DLSYM_PROTO(pcre2_config_8); +DLSYM_PROTO(pcre2_get_error_message_8); +DLSYM_PROTO(pcre2_match_8); +DLSYM_PROTO(pcre2_match_data_create_from_pattern_8); +DLSYM_PROTO(pcre2_match_data_free_8); +DLSYM_PROTO(pcre2_pattern_info_8); +DLSYM_PROTO(pcre2_serialize_decode_8); +DLSYM_PROTO(pcre2_serialize_encode_8); +DLSYM_PROTO(pcre2_serialize_free_8); +DLSYM_PROTO(pcre2_serialize_get_number_of_codes_8); +#undef DLSYM_PROTO + +#undef pcre2_code_free +#define pcre2_code_free sym_pcre2_code_free_8 +#undef pcre2_compile +#define pcre2_compile sym_pcre2_compile_8 +#undef pcre2_config +#define pcre2_config sym_pcre2_config_8 +#undef pcre2_get_error_message +#define pcre2_get_error_message sym_pcre2_get_error_message_8 +#undef pcre2_match +#define pcre2_match sym_pcre2_match_8 +#undef pcre2_match_data_create_from_pattern +#define pcre2_match_data_create_from_pattern sym_pcre2_match_data_create_from_pattern_8 +#undef pcre2_match_data_free +#define pcre2_match_data_free sym_pcre2_match_data_free_8 +#undef pcre2_pattern_info +#define pcre2_pattern_info sym_pcre2_pattern_info_8 +#undef pcre2_serialize_decode +#define pcre2_serialize_decode sym_pcre2_serialize_decode_8 +#undef pcre2_serialize_encode +#define pcre2_serialize_encode sym_pcre2_serialize_encode_8 +#undef pcre2_serialize_free +#define pcre2_serialize_free sym_pcre2_serialize_free_8 +#undef pcre2_serialize_get_number_of_codes +#define pcre2_serialize_get_number_of_codes sym_pcre2_serialize_get_number_of_codes_8 + +#else + +static inline int regex_pcre2_load(void) +{ + return 0; +} + +#endif /* USE_PCRE2_DLSYM */ + +#endif /* USE_PCRE2 */ +#endif /* LIBSELINUX_REGEX_DLSYM_H */