From 5dc896132ef4d63b6f6fff858761f5acf248f738 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Fri, 3 Oct 2025 11:30:23 +0000 Subject: [PATCH] Avoid zero-length allocations, which return NULL on z/OS It's totally legal to return NULL for malloc(0), but every other platform that we target returns a non-NULL pointer. We don't need to do an allocation anyway (nor do we need to copy the terminating NUL byte). --- src/pcre2_dfa_match.c | 17 ++++++++++------- src/pcre2_match.c | 34 ++++++++++++++++++++-------------- src/pcre2test_inc.h | 1 + 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/pcre2_dfa_match.c b/src/pcre2_dfa_match.c index 519229504..f507acf8d 100644 --- a/src/pcre2_dfa_match.c +++ b/src/pcre2_dfa_match.c @@ -3342,7 +3342,6 @@ pcre2_dfa_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, pcre2_match_context *mcontext, int *workspace, PCRE2_SIZE wscount) { int rc; -int was_zero_terminated = 0; const pcre2_real_code *re = (const pcre2_real_code *)code; uint32_t original_options = options; @@ -3403,7 +3402,6 @@ if ((options & ~PUBLIC_DFA_MATCH_OPTIONS) != 0) if (length == PCRE2_ZERO_TERMINATED) { length = PRIV(strlen)(subject); - was_zero_terminated = 1; } if (wscount < 20) { rc = PCRE2_ERROR_DFA_WSSIZE; goto EXIT; } @@ -4062,11 +4060,16 @@ for (;;) if (rc >= 0 && (options & PCRE2_COPY_MATCHED_SUBJECT) != 0) { - length = CU2BYTES(length + was_zero_terminated); - match_data->subject = match_data->memctl.malloc(length, - match_data->memctl.memory_data); - if (match_data->subject == NULL) { rc = PCRE2_ERROR_NOMEMORY; goto EXIT; } - memcpy((void *)match_data->subject, subject, length); + if (length != 0) + { + match_data->subject = match_data->memctl.malloc(CU2BYTES(length), + match_data->memctl.memory_data); + if (match_data->subject == NULL) + { rc = PCRE2_ERROR_NOMEMORY; goto EXIT; } + memcpy((void *)match_data->subject, subject, CU2BYTES(length)); + } + else + match_data->subject = NULL; match_data->flags |= PCRE2_MD_COPIED_SUBJECT; } else if (rc >= 0 || rc == PCRE2_ERROR_PARTIAL) diff --git a/src/pcre2_match.c b/src/pcre2_match.c index ab7647e8a..02faa0dcf 100644 --- a/src/pcre2_match.c +++ b/src/pcre2_match.c @@ -6948,7 +6948,6 @@ pcre2_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, pcre2_match_context *mcontext) { int rc; -int was_zero_terminated = 0; const uint8_t *start_bits = NULL; const pcre2_real_code *re = (const pcre2_real_code *)code; uint32_t original_options = options; @@ -7024,7 +7023,6 @@ req_cu_ptr = start_match - 1; if (length == PCRE2_ZERO_TERMINATED) { length = PRIV(strlen)(subject); - was_zero_terminated = 1; } true_end_subject = end_subject = subject + length; @@ -7193,12 +7191,16 @@ if (use_jit) match_data->options = original_options; if (rc >= 0 && (options & PCRE2_COPY_MATCHED_SUBJECT) != 0) { - length = CU2BYTES(length + was_zero_terminated); - match_data->subject = match_data->memctl.malloc(length, - match_data->memctl.memory_data); - if (match_data->subject == NULL) - return match_data->rc = PCRE2_ERROR_NOMEMORY; - memcpy((void *)match_data->subject, subject, length); + if (length != 0) + { + match_data->subject = match_data->memctl.malloc(CU2BYTES(length), + match_data->memctl.memory_data); + if (match_data->subject == NULL) + return match_data->rc = PCRE2_ERROR_NOMEMORY; + memcpy((void *)match_data->subject, subject, CU2BYTES(length)); + } + else + match_data->subject = NULL; match_data->flags |= PCRE2_MD_COPIED_SUBJECT; } else @@ -8155,12 +8157,16 @@ if (rc == MATCH_MATCH) mb->last_used_ptr : mb->end_match_ptr) - subject; if ((options & PCRE2_COPY_MATCHED_SUBJECT) != 0) { - length = CU2BYTES(length + was_zero_terminated); - match_data->subject = match_data->memctl.malloc(length, - match_data->memctl.memory_data); - if (match_data->subject == NULL) - return match_data->rc = PCRE2_ERROR_NOMEMORY; - memcpy((void *)match_data->subject, subject, length); + if (length != 0) + { + match_data->subject = match_data->memctl.malloc(CU2BYTES(length), + match_data->memctl.memory_data); + if (match_data->subject == NULL) + return match_data->rc = PCRE2_ERROR_NOMEMORY; + memcpy((void *)match_data->subject, subject, CU2BYTES(length)); + } + else + match_data->subject = NULL; match_data->flags |= PCRE2_MD_COPIED_SUBJECT; } else match_data->subject = original_subject; diff --git a/src/pcre2test_inc.h b/src/pcre2test_inc.h index 7bf72dccd..1883b39af 100644 --- a/src/pcre2test_inc.h +++ b/src/pcre2test_inc.h @@ -2533,6 +2533,7 @@ if (pat_patctl.convert_type != CONVERT_UNSET) // TODO No valgrind guards for out-of-bounds read in pcre2_pattern_convert(), // nor do we appear to have a facility for testing zero-terminated patterns here. + // Can we use something other than zero as a sentinel to allow testing empty inputs? if (pat_patctl.convert_length != 0) {