Skip to content

Commit e97d775

Browse files
committed
libselinux: add build time option to drop hard dependency on libpcre2
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 <[email protected]>
1 parent b66ff27 commit e97d775

File tree

5 files changed

+220
-4
lines changed

5 files changed

+220
-4
lines changed

.github/workflows/run_tests.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ jobs:
1515
- {python: '3.12', ruby: '3.3', other: 'test-debug'}
1616
- {python: '3.12', ruby: '3.3', other: 'linker-bfd'}
1717
- {python: '3.12', ruby: '3.3', other: 'linker-gold'}
18-
# Test several Python versions with the latest Ruby version
18+
- {python: '3.12', ruby: '3.3', other: 'pcre2-dlsym'}
19+
# Test several Python versions with the latest Ruby version
1920
- {python: '3.11', ruby: '3.3'}
2021
- {python: '3.10', ruby: '3.3'}
2122
- {python: '3.9', ruby: '3.3'}
@@ -88,6 +89,8 @@ jobs:
8889
CC="$CC -fuse-ld=bfd"
8990
elif [ "${{ matrix.python-ruby-version.other }}" = "linker-gold" ] ; then
9091
CC="$CC -fuse-ld=gold"
92+
elif [ "${{ matrix.python-ruby-version.other }}" = "pcre2-dlsym" ] ; then
93+
echo "USE_PCRE2_DLSYM=y" >> $GITHUB_ENV
9194
fi
9295
# https://bugs.ruby-lang.org/issues/18616
9396
# https://github.com/llvm/llvm-project/issues/49958

libselinux/Makefile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,15 @@ endif
2424
export DISABLE_SETRANS DISABLE_RPM DISABLE_FLAGS ANDROID_HOST DISABLE_X11 LABEL_BACKEND_ANDROID
2525

2626
USE_PCRE2 ?= y
27+
USE_PCRE2_DLSYM ?= n
2728
ifeq ($(USE_PCRE2),y)
28-
PCRE_MODULE := libpcre2-8
29-
PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8
29+
ifeq ($(USE_PCRE2_DLSYM),n)
30+
PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8
31+
PCRE_MODULE := libpcre2-8
32+
else
33+
PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8 -DUSE_PCRE2_DLSYM
34+
PCRE_MODULE :=
35+
endif
3036
else
3137
PCRE_MODULE := libpcre
3238
endif

libselinux/src/regex.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <string.h>
66

77
#include "regex.h"
8+
#include "regex_dlsym.h"
89
#include "label_file.h"
910
#include "selinux_internal.h"
1011

@@ -81,6 +82,9 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
8182
{
8283
memset(errordata, 0, sizeof(struct regex_error_data));
8384

85+
if (regex_pcre2_load() < 0)
86+
return -1;
87+
8488
*regex = regex_data_create();
8589
if (!(*regex))
8690
return -1;
@@ -110,7 +114,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
110114
char const *regex_version(void)
111115
{
112116
static char version_buf[256];
113-
size_t len = pcre2_config(PCRE2_CONFIG_VERSION, NULL);
117+
size_t len;
118+
119+
if (regex_pcre2_load() < 0)
120+
return NULL;
121+
122+
len = pcre2_config(PCRE2_CONFIG_VERSION, NULL);
114123
if (len <= 0 || len > sizeof(version_buf))
115124
return NULL;
116125

@@ -125,6 +134,10 @@ int regex_load_mmap(struct mmap_area *mmap_area, struct regex_data **regex,
125134
uint32_t entry_len;
126135

127136
*regex_compiled = false;
137+
138+
if (regex_pcre2_load() < 0)
139+
return -1;
140+
128141
rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
129142
if (rc < 0)
130143
return -1;
@@ -178,6 +191,9 @@ int regex_writef(const struct regex_data *regex, FILE *fp, int do_write_precompr
178191
uint32_t to_write = 0;
179192
PCRE2_UCHAR *bytes = NULL;
180193

194+
if (regex_pcre2_load() < 0)
195+
return -1;
196+
181197
if (do_write_precompregex) {
182198
/* encode the pattern for serialization */
183199
rc = pcre2_serialize_encode((const pcre2_code **)&regex->regex,
@@ -212,6 +228,9 @@ int regex_writef(const struct regex_data *regex, FILE *fp, int do_write_precompr
212228

213229
void regex_data_free(struct regex_data *regex)
214230
{
231+
if (regex_pcre2_load() < 0)
232+
return;
233+
215234
if (regex) {
216235
if (regex->regex)
217236
pcre2_code_free(regex->regex);
@@ -230,6 +249,10 @@ int regex_match(struct regex_data *regex, char const *subject, int partial)
230249
{
231250
int rc;
232251
pcre2_match_data *match_data;
252+
253+
if (regex_pcre2_load() < 0)
254+
return REGEX_ERROR;
255+
233256
__pthread_mutex_lock(&regex->match_mutex);
234257

235258
#ifdef AGGRESSIVE_FREE_AFTER_REGEX_MATCH
@@ -279,6 +302,10 @@ int regex_cmp(const struct regex_data *regex1, const struct regex_data *regex2)
279302
{
280303
int rc;
281304
size_t len1, len2;
305+
306+
if (regex_pcre2_load() < 0)
307+
return SELABEL_INCOMPARABLE;
308+
282309
rc = pcre2_pattern_info(regex1->regex, PCRE2_INFO_SIZE, &len1);
283310
assert(rc == 0);
284311
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,
546573
size_t pos = 0;
547574
if (!buffer || !buf_size)
548575
return;
576+
#ifdef USE_PCRE2
577+
rc = regex_pcre2_load();
578+
if (rc < 0)
579+
return;
580+
#endif
549581
rc = snprintf(buffer, buf_size, "REGEX back-end error: ");
550582
if (rc < 0)
551583
/*

libselinux/src/regex_dlsym.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include "regex_dlsym.h"
2+
3+
#ifdef USE_PCRE2_DLSYM
4+
5+
#include "callbacks.h"
6+
#include "selinux_internal.h"
7+
8+
#include <dlfcn.h>
9+
#include <pthread.h>
10+
11+
12+
#define DLSYM_FUNC(symbol) typeof(symbol)* sym_##symbol = NULL
13+
14+
#define DLSYM_RESOLVE(handle, symbol) do { \
15+
sym_##symbol = dlsym(handle, #symbol); \
16+
if (!sym_##symbol) { \
17+
selinux_log(SELINUX_ERROR, "Failed to resolve symbol %s: %s\n", #symbol, dlerror()); \
18+
goto err; \
19+
} \
20+
} while(0)
21+
22+
DLSYM_FUNC(pcre2_code_free_8);
23+
DLSYM_FUNC(pcre2_compile_8);
24+
DLSYM_FUNC(pcre2_config_8);
25+
DLSYM_FUNC(pcre2_get_error_message_8);
26+
DLSYM_FUNC(pcre2_match_8);
27+
DLSYM_FUNC(pcre2_match_data_create_from_pattern_8);
28+
DLSYM_FUNC(pcre2_match_data_free_8);
29+
DLSYM_FUNC(pcre2_pattern_info_8);
30+
DLSYM_FUNC(pcre2_serialize_decode_8);
31+
DLSYM_FUNC(pcre2_serialize_encode_8);
32+
DLSYM_FUNC(pcre2_serialize_free_8);
33+
DLSYM_FUNC(pcre2_serialize_get_number_of_codes_8);
34+
35+
static void *libpcre2_handle = NULL;
36+
static pthread_mutex_t libpcre2_lock = PTHREAD_MUTEX_INITIALIZER;
37+
38+
39+
static void *load_impl(void) {
40+
void *handle;
41+
42+
handle = dlopen("libpcre2-8.so", RTLD_LAZY);
43+
if (!handle) {
44+
handle = dlopen("libpcre2-8.so.0", RTLD_LAZY);
45+
if (!handle) {
46+
selinux_log(SELINUX_ERROR, "Failed to load libpcre2-8: %s\n", dlerror());
47+
return NULL;
48+
}
49+
}
50+
51+
DLSYM_RESOLVE(handle, pcre2_code_free_8);
52+
DLSYM_RESOLVE(handle, pcre2_compile_8);
53+
DLSYM_RESOLVE(handle, pcre2_config_8);
54+
DLSYM_RESOLVE(handle, pcre2_get_error_message_8);
55+
DLSYM_RESOLVE(handle, pcre2_match_8);
56+
DLSYM_RESOLVE(handle, pcre2_match_data_create_from_pattern_8);
57+
DLSYM_RESOLVE(handle, pcre2_match_data_free_8);
58+
DLSYM_RESOLVE(handle, pcre2_pattern_info_8);
59+
DLSYM_RESOLVE(handle, pcre2_serialize_decode_8);
60+
DLSYM_RESOLVE(handle, pcre2_serialize_encode_8);
61+
DLSYM_RESOLVE(handle, pcre2_serialize_free_8);
62+
DLSYM_RESOLVE(handle, pcre2_serialize_get_number_of_codes_8);
63+
64+
return handle;
65+
66+
err:
67+
sym_pcre2_code_free_8 = NULL;
68+
sym_pcre2_compile_8 = NULL;
69+
sym_pcre2_config_8 = NULL;
70+
sym_pcre2_get_error_message_8 = NULL;
71+
sym_pcre2_match_8 = NULL;
72+
sym_pcre2_match_data_create_from_pattern_8 = NULL;
73+
sym_pcre2_match_data_free_8 = NULL;
74+
sym_pcre2_pattern_info_8 = NULL;
75+
sym_pcre2_serialize_decode_8 = NULL;
76+
sym_pcre2_serialize_encode_8 = NULL;
77+
sym_pcre2_serialize_free_8 = NULL;
78+
sym_pcre2_serialize_get_number_of_codes_8 = NULL;
79+
80+
if (handle)
81+
dlclose(handle);
82+
return NULL;
83+
}
84+
85+
int regex_pcre2_load(void) {
86+
void *handle;
87+
88+
handle = __atomic_load_n(&libpcre2_handle, __ATOMIC_ACQUIRE);
89+
if (handle)
90+
return 0;
91+
92+
__pthread_mutex_lock(&libpcre2_lock);
93+
94+
/* Check if another thread validated the context while we waited on the mutex */
95+
handle = __atomic_load_n(&libpcre2_handle, __ATOMIC_ACQUIRE);
96+
if (handle) {
97+
__pthread_mutex_unlock(&libpcre2_lock);
98+
return 0;
99+
}
100+
101+
handle = load_impl();
102+
if (handle)
103+
__atomic_store_n(&libpcre2_handle, handle, __ATOMIC_RELEASE);
104+
105+
__pthread_mutex_unlock(&libpcre2_lock);
106+
107+
return handle ? 0 : -1;
108+
}
109+
110+
#endif /* USE_PCRE2_DLSYM */

libselinux/src/regex_dlsym.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#ifndef LIBSELINUX_REGEX_DLSYM_H
2+
#define LIBSELINUX_REGEX_DLSYM_H
3+
4+
#ifdef USE_PCRE2
5+
6+
#ifdef USE_PCRE2_DLSYM
7+
8+
#include <stdint.h>
9+
10+
#include <pcre2.h>
11+
12+
13+
int regex_pcre2_load(void);
14+
15+
#define DLSYM_PROTO(symbol) extern typeof(symbol)* sym_##symbol
16+
DLSYM_PROTO(pcre2_code_free_8);
17+
DLSYM_PROTO(pcre2_compile_8);
18+
DLSYM_PROTO(pcre2_config_8);
19+
DLSYM_PROTO(pcre2_get_error_message_8);
20+
DLSYM_PROTO(pcre2_match_8);
21+
DLSYM_PROTO(pcre2_match_data_create_from_pattern_8);
22+
DLSYM_PROTO(pcre2_match_data_free_8);
23+
DLSYM_PROTO(pcre2_pattern_info_8);
24+
DLSYM_PROTO(pcre2_serialize_decode_8);
25+
DLSYM_PROTO(pcre2_serialize_encode_8);
26+
DLSYM_PROTO(pcre2_serialize_free_8);
27+
DLSYM_PROTO(pcre2_serialize_get_number_of_codes_8);
28+
#undef DLSYM_PROTO
29+
30+
#undef pcre2_code_free
31+
#define pcre2_code_free sym_pcre2_code_free_8
32+
#undef pcre2_compile
33+
#define pcre2_compile sym_pcre2_compile_8
34+
#undef pcre2_config
35+
#define pcre2_config sym_pcre2_config_8
36+
#undef pcre2_get_error_message
37+
#define pcre2_get_error_message sym_pcre2_get_error_message_8
38+
#undef pcre2_match
39+
#define pcre2_match sym_pcre2_match_8
40+
#undef pcre2_match_data_create_from_pattern
41+
#define pcre2_match_data_create_from_pattern sym_pcre2_match_data_create_from_pattern_8
42+
#undef pcre2_match_data_free
43+
#define pcre2_match_data_free sym_pcre2_match_data_free_8
44+
#undef pcre2_pattern_info
45+
#define pcre2_pattern_info sym_pcre2_pattern_info_8
46+
#undef pcre2_serialize_decode
47+
#define pcre2_serialize_decode sym_pcre2_serialize_decode_8
48+
#undef pcre2_serialize_encode
49+
#define pcre2_serialize_encode sym_pcre2_serialize_encode_8
50+
#undef pcre2_serialize_free
51+
#define pcre2_serialize_free sym_pcre2_serialize_free_8
52+
#undef pcre2_serialize_get_number_of_codes
53+
#define pcre2_serialize_get_number_of_codes sym_pcre2_serialize_get_number_of_codes_8
54+
55+
#else
56+
57+
static inline int regex_pcre2_load(void)
58+
{
59+
return 0;
60+
}
61+
62+
#endif /* USE_PCRE2_DLSYM */
63+
64+
#endif /* USE_PCRE2 */
65+
#endif /* LIBSELINUX_REGEX_DLSYM_H */

0 commit comments

Comments
 (0)