Skip to content

Commit

Permalink
libselinux: add build time option to drop hard dependency on libpcre2
Browse files Browse the repository at this point in the history
Currently libselinux links to libpcre2.  The regex library is used in
the file backend of the selabel database for file context path matching.

Some client applications using libselinux might not use that selabel
functionality, but they still require to load libpcre2.  Examples are
dbus-broker and sshd (where openssh only uses the selabel interfaces to
create ~/.ssh with the default context).

Add a build time option, USE_PCRE2_DLSYM, to drop the hard dependency on
libpcre2 and only load it, if actually needed, at runtime via dlopen(3).
Since loading the database for the file backend takes a couple of
milliseconds performance is not a concern.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
  • Loading branch information
cgzones committed Apr 4, 2024
1 parent ca15903 commit 8731a53
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 4 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'}
Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions libselinux/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 33 additions & 1 deletion libselinux/src/regex.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <string.h>

#include "regex.h"
#include "regex_dlsym.h"
#include "label_file.h"
#include "selinux_internal.h"

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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 **)&regex->regex,
Expand Down Expand Up @@ -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);
Expand All @@ -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(&regex->match_mutex);

#ifdef AGGRESSIVE_FREE_AFTER_REGEX_MATCH
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
/*
Expand Down
110 changes: 110 additions & 0 deletions libselinux/src/regex_dlsym.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include "regex_dlsym.h"

#ifdef USE_PCRE2_DLSYM

#include "callbacks.h"
#include "selinux_internal.h"

#include <dlfcn.h>
#include <pthread.h>


#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 */
65 changes: 65 additions & 0 deletions libselinux/src/regex_dlsym.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#ifndef LIBSELINUX_REGEX_DLSYM_H
#define LIBSELINUX_REGEX_DLSYM_H

#ifdef USE_PCRE2

#ifdef USE_PCRE2_DLSYM

#include <stdint.h>

#include <pcre2.h>


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 */

0 comments on commit 8731a53

Please sign in to comment.