File php-pcre-replace-impl-CWE-680.patch of Package php7.16741
Index: php-7.0.7/ext/pcre/php_pcre.c
===================================================================
--- php-7.0.7.orig/ext/pcre/php_pcre.c 2016-05-25 15:14:21.000000000 +0200
+++ php-7.0.7/ext/pcre/php_pcre.c 2018-08-29 09:33:16.459141982 +0200
@@ -1108,6 +1108,18 @@ PHPAPI zend_string *php_pcre_replace(zen
}
/* }}} */
+static size_t zend_safe_address_guarded(size_t nmemb, size_t size, size_t offset)
+{
+ int overflow;
+ size_t ret = zend_safe_address(nmemb, size, offset, &overflow);
+
+ if (overflow) {
+ zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
+ return 0;
+ }
+ return ret;
+}
+
/* {{{ php_pcre_replace_impl() */
PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *subject_str, char *subject, int subject_len, zval *replace_val, int is_callable_replace, int limit, int *replace_count)
{
@@ -1119,13 +1131,12 @@ PHPAPI zend_string *php_pcre_replace_imp
char **subpat_names; /* Array for named subpatterns */
int num_subpats; /* Number of captured subpatterns */
int size_offsets; /* Size of the offsets array */
- int new_len; /* Length of needed storage */
- int alloc_len; /* Actual allocated length */
+ size_t new_len; /* Length of needed storage */
+ size_t alloc_len; /* Actual allocated length */
int match_len; /* Length of the current match */
int backref; /* Backreference number */
int start_offset; /* Where the new search starts */
int g_notempty=0; /* If the match should not be empty */
- int replace_len=0; /* Length of replacement string */
char *replace=NULL, /* Replacement string */
*walkbuf, /* Location of current replacement in the result */
*walk, /* Used to walk the replacement string */
@@ -1133,7 +1144,7 @@ PHPAPI zend_string *php_pcre_replace_imp
*piece, /* The current piece of subject */
*replace_end=NULL, /* End of replacement string */
walk_last; /* Last walked character */
- int result_len; /* Length of result */
+ size_t result_len; /* Length of result */
unsigned char *mark = NULL; /* Target for MARK name */
zend_string *result; /* Result of replacement */
zend_string *eval_result=NULL; /* Result of custom function */
@@ -1155,8 +1166,7 @@ PHPAPI zend_string *php_pcre_replace_imp
if (!is_callable_replace) {
replace = Z_STRVAL_P(replace_val);
- replace_len = (int)Z_STRLEN_P(replace_val);
- replace_end = replace + replace_len;
+ replace_end = replace + Z_STRLEN_P(replace_val);
}
/* Calculate the size of the offsets array, and allocate memory for it. */
@@ -1176,6 +1186,11 @@ PHPAPI zend_string *php_pcre_replace_imp
if (UNEXPECTED(pce->name_count > 0)) {
subpat_names = make_subpats_table(num_subpats, pce);
if (!subpat_names) {
+ if (size_offsets <= 32) {
+ free_alloca(offsets, use_heap);
+ } else {
+ efree(offsets);
+ }
return NULL;
}
}
@@ -1219,7 +1234,7 @@ PHPAPI zend_string *php_pcre_replace_imp
match = subject + offsets[0];
new_len = result_len + offsets[0] - start_offset; /* part before the match */
-
+
/* if (!is_callable_replace) */
if (EXPECTED(replace)) {
/* do regular substitution */
@@ -1245,7 +1260,7 @@ PHPAPI zend_string *php_pcre_replace_imp
}
if (new_len >= alloc_len) {
- alloc_len = alloc_len + 2 * new_len;
+ alloc_len = zend_safe_address_guarded(2, new_len, alloc_len);
if (result == NULL) {
result = zend_string_alloc(alloc_len, 0);
} else {
@@ -1255,7 +1270,7 @@ PHPAPI zend_string *php_pcre_replace_imp
/* copy the part of the string before the match */
memcpy(&ZSTR_VAL(result)[result_len], piece, match-piece);
- result_len += (int)(match-piece);
+ result_len += (match-piece);
/* copy replacement and backrefs */
walkbuf = ZSTR_VAL(result) + result_len;
@@ -1283,14 +1298,14 @@ PHPAPI zend_string *php_pcre_replace_imp
}
*walkbuf = '\0';
/* increment the result length by how much we've added to the string */
- result_len += (int)(walkbuf - (ZSTR_VAL(result) + result_len));
+ result_len += (walkbuf - (ZSTR_VAL(result) + result_len));
} else {
/* Use custom function to get replacement string and its length. */
eval_result = preg_do_repl_func(replace_val, subject, offsets, subpat_names, count, mark);
ZEND_ASSERT(eval_result);
- new_len += (int)ZSTR_LEN(eval_result);
+ new_len = zend_safe_address_guarded(1, ZSTR_LEN(eval_result), new_len);
if (new_len >= alloc_len) {
- alloc_len = alloc_len + 2 * new_len;
+ alloc_len = zend_safe_address_guarded(2, new_len, alloc_len);
if (result == NULL) {
result = zend_string_alloc(alloc_len, 0);
} else {
@@ -1331,7 +1346,7 @@ PHPAPI zend_string *php_pcre_replace_imp
break;
}
new_len = result_len + subject_len - start_offset;
- if (new_len > alloc_len) {
+ if (new_len >= alloc_len) {
alloc_len = new_len; /* now we know exactly how long it is */
if (NULL != result) {
result = zend_string_realloc(result, alloc_len, 0);
@@ -1512,7 +1527,7 @@ static int preg_replace_impl(zval *retur
}
}
} ZEND_HASH_FOREACH_END();
- } else {
+ } else {
/* if subject is not an array */
old_replace_count = replace_count;
if ((result = php_replace_in_subject(regex, replace, subject, limit_val, is_callable_replace, &replace_count)) != NULL) {
@@ -1520,10 +1535,13 @@ static int preg_replace_impl(zval *retur
RETVAL_STR(result);
} else {
zend_string_release(result);
+ RETVAL_NULL();
}
+ } else {
+ RETVAL_NULL();
}
}
-
+
return replace_count;
}
/* }}} */