Coverage Report

Created: 2026-09-14 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/shared/pcre2-util.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include "dlfcn-util.h"
4
#include "hash-funcs.h"
5
#include "log.h"
6
#include "pcre2-util.h"
7
8
#if HAVE_PCRE2
9
DLSYM_PROTOTYPE(pcre2_match_data_create) = NULL;
10
DLSYM_PROTOTYPE(pcre2_match_data_free) = NULL;
11
DLSYM_PROTOTYPE(pcre2_code_free) = NULL;
12
DLSYM_PROTOTYPE(pcre2_compile) = NULL;
13
DLSYM_PROTOTYPE(pcre2_get_error_message) = NULL;
14
DLSYM_PROTOTYPE(pcre2_match) = NULL;
15
DLSYM_PROTOTYPE(pcre2_get_ovector_pointer) = NULL;
16
17
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
18
        pcre2_code_hash_ops_free,
19
        pcre2_code,
20
        (void (*)(const pcre2_code *, struct siphash*))trivial_hash_func,
21
        (int (*)(const pcre2_code *, const pcre2_code*))trivial_compare_func,
22
        sym_pcre2_code_free);
23
#else
24
const struct hash_ops pcre2_code_hash_ops_free = {};
25
#endif
26
27
0
int dlopen_pcre2(int log_level) {
28
#if HAVE_PCRE2
29
        static void *pcre2_dl = NULL;
30
31
        LIBPCRE2_NOTE(suggested);
32
33
        /* So here's something weird: PCRE2 actually renames the symbols exported by the library via C
34
         * macros, so that the exported symbols carry a suffix "_8" but when used from C the suffix is
35
         * gone. In the argument list below we ignore this mangling. Surprisingly (at least to me), we
36
         * actually get away with that. That's because DLSYM_ARG() uses STRINGIFY() to generate a string
37
         * version of the symbol name, and that resolves the macro mapping implicitly already, so that the
38
         * string actually contains the "_8" suffix already due to that and we don't have to append it
39
         * manually anymore. C is weird. 🤯 */
40
41
        return dlopen_many_sym_or_warn(
42
                        &pcre2_dl, "libpcre2-8.so.0", log_level,
43
                        DLSYM_ARG(pcre2_match_data_create),
44
                        DLSYM_ARG(pcre2_match_data_free),
45
                        DLSYM_ARG(pcre2_code_free),
46
                        DLSYM_ARG(pcre2_compile),
47
                        DLSYM_ARG(pcre2_get_error_message),
48
                        DLSYM_ARG(pcre2_match),
49
                        DLSYM_ARG(pcre2_get_ovector_pointer));
50
#else
51
0
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
52
0
                              "PCRE2 support is not compiled in.");
53
0
#endif
54
0
}
55
56
716
int pattern_compile_and_log(const char *pattern, PatternCompileCase case_, pcre2_code **ret) {
57
#if HAVE_PCRE2
58
        PCRE2_SIZE erroroffset;
59
        _cleanup_(pcre2_code_freep) pcre2_code *p = NULL;
60
        unsigned flags = 0;
61
        int errorcode, r;
62
63
        assert(pattern);
64
65
        r = dlopen_pcre2(LOG_ERR);
66
        if (r < 0)
67
                return r;
68
69
        if (case_ == PATTERN_COMPILE_CASE_INSENSITIVE)
70
                flags = PCRE2_CASELESS;
71
        else if (case_ == PATTERN_COMPILE_CASE_AUTO) {
72
                _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
73
                bool has_case;
74
                _cleanup_(pcre2_code_freep) pcre2_code *cs = NULL;
75
76
                md = sym_pcre2_match_data_create(1, NULL);
77
                if (!md)
78
                        return log_oom();
79
80
                r = pattern_compile_and_log("[[:upper:]]", PATTERN_COMPILE_CASE_SENSITIVE, &cs);
81
                if (r < 0)
82
                        return r;
83
84
                r = sym_pcre2_match(cs, (PCRE2_SPTR8) pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
85
                has_case = r >= 0;
86
87
                flags = !has_case * PCRE2_CASELESS;
88
        }
89
90
        log_debug("Doing case %s matching based on %s",
91
                  flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
92
                  case_ != PATTERN_COMPILE_CASE_AUTO ? "request" : "pattern casing");
93
94
        p = sym_pcre2_compile((PCRE2_SPTR8) pattern,
95
                              PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
96
        if (!p) {
97
                unsigned char buf[LINE_MAX];
98
99
                r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
100
101
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
102
                                       "Bad pattern \"%s\": %s", pattern,
103
                                       r < 0 ? "unknown error" : (char *)buf);
104
        }
105
106
        if (ret)
107
                *ret = TAKE_PTR(p);
108
109
        return 0;
110
#else
111
716
        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
112
716
#endif
113
716
}
114
115
0
int pattern_matches_and_log(pcre2_code *compiled_pattern, const char *message, size_t size, size_t *ret_ovec) {
116
#if HAVE_PCRE2
117
        _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
118
        int r;
119
120
        assert(compiled_pattern);
121
        assert(message);
122
        /* pattern_compile_and_log() must be called before this function is called and that function already
123
         * dlopens pcre2 so we can assert on it being available here. */
124
        assert(sym_pcre2_match);
125
126
        md = sym_pcre2_match_data_create(1, NULL);
127
        if (!md)
128
                return log_oom();
129
130
        r = sym_pcre2_match(compiled_pattern,
131
                            (const unsigned char *)message,
132
                            size,
133
                            0,      /* start at offset 0 in the subject */
134
                            0,      /* default options */
135
                            md,
136
                            NULL);
137
        if (r == PCRE2_ERROR_NOMATCH)
138
                return false;
139
        if (r < 0) {
140
                unsigned char buf[LINE_MAX];
141
142
                r = sym_pcre2_get_error_message(r, buf, sizeof(buf));
143
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Pattern matching failed: %s",
144
                                       r < 0 ? "unknown error" : (char*) buf);
145
        }
146
147
        if (ret_ovec) {
148
                ret_ovec[0] = sym_pcre2_get_ovector_pointer(md)[0];
149
                ret_ovec[1] = sym_pcre2_get_ovector_pointer(md)[1];
150
        }
151
152
        return true;
153
#else
154
        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
155
0
#endif
156
0
}