match: avoid crash if subject NULL and PCRE2_ZERO_TERMINATED (#53)

* pcre2_match: avoid crash if subject NULL and PCRE2_ZERO_TERMINATED

When length of subject is PCRE2_ZERO_TERMINATED strlen is used
to calculate its size, which will trigger a crash if subject is
also NULL.

Move the NULL check before strlen on it would be used, and make
sure or dependent variables are set after the NULL validation
as well.

While at it, fix a typo in a debug flag in the same file, which
is otherwise unrelated and make sure the full section of constrain
checks can be identified clearly using the leading comment alone.

* pcre2_dfa_match: avoid crash if subject NULL and PCRE2_ZERO_TERMINATED

When length of subject is PCRE2_ZERO_TERMINATED strlen is used
to calculate its size, which will trigger a crash if subject is
also NULL.

Move the NULL check before the detection for subject sizes to
avoid this issue.

* pcre2_substitute: avoid crash if subject or replacement are NULL

The underlying pcre2_match() function will validate the subject if
needed, but will crash when length is PCRE2_ZERO_TERMINATED or if
subject == NULL and pcre2_match() is not being called because
match_data was provided.

The replacement parameter is missing NULL checks, and so currently
allows for an equivalent response to "" if rlength == 0.

Restrict all other cases to avoid strlen(NULL) crashes in the same
way that is done for subject, but also make sure to reject invalid
length values as early as possible.
This commit is contained in:
Carlo Marcelo Arenas Belón 2021-11-27 08:49:31 -08:00 committed by GitHub
parent d24a1c9d31
commit ae4e6261e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 28 additions and 23 deletions

View File

@ -3649,7 +3649,9 @@ needed is returned via \fIoutlengthptr\fP. Note that this does not happen by
default. default.
.P .P
PCRE2_ERROR_NULL is returned if PCRE2_SUBSTITUTE_MATCHED is set but the PCRE2_ERROR_NULL is returned if PCRE2_SUBSTITUTE_MATCHED is set but the
\fImatch_data\fP argument is NULL. \fImatch_data\fP argument is NULL or if the \fIsubject\fP or \fIreplacement\fP
arguments are NULL. For backward compatibility reasons an exception is made for
the \fIreplacement\fP argument if the \fIrlength\fP argument is also 0.
.P .P
PCRE2_ERROR_BADREPLACEMENT is used for miscellaneous syntax errors in the PCRE2_ERROR_BADREPLACEMENT is used for miscellaneous syntax errors in the
replacement string, with more particular errors being PCRE2_ERROR_BADREPESCAPE replacement string, with more particular errors being PCRE2_ERROR_BADREPESCAPE

View File

@ -3285,8 +3285,11 @@ rws->next = NULL;
rws->size = RWS_BASE_SIZE; rws->size = RWS_BASE_SIZE;
rws->free = RWS_BASE_SIZE - RWS_ANCHOR_SIZE; rws->free = RWS_BASE_SIZE - RWS_ANCHOR_SIZE;
/* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated /* Plausibility checks */
subject string. */
if ((options & ~PUBLIC_DFA_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;
if (re == NULL || subject == NULL || workspace == NULL || match_data == NULL)
return PCRE2_ERROR_NULL;
if (length == PCRE2_ZERO_TERMINATED) if (length == PCRE2_ZERO_TERMINATED)
{ {
@ -3294,11 +3297,6 @@ if (length == PCRE2_ZERO_TERMINATED)
was_zero_terminated = 1; was_zero_terminated = 1;
} }
/* Plausibility checks */
if ((options & ~PUBLIC_DFA_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;
if (re == NULL || subject == NULL || workspace == NULL || match_data == NULL)
return PCRE2_ERROR_NULL;
if (wscount < 20) return PCRE2_ERROR_DFA_WSSIZE; if (wscount < 20) return PCRE2_ERROR_DFA_WSSIZE;
if (start_offset > length) return PCRE2_ERROR_BADOFFSET; if (start_offset > length) return PCRE2_ERROR_BADOFFSET;

View File

@ -49,7 +49,7 @@ POSSIBILITY OF SUCH DAMAGE.
/* #define DEBUG_SHOW_OPS */ /* #define DEBUG_SHOW_OPS */
/* #define DEBUG_SHOW_RMATCH */ /* #define DEBUG_SHOW_RMATCH */
#ifdef DEBUG_FRAME_DISPLAY #ifdef DEBUG_FRAMES_DISPLAY
#include <stdarg.h> #include <stdarg.h>
#endif #endif
@ -6129,8 +6129,8 @@ PCRE2_UCHAR req_cu2 = 0;
PCRE2_SPTR bumpalong_limit; PCRE2_SPTR bumpalong_limit;
PCRE2_SPTR end_subject; PCRE2_SPTR end_subject;
PCRE2_SPTR true_end_subject; PCRE2_SPTR true_end_subject;
PCRE2_SPTR start_match = subject + start_offset; PCRE2_SPTR start_match;
PCRE2_SPTR req_cu_ptr = start_match - 1; PCRE2_SPTR req_cu_ptr;
PCRE2_SPTR start_partial; PCRE2_SPTR start_partial;
PCRE2_SPTR match_partial; PCRE2_SPTR match_partial;
@ -6170,9 +6170,14 @@ PCRE2_SPTR stack_frames_vector[START_FRAMES_SIZE/sizeof(PCRE2_SPTR)]
PCRE2_KEEP_UNINITIALIZED; PCRE2_KEEP_UNINITIALIZED;
mb->stack_frames = (heapframe *)stack_frames_vector; mb->stack_frames = (heapframe *)stack_frames_vector;
/* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated /* Plausibility checks */
subject string. */
if ((options & ~PUBLIC_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;
if (code == NULL || subject == NULL || match_data == NULL)
return PCRE2_ERROR_NULL;
start_match = subject + start_offset;
req_cu_ptr = start_match - 1;
if (length == PCRE2_ZERO_TERMINATED) if (length == PCRE2_ZERO_TERMINATED)
{ {
length = PRIV(strlen)(subject); length = PRIV(strlen)(subject);
@ -6180,11 +6185,6 @@ if (length == PCRE2_ZERO_TERMINATED)
} }
true_end_subject = end_subject = subject + length; true_end_subject = end_subject = subject + length;
/* Plausibility checks */
if ((options & ~PUBLIC_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;
if (code == NULL || subject == NULL || match_data == NULL)
return PCRE2_ERROR_NULL;
if (start_offset > length) return PCRE2_ERROR_BADOFFSET; if (start_offset > length) return PCRE2_ERROR_BADOFFSET;
/* Check that the first field in the block is the magic number. */ /* Check that the first field in the block is the magic number. */

View File

@ -260,6 +260,12 @@ PCRE2_UNSET, so as not to imply an offset in the replacement. */
if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0) if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0)
return PCRE2_ERROR_BADOPTION; return PCRE2_ERROR_BADOPTION;
/* Validate length and find the end of the replacement. */
if (replacement == NULL && rlength > 0) return PCRE2_ERROR_NULL;
else if (rlength == PCRE2_ZERO_TERMINATED)
rlength = PRIV(strlen)(replacement);
repend = replacement + rlength;
/* Check for using a match that has already happened. Note that the subject /* Check for using a match that has already happened. Note that the subject
pointer in the match data may be NULL after a no-match. */ pointer in the match data may be NULL after a no-match. */
@ -292,6 +298,7 @@ else if (use_existing_match)
(pcre2_general_context *)mcontext; (pcre2_general_context *)mcontext;
int pairs = (code->top_bracket + 1 < match_data->oveccount)? int pairs = (code->top_bracket + 1 < match_data->oveccount)?
code->top_bracket + 1 : match_data->oveccount; code->top_bracket + 1 : match_data->oveccount;
if (subject == NULL) return PCRE2_ERROR_NULL;
internal_match_data = pcre2_match_data_create(match_data->oveccount, internal_match_data = pcre2_match_data_create(match_data->oveccount,
gcontext); gcontext);
if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY; if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY;
@ -312,11 +319,9 @@ scb.input = subject;
scb.output = (PCRE2_SPTR)buffer; scb.output = (PCRE2_SPTR)buffer;
scb.ovector = ovector; scb.ovector = ovector;
/* Find lengths of zero-terminated strings and the end of the replacement. */ /* Find lengths of zero-terminated subject */
if (length == PCRE2_ZERO_TERMINATED)
if (length == PCRE2_ZERO_TERMINATED) length = PRIV(strlen)(subject); length = subject? PRIV(strlen)(subject) : 0;
if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement);
repend = replacement + rlength;
/* Check UTF replacement string if necessary. */ /* Check UTF replacement string if necessary. */