24RCSID(
"$Id: da5e43f7b0dc78ce88065e87045c8e3ec6d6f523 $")
28#include <freeradius-devel/util/regex.h>
30#if defined(HAVE_REGEX_PCRE2) && defined(PCRE2_CONFIG_JIT)
31#ifndef FR_PCRE_JIT_STACK_MIN
32# define FR_PCRE_JIT_STACK_MIN (128 * 1024)
34#ifndef FR_PCRE_JIT_STACK_MAX
35# define FR_PCRE_JIT_STACK_MAX (512 * 1024)
76#ifdef HAVE_REGEX_PCRE2
90 TALLOC_CTX *alloc_ctx;
91 pcre2_general_context *gcontext;
92 pcre2_compile_context *ccontext;
93 pcre2_match_context *mcontext;
94#ifdef PCRE2_CONFIG_JIT
95 pcre2_jit_stack *jit_stack;
103static _Thread_local fr_pcre2_tls_t *fr_pcre2_tls;
110static void *_pcre2_talloc(PCRE2_SIZE to_alloc,
UNUSED void *uctx)
112 return talloc_array(fr_pcre2_tls->alloc_ctx,
uint8_t, to_alloc);
120static void _pcre2_talloc_free(
void *to_free,
UNUSED void *uctx)
129static int _pcre2_tls_free(fr_pcre2_tls_t *tls)
131 if (tls->gcontext) pcre2_general_context_free(tls->gcontext);
132 if (tls->ccontext) pcre2_compile_context_free(tls->ccontext);
133 if (tls->mcontext) pcre2_match_context_free(tls->mcontext);
134#ifdef PCRE2_CONFIG_JIT
135 if (tls->jit_stack) pcre2_jit_stack_free(tls->jit_stack);
141static int _pcre2_tls_free_on_exit(
void *arg)
149static int fr_pcre2_tls_init(
void)
153 if (
unlikely(fr_pcre2_tls != NULL))
return 0;
155 fr_pcre2_tls = tls = talloc_zero(NULL, fr_pcre2_tls_t);
157 talloc_set_destructor(tls, _pcre2_tls_free);
159 tls->gcontext = pcre2_general_context_create(_pcre2_talloc, _pcre2_talloc_free, NULL);
160 if (!tls->gcontext) {
165 tls->ccontext = pcre2_compile_context_create(tls->gcontext);
166 if (!tls->ccontext) {
170 _pcre2_tls_free(tls);
174 tls->mcontext = pcre2_match_context_create(tls->gcontext);
175 if (!tls->mcontext) {
180#ifdef PCRE2_CONFIG_JIT
181 pcre2_config(PCRE2_CONFIG_JIT, &tls->do_jit);
183 tls->jit_stack = pcre2_jit_stack_create(FR_PCRE_JIT_STACK_MIN, FR_PCRE_JIT_STACK_MAX, tls->gcontext);
184 if (!tls->jit_stack) {
188 pcre2_jit_stack_assign(tls->mcontext, NULL, tls->jit_stack);
207static int _regex_free(regex_t *preg)
209 if (preg->compiled) pcre2_code_free(preg->compiled);
234ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **
out,
char const *pattern,
size_t len,
235 fr_regex_flags_t
const *flags,
bool subcaptures,
bool runtime)
250 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
262 if (flags->ignore_case) cflags |= PCRE2_CASELESS;
263 if (flags->multiline) cflags |= PCRE2_MULTILINE;
264 if (flags->dot_all) cflags |= PCRE2_DOTALL;
265 if (flags->unicode) cflags |= PCRE2_UTF;
266 if (flags->extended) cflags |= PCRE2_EXTENDED;
269 if (!subcaptures) cflags |= PCRE2_NO_AUTO_CAPTURE;
271 preg = talloc_zero(ctx, regex_t);
272 talloc_set_destructor(preg, _regex_free);
274 preg->compiled = pcre2_compile((PCRE2_SPTR8)pattern, len,
275 cflags, &ret, &offset, fr_pcre2_tls->ccontext);
276 if (!preg->compiled) {
277 PCRE2_UCHAR errbuff[128];
279 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
287 preg->precompiled =
true;
289#ifdef PCRE2_CONFIG_JIT
295 if (fr_pcre2_tls->do_jit) {
296 ret = pcre2_jit_compile(preg->compiled, PCRE2_JIT_COMPLETE);
298 PCRE2_UCHAR errbuff[128];
300 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
327int regex_exec(regex_t *preg,
char const *subject,
size_t len, fr_regmatch_t *regmatch)
332 char *our_subject = NULL;
333 bool dup_subject =
true;
334 pcre2_match_data *match_data;
339 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
342#ifdef PCRE2_COPY_MATCHED_SUBJECT
347# ifdef PCRE2_CONFIG_JIT
364 options |= PCRE2_COPY_MATCHED_SUBJECT;
365# ifdef PCRE2_CONFIG_JIT
382 regmatch->subject = subject;
393 match_data = pcre2_match_data_create_from_pattern(preg->compiled, fr_pcre2_tls->gcontext);
399 match_data = regmatch->match_data;
402#ifdef PCRE2_CONFIG_JIT
404 ret = pcre2_jit_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
405 match_data, fr_pcre2_tls->mcontext);
409 ret = pcre2_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
410 match_data, fr_pcre2_tls->mcontext);
412 if (!regmatch) pcre2_match_data_free(match_data);
414 PCRE2_UCHAR errbuff[128];
418 if (ret == PCRE2_ERROR_NOMATCH) {
419 if (regmatch) regmatch->used = 0;
423 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
429 if (regmatch) regmatch->used = ret;
452int regex_substitute(TALLOC_CTX *ctx,
char **
out,
size_t max_out, regex_t *preg, fr_regex_flags_t
const *flags,
453 char const *subject,
size_t subject_len,
454 char const *replacement,
size_t replacement_len,
455 fr_regmatch_t *regmatch)
459 size_t buff_len, actual_len;
462#ifndef PCRE2_COPY_MATCHED_SUBJECT
463 char *our_subject = NULL;
469 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
477#ifndef PCRE2_COPY_MATCHED_SUBJECT
484 subject = our_subject =
talloc_bstrndup(regmatch, subject, subject_len);
502 options |= PCRE2_COPY_MATCHED_SUBJECT;
509 actual_len = buff_len = subject_len + 1;
510 buff = talloc_array(ctx,
char, buff_len);
512#ifndef PCRE2_COPY_MATCHED_SUBJECT
519 options |= PCRE2_SUBSTITUTE_OVERFLOW_LENGTH;
520 if (flags->global) options |= PCRE2_SUBSTITUTE_GLOBAL;
532 ret = pcre2_substitute(preg->compiled,
533 (PCRE2_SPTR8)subject, (PCRE2_SIZE)subject_len, 0,
534 options, NULL, fr_pcre2_tls->mcontext,
535 (PCRE2_UCHAR
const *)replacement, replacement_len, (PCRE2_UCHAR *)
buff, &actual_len);
538 PCRE2_UCHAR errbuff[128];
540#ifndef PCRE2_COPY_MATCHED_SUBJECT
545 if (ret == PCRE2_ERROR_NOMEMORY) {
546 if ((max_out > 0) && (actual_len > max_out)) {
548 "exceeds max string length (%zu)", actual_len - 1, max_out - 1);
556 if (actual_len == buff_len) {
560 buff_len = actual_len;
561 buff = talloc_array(ctx,
char, buff_len);
565 if (ret == PCRE2_ERROR_NOMATCH) {
566 if (regmatch) regmatch->used = 0;
570 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
581 if (actual_len < (buff_len - 1)) {
589 if (regmatch) regmatch->used = ret;
602uint32_t regex_subcapture_count(regex_t
const *preg)
606 if (pcre2_pattern_info(preg->compiled, PCRE2_INFO_CAPTURECOUNT, &
count) != 0) {
618static int _pcre2_match_data_free(fr_regmatch_t *regmatch)
620 pcre2_match_data_free(regmatch->match_data);
632fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx,
uint32_t count)
634 fr_regmatch_t *regmatch;
639 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return NULL;
641 regmatch = talloc(ctx, fr_regmatch_t);
648 regmatch->match_data = pcre2_match_data_create(
count, fr_pcre2_tls->gcontext);
649 if (!regmatch->match_data) {
653 talloc_set_type(regmatch->match_data, pcre2_match_data);
655 talloc_set_destructor(regmatch, _pcre2_match_data_free);
678static int _regex_free(regex_t *preg)
709ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **
out,
char const *pattern,
size_t len,
710 fr_regex_flags_t
const *flags,
bool subcaptures,
UNUSED bool runtime)
713 int cflags = REG_EXTENDED;
726 fr_strerror_const(
"g - Global matching/substitution not supported with posix-regex");
729 if (flags->dot_all) {
733 if (flags->unicode) {
737 if (flags->extended) {
742 if (flags->ignore_case) cflags |= REG_ICASE;
743 if (flags->multiline) cflags |= REG_NEWLINE;
747 if (!subcaptures) cflags |= REG_NOSUB;
754 p += strlen(pattern);
756 if ((
size_t)(p - pattern) != len) {
757 fr_strerror_printf(
"Found null in pattern at offset %zu. Pattern unsafe for compilation",
759 return -(p - pattern);
762 preg = talloc_zero(ctx, regex_t);
765 ret = regcomp(preg, pattern, cflags);
768 preg = talloc_zero(ctx, regex_t);
770 ret = regncomp(preg, pattern, len, cflags);
775 regerror(ret, preg, errbuf,
sizeof(errbuf));
783 talloc_set_destructor(preg, _regex_free);
805int regex_exec(regex_t *preg,
char const *subject,
size_t len, fr_regmatch_t *regmatch)
816 matches = regmatch->allocd;
821 memset(regmatch->match_data, 0,
sizeof(regmatch->match_data[0]) * matches);
830 p += strlen(subject);
832 if ((
size_t)(p - subject) != len) {
833 fr_strerror_printf(
"Found null in subject at offset %zu. String unsafe for evaluation",
835 if (regmatch) regmatch->used = 0;
838 ret = regexec(preg, subject, matches, regmatch ? regmatch->match_data : NULL, 0);
841 ret = regnexec(preg, subject, len, matches, regmatch ? regmatch->match_data : NULL, 0);
844 if (ret != REG_NOMATCH) {
847 regerror(ret, preg, errbuf,
sizeof(errbuf));
861 regmatch->used = preg->re_nsub + 1;
865 if (!regmatch->subject) {
884# if defined(HAVE_REGEX_POSIX)
893fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx,
uint32_t count)
895 fr_regmatch_t *regmatch;
908 regmatch->match_data = talloc_array(regmatch, regmatch_t,
count);
909 if (
unlikely(!regmatch->match_data))
goto error;
911 regmatch->allocd =
count;
913 regmatch->subject = NULL;
951#define DO_REGEX_FLAG(_f, _c) \
953 if (err_on_dup && out->_f) { \
954 fr_strerror_printf("Duplicate regex flag '%c'", *our_in.p); \
955 if (err) *err = -2; \
956 FR_SBUFF_ERROR_RETURN(&our_in); \
961 DO_REGEX_FLAG(global,
'g');
962 DO_REGEX_FLAG(ignore_case,
'i');
963 DO_REGEX_FLAG(multiline,
'm');
964 DO_REGEX_FLAG(dot_all,
's');
965 DO_REGEX_FLAG(unicode,
'u');
966 DO_REGEX_FLAG(extended,
'x');
993#define DO_REGEX_FLAG(_f, _c) \
994 if (flags->_f) FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, _c)
996 DO_REGEX_FLAG(global,
'g');
997 DO_REGEX_FLAG(ignore_case,
'i');
998 DO_REGEX_FLAG(multiline,
'm');
999 DO_REGEX_FLAG(dot_all,
's');
1000 DO_REGEX_FLAG(unicode,
'u');
1001 DO_REGEX_FLAG(extended,
'x');
1027 TALLOC_CTX *ctx = NULL;
1030 regex_t *regex = NULL;
1043 if (!ctx)
return -1;
1050 if (slen < 0)
return slen;
1056 lhs = a->vb_strvalue;
1057 lhs_len = a->vb_length;
1060 if (regex_compile(ctx, ®ex, b->vb_strvalue, b->vb_length, NULL,
false,
true) < 0) {
1065#ifdef STATIC_ANALYZER
1072 rcode = regex_exec(regex, lhs, lhs_len, NULL);
1074 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