X-Git-Url: http://www.privoxy.org/gitweb/templates.html?a=blobdiff_plain;f=pcrs.c;h=eaeea6255698086e119cc0bdcea021ffd86311dc;hb=19d7684ca10f6c1279568aa19e9a9da2276851f1;hp=a6c521af7e2a5173442b4c36540ade9bf2d3c2bc;hpb=c180f38183fb89876767ae57322d1792f8a632cb;p=privoxy.git diff --git a/pcrs.c b/pcrs.c index a6c521af..eaeea625 100644 --- a/pcrs.c +++ b/pcrs.c @@ -1,4 +1,3 @@ -const char pcrs_rcs[] = "$Id: pcrs.c,v 1.28 2007/08/18 14:37:27 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/pcrs.c,v $ @@ -16,172 +15,26 @@ const char pcrs_rcs[] = "$Id: pcrs.c,v 1.28 2007/08/18 14:37:27 fabiankeil Exp $ * * Copyright (C) 2006, 2007 Fabian Keil * - * This program is free software; you can redistribute it - * and/or modify it under the terms of the GNU Lesser - * General Public License (LGPL), version 2.1, which should - * be included in this distribution (see LICENSE.txt), with - * the exception that the permission to replace that license - * with the GNU General Public License (GPL) given in section - * 3 is restricted to version 2 of the GPL. + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. * * This program is distributed in the hope that it will * be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the license for more details. + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. * - * The GNU Lesser General Public License should be included - * with this file. If not, you can view it at - * http://www.gnu.org/licenses/lgpl.html + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html * or write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Revisions : - * $Log: pcrs.c,v $ - * Revision 1.28 2007/08/18 14:37:27 fabiankeil - * Ditch hex_to_byte() in favour of xtoi(). - * - * Revision 1.27 2007/08/05 13:47:04 fabiankeil - * #1763173 from Stefan Huehner: s@const static@static const@. - * - * Revision 1.26 2007/07/01 13:29:54 fabiankeil - * Add limited hex notation support for the PCRS - * substitution text ('\x7e' = '~'). Closes #1627140. - * - * Revision 1.25 2007/04/30 15:02:18 fabiankeil - * Introduce dynamic pcrs jobs that can resolve variables. - * - * Revision 1.24 2007/01/05 15:46:12 fabiankeil - * Don't use strlen() to calculate the length of - * the pcrs substitutes. They don't have to be valid C - * strings and getting their length wrong can result in - * user-controlled memory corruption. - * - * Thanks to Felix Gröbert for reporting the problem - * and providing the fix [#1627140]. - * - * Revision 1.23 2006/12/29 17:53:05 fabiankeil - * Fixed gcc43 conversion warnings. - * - * Revision 1.22 2006/12/24 17:34:20 fabiankeil - * Add pcrs_strerror() message for PCRE_ERROR_MATCHLIMIT - * and give a hint why an error code might be unknown. - * - * Catch NULL subjects early in pcrs_execute(). - * - * Revision 1.21 2006/07/18 14:48:47 david__schmidt - * Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch) - * with what was really the latest development (the v_3_0_branch branch) - * - * Revision 1.19.2.4 2005/05/07 21:50:55 david__schmidt - * A few memory leaks plugged (mostly on error paths) - * - * Revision 1.19.2.3 2003/12/04 12:32:45 oes - * Append a trailing nullbyte to result to facilitate string processing - * - * Revision 1.19.2.2 2002/10/08 16:22:28 oes - * Bugfix: Need to check validity of backreferences explicitly, - * because when max_matches are reached and matches is expanded, - * realloc() does not zero the memory. Fixes Bug # 606227 - * - * Revision 1.19.2.1 2002/08/10 11:23:40 oes - * Include prce.h via project.h, where the appropriate - * source will have been selected - * - * Revision 1.19 2002/03/08 14:47:48 oes - * Cosmetics - * - * Revision 1.18 2002/03/08 14:17:14 oes - * Fixing -Wconversion warnings - * - * Revision 1.17 2002/03/08 13:45:48 oes - * Hiding internal functions - * - * Revision 1.16 2001/11/30 21:32:14 jongfoster - * Fixing signed/unsigned comparison (Andreas please check this!) - * One tab->space - * - * Revision 1.15 2001/09/20 16:11:06 steudten - * - * Add casting for some string functions. - * - * Revision 1.14 2001/09/09 21:41:57 oes - * Fixing yet another silly bug - * - * Revision 1.13 2001/09/06 14:05:59 oes - * Fixed silly bug - * - * Revision 1.12 2001/08/18 11:35:00 oes - * - Introduced pcrs_strerror() - * - made some NULL arguments non-fatal - * - added support for \n \r \e \b \t \f \a \0 in substitute - * - made quoting adhere to standard rules - * - added warning for bad backrefs - * - added pcrs_execute_list() - * - fixed comments - * - bugfix & cosmetics - * - * Revision 1.11 2001/08/15 15:32:03 oes - * - Added support for Perl's special variables $+, $' and $` - * - Improved the substitute parser - * - Replaced the hard limit for the maximum number of matches - * by dynamic reallocation - * - * Revision 1.10 2001/08/05 13:13:11 jongfoster - * Making parameters "const" where possible. - * - * Revision 1.9 2001/07/18 17:27:00 oes - * Changed interface; Cosmetics - * - * Revision 1.8 2001/06/29 21:45:41 oes - * Indentation, CRLF->LF, Tab-> Space - * - * Revision 1.7 2001/06/29 13:33:04 oes - * - Cleaned up, renamed and reordered functions, - * improved comments - * - Removed my_strsep - * - Replaced globalflag with a general flags int - * that holds PCRS_GLOBAL, PCRS_SUCCESS, and PCRS_TRIVIAL - * - Introduced trivial option that will prevent pcrs - * from honouring backreferences in the substitute, - * which is useful for large substitutes that are - * red in from somewhere and saves the pain of escaping - * the backrefs - * - Introduced convenience function pcrs_free_joblist() - * - Split pcrs_make_job() into pcrs_compile(), which still - * takes a complete s/// comand as argument and parses it, - * and a new function pcrs_make_job, which takes the - * three separate components. This should make for a - * much friendlier frontend. - * - Removed create_pcrs_job() which was useless - * - Fixed a bug in pcrs_execute - * - Success flag is now handled by pcrs instead of user - * - * Revision 1.6 2001/06/03 19:12:45 oes - * added FIXME - * - * Revision 1.5 2001/05/29 09:50:24 jongfoster - * (Fixed one int -> size_t) - * - * Revision 1.4 2001/05/25 14:12:40 oes - * Fixed bug: Empty substitutes now detected - * - * Revision 1.3 2001/05/25 11:03:55 oes - * Added sanity check for NULL jobs to pcrs_exec_substitution - * - * Revision 1.2 2001/05/22 18:46:04 oes - * - * Added support for PCRE_UNGREEDY behaviour to pcrs, - * which is selected by the (nonstandard and therefore - * capital) letter 'U' in the option string. - * It causes the quantifiers to be ungreedy by default. - * Appending a ? turns back to greedy (!). - * - * Revision 1.1.1.1 2001/05/15 13:59:02 oes - * Initial import of version 2.9.3 source tree - * - * *********************************************************************/ - + #include #include @@ -200,13 +53,11 @@ const char pcrs_rcs[] = "$Id: pcrs.c,v 1.28 2007/08/18 14:37:27 fabiankeil Exp $ #include "pcrs.h" -const char pcrs_h_rcs[] = PCRS_H_VERSION; - /* * Internal prototypes */ -static int pcrs_parse_perl_options(const char *optstring, int *flags); +static int pcrs_parse_perl_options(const char *optstring, unsigned int *flags); static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int trivialflag, int capturecount, int *errptr); static int is_hex_sequence(const char *sequence); @@ -216,7 +67,7 @@ static int is_hex_sequence(const char *sequence); * Function : pcrs_strerror * * Description : Return a string describing a given error code. - * + * * Parameters : * 1 : error = the error code * @@ -225,30 +76,32 @@ static int is_hex_sequence(const char *sequence); *********************************************************************/ const char *pcrs_strerror(const int error) { - if (error < 0) + static char buf[100]; + + if (error != 0) { switch (error) { /* Passed-through PCRE error: */ - case PCRE_ERROR_NOMEMORY: return "(pcre:) No memory"; + case PCREn(ERROR_NOMEMORY): return "(pcre:) No memory"; /* Shouldn't happen unless PCRE or PCRS bug, or user messed with compiled job: */ - case PCRE_ERROR_NULL: return "(pcre:) NULL code or subject or ovector"; - case PCRE_ERROR_BADOPTION: return "(pcre:) Unrecognized option bit"; - case PCRE_ERROR_BADMAGIC: return "(pcre:) Bad magic number in code"; + case PCREn(ERROR_NULL): return "(pcre:) NULL code or subject or ovector"; + case PCREn(ERROR_BADOPTION): return "(pcre:) Unrecognized option bit"; + case PCREn(ERROR_BADMAGIC): return "(pcre:) Bad magic number in code"; +#if defined(PCRE_ERROR_UNKNOWN_NODE) case PCRE_ERROR_UNKNOWN_NODE: return "(pcre:) Bad node in pattern"; - +#endif /* Can't happen / not passed: */ - case PCRE_ERROR_NOSUBSTRING: return "(pcre:) Fire in power supply"; - case PCRE_ERROR_NOMATCH: return "(pcre:) Water in power supply"; + case PCREn(ERROR_NOSUBSTRING): return "(pcre:) Fire in power supply"; + case PCREn(ERROR_NOMATCH): return "(pcre:) Water in power supply"; #ifdef PCRE_ERROR_MATCHLIMIT /* * Only reported by PCRE versions newer than our own. */ - case PCRE_ERROR_MATCHLIMIT: return "(pcre:) Match limit reached"; + case PCREn(ERROR_MATCHLIMIT): return "(pcre:) Match limit reached"; #endif /* def PCRE_ERROR_MATCHLIMIT */ - /* PCRS errors: */ case PCRS_ERR_NOMEM: return "(pcrs:) No memory"; case PCRS_ERR_CMDSYNTAX: return "(pcrs:) Syntax error while parsing command"; @@ -258,13 +111,15 @@ const char *pcrs_strerror(const int error) case PCRS_WARN_TRUNCATION: return "(pcrs:) At least one variable was too big and has been truncated before compilation"; - /* - * XXX: With the exception of PCRE_ERROR_MATCHLIMIT we - * only catch PCRE errors that can happen with our internal - * version. If Privoxy is linked against a newer - * PCRE version all bets are off ... - */ - default: return "Unknown error. Privoxy out of sync with PCRE?"; + default: +#ifdef HAVE_PCRE2 + pcre2_get_error_message(error, (PCRE2_UCHAR8*)buf, sizeof(buf)); +#else + snprintf(buf, sizeof(buf), + "Error code %d. For details, check the pcre documentation.", + error); +#endif + return buf; } } /* error >= 0: No error */ @@ -284,15 +139,15 @@ const char *pcrs_strerror(const int error) * 'T' (trivial) options but pcrs needs them, the corresponding * flags are set if 'g'or 'T' is encountered. * Note: The 'T' and 'U' options do not conform to Perl. - * + * * Parameters : * 1 : optstring = string with options in perl syntax * 2 : flags = see description * - * Returns : option integer suitable for pcre + * Returns : option integer suitable for pcre * *********************************************************************/ -static int pcrs_parse_perl_options(const char *optstring, int *flags) +static int pcrs_parse_perl_options(const char *optstring, unsigned int *flags) { size_t i; int rc = 0; @@ -306,12 +161,13 @@ static int pcrs_parse_perl_options(const char *optstring, int *flags) { case 'e': break; /* ToDo ;-) */ case 'g': *flags |= PCRS_GLOBAL; break; - case 'i': rc |= PCRE_CASELESS; break; - case 'm': rc |= PCRE_MULTILINE; break; + case 'i': rc |= PCREn(CASELESS); break; + case 'm': rc |= PCREn(MULTILINE); break; case 'o': break; - case 's': rc |= PCRE_DOTALL; break; - case 'x': rc |= PCRE_EXTENDED; break; - case 'U': rc |= PCRE_UNGREEDY; break; + case 's': rc |= PCREn(DOTALL); break; + case 'x': rc |= PCREn(EXTENDED); break; + case 'D': *flags |= PCRS_DYNAMIC; break; + case 'U': rc |= PCREn(UNGREEDY); break; case 'T': *flags |= PCRS_TRIVIAL; break; default: break; } @@ -321,6 +177,38 @@ static int pcrs_parse_perl_options(const char *optstring, int *flags) } +#ifdef FUZZ +/********************************************************************* + * + * Function : pcrs_compile_fuzzed_replacement + * + * Description : Wrapper around pcrs_compile_replacement() for + * fuzzing purposes. + * + * Parameters : + * 1 : replacement = replacement part of s/// operator + * in perl syntax + * 2 : errptr = pointer to an integer in which error + * conditions can be returned. + * + * Returns : pcrs_substitute data structure, or NULL if an + * error is encountered. In that case, *errptr has + * the reason. + * + *********************************************************************/ +extern pcrs_substitute *pcrs_compile_fuzzed_replacement(const char *replacement, int *errptr) +{ + int capturecount = PCRS_MAX_SUBMATCHES; /* XXX: fuzzworthy? */ + int trivial_flag = 0; /* We don't want to fuzz strncpy() */ + + *errptr = 0; /* XXX: Should pcrs_compile_replacement() do this? */ + + return pcrs_compile_replacement(replacement, trivial_flag, capturecount, errptr); + +} +#endif + + /********************************************************************* * * Function : pcrs_compile_replacement @@ -348,10 +236,13 @@ static int pcrs_parse_perl_options(const char *optstring, int *flags) static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int trivialflag, int capturecount, int *errptr) { int i, k, l, quoted; - size_t length; char *text; pcrs_substitute *r; - +#ifndef FUZZ + size_t length; +#else + static size_t length; +#endif i = k = l = quoted = 0; /* @@ -381,14 +272,14 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr return NULL; } memset(text, '\0', length + 1); - + /* * In trivial mode, just copy the substitute text */ if (trivialflag) { - text = strncpy(text, replacement, length + 1); + strlcpy(text, replacement, length + 1); k = (int)length; } @@ -446,11 +337,11 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr */ const int ascii_value = xtoi(&replacement[i+2]); - assert(ascii_value > 0); + assert(ascii_value >= 0); assert(ascii_value < 256); text[k++] = (char)ascii_value; i += 4; - } + } else { quoted = 1; @@ -464,6 +355,13 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr if (replacement[i] == '$' && !quoted && i < (int)(length - 1)) { char *symbol, symbols[] = "'`+&"; + if (l >= PCRS_MAX_SUBMATCHES) + { + freez(text); + freez(r); + *errptr = PCRS_WARN_BADREF; + return NULL; + } r->block_length[l] = (size_t)(k - r->block_offset[l]); /* Numerical backreferences */ @@ -475,14 +373,17 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr } if (r->backref[l] > capturecount) { + freez(text); + freez(r); *errptr = PCRS_WARN_BADREF; + return NULL; } } /* Symbolic backreferences: */ else if (NULL != (symbol = strchr(symbols, replacement[i + 1]))) { - + if (symbol - symbols == 2) /* $+ */ { r->backref[l] = capturecount; @@ -493,7 +394,7 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr } else /* $' or $` */ { - r->backref[l] = PCRS_MAX_SUBMATCHES + 1 - (symbol - symbols); + r->backref[l] = (int)(PCRS_MAX_SUBMATCHES + 1 - (symbol - symbols)); } i += 2; } @@ -504,19 +405,25 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr goto plainchar; } + assert(r->backref[l] < PCRS_MAX_SUBMATCHES + 2); /* Valid and in range? -> record */ - if (r->backref[l] < PCRS_MAX_SUBMATCHES + 2) + if ((0 <= r->backref[l]) && + (r->backref[l] < PCRS_MAX_SUBMATCHES + 2) && + (l < PCRS_MAX_SUBMATCHES - 1)) { r->backref_count[r->backref[l]] += 1; r->block_offset[++l] = k; } else { + freez(text); + freez(r); *errptr = PCRS_WARN_BADREF; - } + return NULL; + } continue; } - + plainchar: /* Plain chars are copied */ text[k++] = replacement[i++]; @@ -542,13 +449,13 @@ plainchar: * Function : pcrs_free_job * * Description : Frees the memory used by a pcrs_job struct and its - * dependant structures. + * dependent structures. * * Parameters : * 1 : job = pointer to the pcrs_job structure to be freed * * Returns : a pointer to the next job, if there was any, or - * NULL otherwise. + * NULL otherwise. * *********************************************************************/ pcrs_job *pcrs_free_job(pcrs_job *job) @@ -562,8 +469,24 @@ pcrs_job *pcrs_free_job(pcrs_job *job) else { next = job->next; - if (job->pattern != NULL) free(job->pattern); - if (job->hints != NULL) free(job->hints); + if (job->pattern != NULL) + { +#ifdef HAVE_PCRE2 + pcre2_code_free(job->pattern); +#else + free(job->pattern); +#endif + } +#ifndef HAVE_PCRE2 + if (job->hints != NULL) + { +#ifdef PCRE_CONFIG_JIT + pcre_free_study(job->hints); +#else + free(job->hints); +#endif + } +#endif if (job->substitute != NULL) { if (job->substitute->text != NULL) free(job->substitute->text); @@ -592,7 +515,7 @@ pcrs_job *pcrs_free_job(pcrs_job *job) *********************************************************************/ void pcrs_free_joblist(pcrs_job *joblist) { - while ( NULL != (joblist = pcrs_free_job(joblist)) ) {}; + while (NULL != (joblist = pcrs_free_job(joblist))) {}; return; @@ -603,7 +526,7 @@ void pcrs_free_joblist(pcrs_job *joblist) * * Function : pcrs_compile_command * - * Description : Parses a string with a Perl-style s/// command, + * Description : Parses a string with a Perl-style s/// command, * calls pcrs_compile, and returns a corresponding * pcrs_job, or NULL if parsing or compiling the job * fails. @@ -623,11 +546,11 @@ pcrs_job *pcrs_compile_command(const char *command, int *errptr) int i, k, l, quoted = FALSE; size_t limit; char delimiter; - char *tokens[4]; + char *tokens[4]; pcrs_job *newjob; - - i = k = l = 0; - + + k = l = 0; + /* * Tokenize the perl command */ @@ -646,7 +569,7 @@ pcrs_job *pcrs_compile_command(const char *command, int *errptr) for (i = 0; i <= (int)limit; i++) { - + if (command[i] == delimiter && !quoted) { if (l == 3) @@ -658,7 +581,7 @@ pcrs_job *pcrs_compile_command(const char *command, int *errptr) tokens[++l] = tokens[0] + k; continue; } - + else if (command[i] == '\\' && !quoted) { quoted = TRUE; @@ -680,11 +603,11 @@ pcrs_job *pcrs_compile_command(const char *command, int *errptr) free(tokens[0]); return NULL; } - + newjob = pcrs_compile(tokens[1], tokens[2], tokens[3], errptr); free(tokens[0]); return newjob; - + } @@ -710,20 +633,25 @@ pcrs_job *pcrs_compile_command(const char *command, int *errptr) pcrs_job *pcrs_compile(const char *pattern, const char *substitute, const char *options, int *errptr) { pcrs_job *newjob; - int flags; + unsigned int flags; int capturecount; +#ifdef HAVE_PCRE2 + int ret; +#else + int pcre_study_options = 0; const char *error; +#endif *errptr = 0; - /* + /* * Handle NULL arguments */ if (pattern == NULL) pattern = ""; if (substitute == NULL) substitute = ""; - /* + /* * Get and init memory */ if (NULL == (newjob = (pcrs_job *)malloc(sizeof(pcrs_job)))) @@ -744,37 +672,70 @@ pcrs_job *pcrs_compile(const char *pattern, const char *substitute, const char * /* * Compile the pattern */ +#ifdef HAVE_PCRE2 + PCRE2_SIZE error_offset; + newjob->pattern = pcre2_compile((const unsigned char *)pattern, + PCRE2_ZERO_TERMINATED, (unsigned)newjob->options, errptr, + &error_offset, NULL); +#else newjob->pattern = pcre_compile(pattern, newjob->options, &error, errptr, NULL); +#endif if (newjob->pattern == NULL) { pcrs_free_job(newjob); return NULL; } +#if defined(PCRE_STUDY_JIT_COMPILE) || defined(HAVE_PCRE2) +#ifdef DISABLE_PCRE_JIT_COMPILATION +#warning PCRE_STUDY_JIT_COMPILE is supported but Privoxy has been configured not to use it +#else + if (!(flags & PCRS_DYNAMIC)) + { +#ifdef HAVE_PCRE2 + /* Try to enable JIT compilation but continue if it's unsupported. */ + if ((ret = pcre2_jit_compile(newjob->pattern, PCRE2_JIT_COMPLETE)) && + (ret != PCRE2_ERROR_JIT_BADOPTION)) + { + *errptr = ret; + pcrs_free_job(newjob); + return NULL; + } +#else + pcre_study_options = PCRE_STUDY_JIT_COMPILE; +#endif + } +#endif +#endif +#ifndef HAVE_PCRE2 /* * Generate hints. This has little overhead, since the * hints will be NULL for a boring pattern anyway. */ - newjob->hints = pcre_study(newjob->pattern, 0, &error); + newjob->hints = pcre_study(newjob->pattern, pcre_study_options, &error); if (error != NULL) { *errptr = PCRS_ERR_STUDY; pcrs_free_job(newjob); return NULL; } - +#endif - /* - * Determine the number of capturing subpatterns. + /* + * Determine the number of capturing subpatterns. * This is needed for handling $+ in the substitute. */ +#ifdef HAVE_PCRE2 + if (0 > (*errptr = pcre2_pattern_info(newjob->pattern, PCRE2_INFO_CAPTURECOUNT, &capturecount))) +#else if (0 > (*errptr = pcre_fullinfo(newjob->pattern, newjob->hints, PCRE_INFO_CAPTURECOUNT, &capturecount))) +#endif { pcrs_free_job(newjob); return NULL; } - + /* * Compile the substitute @@ -784,7 +745,7 @@ pcrs_job *pcrs_compile(const char *pattern, const char *substitute, const char * pcrs_free_job(newjob); return NULL; } - + return newjob; } @@ -799,7 +760,7 @@ pcrs_job *pcrs_compile(const char *pattern, const char *substitute, const char * * the joblist to the subject. * The subject itself is left untouched, memory for the result * is malloc()ed and it is the caller's responsibility to free - * the result when it's no longer needed. + * the result when it's no longer needed. * * Note: For convenient string handling, a null byte is * appended to the result. It does not count towards the @@ -809,8 +770,8 @@ pcrs_job *pcrs_compile(const char *pattern, const char *substitute, const char * * Parameters : * 1 : joblist = the chained list of pcrs_jobs to be executed * 2 : subject = the subject string - * 3 : subject_length = the subject's length - * 4 : result = char** for returning the result + * 3 : subject_length = the subject's length + * 4 : result = char** for returning the result * 5 : result_length = size_t* for returning the result's length * * Returns : On success, the number of substitutions that were made. @@ -824,10 +785,10 @@ int pcrs_execute_list(pcrs_job *joblist, char *subject, size_t subject_length, c pcrs_job *job; char *old, *new = NULL; int hits, total_hits; - + old = subject; *result_length = subject_length; - hits = total_hits = 0; + total_hits = 0; for (job = joblist; job != NULL; job = job->next) { @@ -869,8 +830,8 @@ int pcrs_execute_list(pcrs_job *joblist, char *subject, size_t subject_length, c * Parameters : * 1 : job = the pcrs_job to be executed * 2 : subject = the subject (== original) string - * 3 : subject_length = the subject's length - * 4 : result = char** for returning the result + * 3 : subject_length = the subject's length + * 4 : result = char** for returning the result (NULL on error) * 5 : result_length = size_t* for returning the result's length * * Returns : On success, the number of substitutions that were made. @@ -881,58 +842,74 @@ int pcrs_execute_list(pcrs_job *joblist, char *subject, size_t subject_length, c *********************************************************************/ int pcrs_execute(pcrs_job *job, const char *subject, size_t subject_length, char **result, size_t *result_length) { - int offsets[3 * PCRS_MAX_SUBMATCHES], - offset, + int offset, i, k, matches_found, submatches, max_matches = PCRS_MAX_MATCH_INIT; size_t newsize; +#ifdef HAVE_PCRE2 + pcrs_match *matches, *dummy; + pcre2_match_data *pcre2_matches; + size_t *offsets; +#else pcrs_match *matches, *dummy; + int offsets[3 * PCRS_MAX_SUBMATCHES]; +#endif char *result_offset; - offset = i = k = 0; + offset = i = 0; + *result = NULL; - /* + /* * Sanity check & memory allocation */ if (job == NULL || job->pattern == NULL || job->substitute == NULL || NULL == subject) { - *result = NULL; return(PCRS_ERR_BADJOB); } +#ifdef HAVE_PCRE2 + if (NULL == (pcre2_matches = pcre2_match_data_create_from_pattern(job->pattern, NULL))) + { + return(PCRS_ERR_NOMEM); + } + offsets = pcre2_get_ovector_pointer(pcre2_matches); +#endif if (NULL == (matches = (pcrs_match *)malloc((size_t)max_matches * sizeof(pcrs_match)))) { - *result = NULL; return(PCRS_ERR_NOMEM); } memset(matches, '\0', (size_t)max_matches * sizeof(pcrs_match)); - /* * Find the pattern and calculate the space * requirements for the result */ newsize = subject_length; +#ifdef HAVE_PCRE2 + while ((submatches = pcre2_match(job->pattern, (const unsigned char *)subject, + subject_length, (size_t)offset, 0, pcre2_matches, NULL)) > 0) +#else while ((submatches = pcre_exec(job->pattern, job->hints, subject, (int)subject_length, offset, 0, offsets, 3 * PCRS_MAX_SUBMATCHES)) > 0) +#endif { job->flags |= PCRS_SUCCESS; matches[i].submatches = submatches; for (k = 0; k < submatches; k++) { - matches[i].submatch_offset[k] = offsets[2 * k]; + matches[i].submatch_offset[k] = (int)offsets[2 * k]; /* Note: Non-found optional submatches have length -1-(-1)==0 */ - matches[i].submatch_length[k] = (size_t)(offsets[2 * k + 1] - offsets[2 * k]); + matches[i].submatch_length[k] = (size_t)(offsets[2 * k + 1] - offsets[2 * k]); /* reserve mem for each submatch as often as it is ref'd */ newsize += matches[i].submatch_length[k] * (size_t)job->substitute->backref_count[k]; } /* plus replacement text size minus match text size */ - newsize += job->substitute->length - matches[i].submatch_length[0]; + newsize += job->substitute->length - matches[i].submatch_length[0]; /* chunk before match */ matches[i].submatch_offset[PCRS_MAX_SUBMATCHES] = 0; @@ -940,7 +917,7 @@ int pcrs_execute(pcrs_job *job, const char *subject, size_t subject_length, char newsize += (size_t)offsets[0] * (size_t)job->substitute->backref_count[PCRS_MAX_SUBMATCHES]; /* chunk after match */ - matches[i].submatch_offset[PCRS_MAX_SUBMATCHES + 1] = offsets[1]; + matches[i].submatch_offset[PCRS_MAX_SUBMATCHES + 1] = (int)offsets[1]; matches[i].submatch_length[PCRS_MAX_SUBMATCHES + 1] = subject_length - (size_t)offsets[1] - 1; newsize += (subject_length - (size_t)offsets[1]) * (size_t)job->substitute->backref_count[PCRS_MAX_SUBMATCHES + 1]; @@ -951,7 +928,6 @@ int pcrs_execute(pcrs_job *job, const char *subject, size_t subject_length, char if (NULL == (dummy = (pcrs_match *)realloc(matches, (size_t)max_matches * sizeof(pcrs_match)))) { free(matches); - *result = NULL; return(PCRS_ERR_NOMEM); } matches = dummy; @@ -968,24 +944,41 @@ int pcrs_execute(pcrs_job *job, const char *subject, size_t subject_length, char break; /* Go find the next one */ else - offset = offsets[1]; + offset = (int)offsets[1]; } - /* Pass pcre error through if (bad) failiure */ + /* Pass pcre error through if (bad) failure */ +#ifdef HAVE_PCRE2 + if (submatches < PCRE2_ERROR_NOMATCH) +#else if (submatches < PCRE_ERROR_NOMATCH) +#endif { free(matches); - return submatches; +#ifdef HAVE_PCRE2 + pcre2_match_data_free(pcre2_matches); +#endif + return submatches; } matches_found = i; - /* + /* * Get memory for the result (must be freed by caller!) * and append terminating null byte. */ - if ((*result = (char *)malloc(newsize + 1)) == NULL) + if ((*result = (char *)malloc(newsize + 1 +#ifdef HAVE_PCRE2 + /* + * Work around to prevent invalid reads in the jit code. + */ + + 16 +#endif + )) == NULL) { free(matches); +#ifdef HAVE_PCRE2 + pcre2_match_data_free(pcre2_matches); +#endif return PCRS_ERR_NOMEM; } else @@ -994,7 +987,7 @@ int pcrs_execute(pcrs_job *job, const char *subject, size_t subject_length, char } - /* + /* * Replace */ offset = 0; @@ -1003,7 +996,7 @@ int pcrs_execute(pcrs_job *job, const char *subject, size_t subject_length, char for (i = 0; i < matches_found; i++) { /* copy the chunk preceding the match */ - memcpy(result_offset, subject + offset, (size_t)(matches[i].submatch_offset[0] - offset)); + memcpy(result_offset, subject + offset, (size_t)(matches[i].submatch_offset[0] - offset)); result_offset += matches[i].submatch_offset[0] - offset; /* For every segment of the substitute.. */ @@ -1038,6 +1031,9 @@ int pcrs_execute(pcrs_job *job, const char *subject, size_t subject_length, char memcpy(result_offset, subject + offset, subject_length - (size_t)offset); *result_length = newsize; +#ifdef HAVE_PCRE2 + pcre2_match_data_free(pcre2_matches); +#endif free(matches); return matches_found; @@ -1089,7 +1085,7 @@ static int is_hex_sequence(const char *sequence) * FALSE * *********************************************************************/ -int pcrs_job_is_dynamic (char *job) +int pcrs_job_is_dynamic(char *job) { const char delimiter = job[1]; const size_t length = strlen(job); @@ -1133,7 +1129,7 @@ int pcrs_job_is_dynamic (char *job) * Parameters : * 1 : string = The string to search in * - * Returns : A safe delimiter if one was found, otherwise '\0'. + * Returns : A safe delimiter if one was found, otherwise '\0'. * *********************************************************************/ char pcrs_get_delimiter(const char *string) @@ -1142,8 +1138,8 @@ char pcrs_get_delimiter(const char *string) * Some characters that are unlikely to * be part of pcrs replacement strings. */ - char delimiters[] = "><§#+*~%^°-:;µ!@"; - char *d = delimiters; + static const char delimiters[] = "><#+*~%^-:;!@"; + const char *d = delimiters; /* Take the first delimiter that isn't part of the string */ while (*d && NULL != strchr(string, *d)) @@ -1166,16 +1162,16 @@ char pcrs_get_delimiter(const char *string) * * Parameters : * 1 : subject = the subject (== original) string - * 2 : pcrs_command = the pcrs command as string (s@foo@bar@) - * 3 : hits = int* for returning the number of modifications + * 2 : pcrs_command = the pcrs command as string (s@foo@bar@) + * 3 : hits = int* for returning the number of modifications * * Returns : NULL in case of errors, otherwise the - * result of the pcrs command. + * result of the pcrs command. * *********************************************************************/ char *pcrs_execute_single_command(const char *subject, const char *pcrs_command, int *hits) { - size_t size; + size_t buffer_size, new_size; char *result = NULL; pcrs_job *job; @@ -1183,12 +1179,12 @@ char *pcrs_execute_single_command(const char *subject, const char *pcrs_command, assert(pcrs_command); *hits = 0; - size = strlen(subject); + buffer_size = strlen(subject); job = pcrs_compile_command(pcrs_command, hits); if (NULL != job) { - *hits = pcrs_execute(job, subject, size, &result, &size); + *hits = pcrs_execute(job, subject, buffer_size, &result, &new_size); if (*hits < 0) { freez(result); @@ -1200,7 +1196,6 @@ char *pcrs_execute_single_command(const char *subject, const char *pcrs_command, } -static const char warning[] = "... [too long, truncated]"; /********************************************************************* * * Function : pcrs_compile_dynamic_command @@ -1209,13 +1204,12 @@ static const char warning[] = "... [too long, truncated]"; * values of the variables and compiles it. * * Parameters : - * 1 : csp = Current client state (buffers, headers, etc...) - * 2 : pcrs_command = The dynamic pcrs command to compile - * 3 : v = NULL terminated array of variables and their values. - * 4 : error = pcrs error code + * 1 : pcrs_command = The dynamic pcrs command to compile + * 2 : v = NULL terminated array of variables and their values. + * 3 : error = pcrs error code * * Returns : NULL in case of hard errors, otherwise the - * compiled pcrs job. + * compiled pcrs job. * *********************************************************************/ pcrs_job *pcrs_compile_dynamic_command(char *pcrs_command, const struct pcrs_variable v[], int *error) @@ -1248,6 +1242,7 @@ pcrs_job *pcrs_compile_dynamic_command(char *pcrs_command, const struct pcrs_var { /* No proper delimiter found */ *error = PCRS_ERR_CMDSYNTAX; + freez(pcrs_command_tmp); return NULL; } @@ -1257,7 +1252,7 @@ pcrs_job *pcrs_compile_dynamic_command(char *pcrs_command, const struct pcrs_var */ assert(NULL == strchr(v->name, d)); - ret = snprintf(buf, sizeof(buf), "s%c\\$%s%c%s%cgT", d, v->name, d, v->value, d); + ret = snprintf(buf, sizeof(buf), "s%c\\$%s%c%s%cDgT", d, v->name, d, v->value, d); assert(ret >= 0); if (ret >= sizeof(buf)) { @@ -1267,10 +1262,11 @@ pcrs_job *pcrs_compile_dynamic_command(char *pcrs_command, const struct pcrs_var * with a truncation message and close the pattern * properly. */ - const size_t trailer_size = sizeof(warning) + 3; /* 3 for d + "gT" */ + static const char warning[] = "... [too long, truncated]"; + const size_t trailer_size = sizeof(warning) + 4; /* 4 for d + "DgT" */ char *trailer_start = buf + sizeof(buf) - trailer_size; - ret = snprintf(trailer_start, trailer_size, "%s%cgT", warning, d); + ret = snprintf(trailer_start, trailer_size, "%s%cDgT", warning, d); assert(ret == trailer_size - 1); assert(sizeof(buf) == strlen(buf) + 1); truncation = 1;