24RCSID(
"$Id: ecec9010400c9be79a070b6c6380a57e34b0e64e $")
28#include <freeradius-devel/util/regex.h>
29#include <freeradius-devel/util/atexit.h>
31#if defined(HAVE_REGEX_PCRE2) && defined(PCRE2_CONFIG_JIT)
32#ifndef FR_PCRE_JIT_STACK_MIN
33# define FR_PCRE_JIT_STACK_MIN (128 * 1024)
35#ifndef FR_PCRE_JIT_STACK_MAX
36# define FR_PCRE_JIT_STACK_MAX (512 * 1024)
77#ifdef HAVE_REGEX_PCRE2
91 TALLOC_CTX *alloc_ctx;
92 pcre2_general_context *gcontext;
93 pcre2_compile_context *ccontext;
94 pcre2_match_context *mcontext;
95#ifdef PCRE2_CONFIG_JIT
96 pcre2_jit_stack *jit_stack;
104static _Thread_local fr_pcre2_tls_t *fr_pcre2_tls;
111static void *_pcre2_talloc(PCRE2_SIZE to_alloc,
UNUSED void *uctx)
113 return talloc_array(fr_pcre2_tls->alloc_ctx,
uint8_t, to_alloc);
121static void _pcre2_talloc_free(
void *to_free,
UNUSED void *uctx)
130static int _pcre2_tls_free(fr_pcre2_tls_t *tls)
132 if (tls->gcontext) pcre2_general_context_free(tls->gcontext);
133 if (tls->ccontext) pcre2_compile_context_free(tls->ccontext);
134 if (tls->mcontext) pcre2_match_context_free(tls->mcontext);
135#ifdef PCRE2_CONFIG_JIT
136 if (tls->jit_stack) pcre2_jit_stack_free(tls->jit_stack);
142static int _pcre2_tls_free_on_exit(
void *arg)
150static int fr_pcre2_tls_init(
void)
154 if (
unlikely(fr_pcre2_tls != NULL))
return 0;
156 fr_pcre2_tls = tls = talloc_zero(NULL, fr_pcre2_tls_t);
158 talloc_set_destructor(tls, _pcre2_tls_free);
160 tls->gcontext = pcre2_general_context_create(_pcre2_talloc, _pcre2_talloc_free, NULL);
161 if (!tls->gcontext) {
166 tls->ccontext = pcre2_compile_context_create(tls->gcontext);
167 if (!tls->ccontext) {
171 _pcre2_tls_free(tls);
175 tls->mcontext = pcre2_match_context_create(tls->gcontext);
176 if (!tls->mcontext) {
181#ifdef PCRE2_CONFIG_JIT
182 pcre2_config(PCRE2_CONFIG_JIT, &tls->do_jit);
184 tls->jit_stack = pcre2_jit_stack_create(FR_PCRE_JIT_STACK_MIN, FR_PCRE_JIT_STACK_MAX, tls->gcontext);
185 if (!tls->jit_stack) {
189 pcre2_jit_stack_assign(tls->mcontext, NULL, tls->jit_stack);
208static int _regex_free(regex_t *preg)
210 if (preg->compiled) pcre2_code_free(preg->compiled);
235ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **
out,
char const *pattern,
size_t len,
236 fr_regex_flags_t
const *flags,
bool subcaptures,
bool runtime)
251 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
263 if (flags->ignore_case) cflags |= PCRE2_CASELESS;
264 if (flags->multiline) cflags |= PCRE2_MULTILINE;
265 if (flags->dot_all) cflags |= PCRE2_DOTALL;
266 if (flags->unicode) cflags |= PCRE2_UTF;
267 if (flags->extended) cflags |= PCRE2_EXTENDED;
270 if (!subcaptures) cflags |= PCRE2_NO_AUTO_CAPTURE;
272 preg = talloc_zero(ctx, regex_t);
273 talloc_set_destructor(preg, _regex_free);
275 preg->compiled = pcre2_compile((PCRE2_SPTR8)pattern, len,
276 cflags, &ret, &offset, fr_pcre2_tls->ccontext);
277 if (!preg->compiled) {
278 PCRE2_UCHAR errbuff[128];
280 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
288 preg->precompiled =
true;
290#ifdef PCRE2_CONFIG_JIT
296 if (fr_pcre2_tls->do_jit) {
297 ret = pcre2_jit_compile(preg->compiled, PCRE2_JIT_COMPLETE);
299 PCRE2_UCHAR errbuff[128];
301 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
328int regex_exec(regex_t *preg,
char const *subject,
size_t len, fr_regmatch_t *regmatch)
333 char *our_subject = NULL;
334 bool dup_subject =
true;
335 pcre2_match_data *match_data;
340 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
343#ifdef PCRE2_COPY_MATCHED_SUBJECT
348# ifdef PCRE2_CONFIG_JIT
365 options |= PCRE2_COPY_MATCHED_SUBJECT;
366# ifdef PCRE2_CONFIG_JIT
383 regmatch->subject = subject;
394 match_data = pcre2_match_data_create_from_pattern(preg->compiled, fr_pcre2_tls->gcontext);
400 match_data = regmatch->match_data;
403#ifdef PCRE2_CONFIG_JIT
405 ret = pcre2_jit_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
406 match_data, fr_pcre2_tls->mcontext);
410 ret = pcre2_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
411 match_data, fr_pcre2_tls->mcontext);
413 if (!regmatch) pcre2_match_data_free(match_data);
415 PCRE2_UCHAR errbuff[128];
419 if (ret == PCRE2_ERROR_NOMATCH) {
420 if (regmatch) regmatch->used = 0;
424 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
430 if (regmatch) regmatch->used = ret;
453int regex_substitute(TALLOC_CTX *ctx,
char **
out,
size_t max_out, regex_t *preg, fr_regex_flags_t
const *flags,
454 char const *subject,
size_t subject_len,
455 char const *replacement,
size_t replacement_len,
456 fr_regmatch_t *regmatch)
460 size_t buff_len, actual_len;
463#ifndef PCRE2_COPY_MATCHED_SUBJECT
464 char *our_subject = NULL;
470 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
478#ifndef PCRE2_COPY_MATCHED_SUBJECT
485 subject = our_subject =
talloc_bstrndup(regmatch, subject, subject_len);
503 options |= PCRE2_COPY_MATCHED_SUBJECT;
510 actual_len = buff_len = subject_len + 1;
511 buff = talloc_array(ctx,
char, buff_len);
513#ifndef PCRE2_COPY_MATCHED_SUBJECT
520 options |= PCRE2_SUBSTITUTE_OVERFLOW_LENGTH;
521 if (flags->global) options |= PCRE2_SUBSTITUTE_GLOBAL;
533 ret = pcre2_substitute(preg->compiled,
534 (PCRE2_SPTR8)subject, (PCRE2_SIZE)subject_len, 0,
535 options, NULL, fr_pcre2_tls->mcontext,
536 (PCRE2_UCHAR
const *)replacement, replacement_len, (PCRE2_UCHAR *)
buff, &actual_len);
539 PCRE2_UCHAR errbuff[128];
541#ifndef PCRE2_COPY_MATCHED_SUBJECT
546 if (ret == PCRE2_ERROR_NOMEMORY) {
547 if ((max_out > 0) && (actual_len > max_out)) {
549 "exceeds max string length (%zu)", actual_len - 1, max_out - 1);
557 if (actual_len == buff_len) {
561 buff_len = actual_len;
562 buff = talloc_array(ctx,
char, buff_len);
566 if (ret == PCRE2_ERROR_NOMATCH) {
567 if (regmatch) regmatch->used = 0;
571 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
582 if (actual_len < (buff_len - 1)) {
590 if (regmatch) regmatch->used = ret;
603uint32_t regex_subcapture_count(regex_t
const *preg)
607 if (pcre2_pattern_info(preg->compiled, PCRE2_INFO_CAPTURECOUNT, &
count) != 0) {
619static int _pcre2_match_data_free(fr_regmatch_t *regmatch)
621 pcre2_match_data_free(regmatch->match_data);
633fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx,
uint32_t count)
635 fr_regmatch_t *regmatch;
640 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return NULL;
642 regmatch = talloc(ctx, fr_regmatch_t);
649 regmatch->match_data = pcre2_match_data_create(
count, fr_pcre2_tls->gcontext);
650 if (!regmatch->match_data) {
654 talloc_set_type(regmatch->match_data, pcre2_match_data);
656 talloc_set_destructor(regmatch, _pcre2_match_data_free);
679static int _regex_free(regex_t *preg)
710ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **
out,
char const *pattern,
size_t len,
711 fr_regex_flags_t
const *flags,
bool subcaptures,
UNUSED bool runtime)
714 int cflags = REG_EXTENDED;
727 fr_strerror_const(
"g - Global matching/substitution not supported with posix-regex");
730 if (flags->dot_all) {
734 if (flags->unicode) {
738 if (flags->extended) {
743 if (flags->ignore_case) cflags |= REG_ICASE;
744 if (flags->multiline) cflags |= REG_NEWLINE;
748 if (!subcaptures) cflags |= REG_NOSUB;
755 p += strlen(pattern);
757 if ((
size_t)(p - pattern) != len) {
758 fr_strerror_printf(
"Found null in pattern at offset %zu. Pattern unsafe for compilation",
760 return -(p - pattern);
763 preg = talloc_zero(ctx, regex_t);
766 ret = regcomp(preg, pattern, cflags);
769 preg = talloc_zero(ctx, regex_t);
771 ret = regncomp(preg, pattern, len, cflags);
776 regerror(ret, preg, errbuf,
sizeof(errbuf));
784 talloc_set_destructor(preg, _regex_free);
806int regex_exec(regex_t *preg,
char const *subject,
size_t len, fr_regmatch_t *regmatch)
817 matches = regmatch->allocd;
822 memset(regmatch->match_data, 0,
sizeof(regmatch->match_data[0]) * matches);
831 p += strlen(subject);
833 if ((
size_t)(p - subject) != len) {
834 fr_strerror_printf(
"Found null in subject at offset %zu. String unsafe for evaluation",
836 if (regmatch) regmatch->used = 0;
839 ret = regexec(preg, subject, matches, regmatch ? regmatch->match_data : NULL, 0);
842 ret = regnexec(preg, subject, len, matches, regmatch ? regmatch->match_data : NULL, 0);
845 if (ret != REG_NOMATCH) {
848 regerror(ret, preg, errbuf,
sizeof(errbuf));
862 regmatch->used = preg->re_nsub + 1;
866 if (!regmatch->subject) {
885# if defined(HAVE_REGEX_POSIX)
894fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx,
uint32_t count)
896 fr_regmatch_t *regmatch;
909 regmatch->match_data = talloc_array(regmatch, regmatch_t,
count);
910 if (
unlikely(!regmatch->match_data))
goto error;
912 regmatch->allocd =
count;
914 regmatch->subject = NULL;
952#define DO_REGEX_FLAG(_f, _c) \
954 if (err_on_dup && out->_f) { \
955 fr_strerror_printf("Duplicate regex flag '%c'", *our_in.p); \
956 if (err) *err = -2; \
957 FR_SBUFF_ERROR_RETURN(&our_in); \
962 DO_REGEX_FLAG(global,
'g');
963 DO_REGEX_FLAG(ignore_case,
'i');
964 DO_REGEX_FLAG(multiline,
'm');
965 DO_REGEX_FLAG(dot_all,
's');
966 DO_REGEX_FLAG(unicode,
'u');
967 DO_REGEX_FLAG(extended,
'x');
994#define DO_REGEX_FLAG(_f, _c) \
995 if (flags->_f) FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, _c)
997 DO_REGEX_FLAG(global,
'g');
998 DO_REGEX_FLAG(ignore_case,
'i');
999 DO_REGEX_FLAG(multiline,
'm');
1000 DO_REGEX_FLAG(dot_all,
's');
1001 DO_REGEX_FLAG(unicode,
'u');
1002 DO_REGEX_FLAG(extended,
'x');
1028 TALLOC_CTX *ctx = NULL;
1031 regex_t *regex = NULL;
1044 if (!ctx)
return -1;
1051 if (slen < 0)
return slen;
1057 lhs = a->vb_strvalue;
1058 lhs_len = a->vb_length;
1061 if (regex_compile(ctx, ®ex, b->vb_strvalue, b->vb_length, NULL,
false,
true) < 0) {
1066#ifdef STATIC_ANALYZER
1073 rcode = regex_exec(regex, lhs, lhs_len, NULL);
1075 if (rcode < 0)
return rcode;
#define fr_atexit_thread_local(_name, _free, _uctx)
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_OCTETS
Raw octets.
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
#define fr_sbuff_extend(_sbuff_or_marker)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define SBUFF_CHAR_UNPRINTABLES_EXTENDED
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define SBUFF_CHAR_UNPRINTABLES_LOW
Set of terminal elements.
static char buff[sizeof("18446744073709551615")+3]
char * talloc_bstr_realloc(TALLOC_CTX *ctx, char *in, size_t inlen)
Trim a bstr (char) buffer.
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
#define talloc_zero_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
static int talloc_const_free(void const *ptr)
Free const'd memory.
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
int fr_regex_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two boxes using an operator.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_const(_msg)
static fr_slen_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules) 1(fr_value_box_print
static size_t char ** out