File php-pcre-replace-impl-CWE-680.patch of Package php5.14673
Index: php-5.5.14/ext/pcre/php_pcre.c
===================================================================
--- php-5.5.14.orig/ext/pcre/php_pcre.c 2014-06-25 15:06:23.000000000 +0200
+++ php-5.5.14/ext/pcre/php_pcre.c 2018-08-29 12:58:10.939640709 +0200
@@ -999,6 +999,47 @@ PHPAPI char *php_pcre_replace(char *rege
}
/* }}} */
+static int safe_address(int nmemb, int size, int offset)
+{
+ int res;
+ if (nmemb < 0 || nmemb < 0) {
+ return -1;
+ }
+ if (size == 0) {
+ res = 0;
+ }
+ else if(nmemb > INT_MAX / size) {
+ return -1;
+ }
+ else {
+ res = nmemb*size;
+ }
+
+ if (res > INT_MAX - offset) {
+ return -1;
+ }
+
+ return res + offset;
+}
+
+#define safe_address_guarded(res, nmemb, size, offset) \
+ { \
+ if ((res = safe_address(nmemb, size, offset)) == -1) {\
+ if (offsets) \
+ efree(offsets); \
+ if (subpat_names) \
+ efree(subpat_names); \
+ if (result) \
+ efree(result); \
+ zend_error_noreturn(E_ERROR, \
+ "Possible integer overflow in memory allocation (%zu * %zu + %zu)", \
+ nmemb, size, offset); \
+ return NULL; \
+ } \
+ }\
+
+
+
/* {{{ php_pcre_replace_impl() */
PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *replace_val,
int is_callable_replace, int *result_len, int limit, int *replace_count TSRMLS_DC)
@@ -1007,8 +1048,8 @@ PHPAPI char *php_pcre_replace_impl(pcre_
pcre_extra extra_data; /* Used locally for exec options */
int exoptions = 0; /* Execution options */
int count = 0; /* Count of matched subpatterns */
- int *offsets; /* Array of subpattern offsets */
- char **subpat_names; /* Array for named subpatterns */
+ int *offsets = NULL; /* Array of subpattern offsets */
+ char **subpat_names=NULL; /* 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 */
@@ -1021,7 +1062,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_
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 *result, /* Result of replacement */
+ char *result=NULL, /* Result of replacement */
*replace=NULL, /* Replacement string */
*new_buf, /* Temporary buffer for re-allocation */
*walkbuf, /* Location of current replacement in the result */
@@ -1077,7 +1118,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_
offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
- alloc_len = 2 * subject_len + 1;
+ safe_address_guarded(alloc_len, 2, subject_len, 1);
result = safe_emalloc(alloc_len, sizeof(char), 0);
/* Initialize */
@@ -1115,11 +1156,12 @@ PHPAPI char *php_pcre_replace_impl(pcre_
if (eval) {
eval_result_len = preg_do_eval(replace, replace_len, subject,
offsets, count, &eval_result TSRMLS_CC);
- new_len += eval_result_len;
+ safe_address_guarded(new_len, 1, eval_result_len, new_len);
+
} else if (is_callable_replace) {
/* Use custom function to get replacement string and its length. */
eval_result_len = preg_do_repl_func(replace_val, subject, offsets, subpat_names, count, &eval_result TSRMLS_CC);
- new_len += eval_result_len;
+ safe_address_guarded(new_len, 1, eval_result_len, new_len);
} else { /* do regular substitution */
walk = replace;
walk_last = 0;
@@ -1132,18 +1174,18 @@ PHPAPI char *php_pcre_replace_impl(pcre_
}
if (preg_get_backref(&walk, &backref)) {
if (backref < count)
- new_len += offsets[(backref<<1)+1] - offsets[backref<<1];
+ safe_address_guarded(new_len, 1, new_len, offsets[(backref<<1)+1] - offsets[backref<<1]);
continue;
}
}
- new_len++;
+ safe_address_guarded(new_len, 1, new_len, 1);
walk++;
walk_last = walk[-1];
}
}
if (new_len + 1 > alloc_len) {
- alloc_len = 1 + alloc_len + 2 * new_len;
+ safe_address_guarded(alloc_len, 2, new_len, 1 + alloc_len);
new_buf = emalloc(alloc_len);
memcpy(new_buf, result, *result_len);
efree(result);
@@ -1151,7 +1193,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_
}
/* copy the part of the string before the match */
memcpy(&result[*result_len], piece, match-piece);
- *result_len += match-piece;
+ safe_address_guarded(*result_len, 1, *result_len, match-piece);
/* copy replacement and backrefs */
walkbuf = result + *result_len;
@@ -1160,7 +1202,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_
* and clean up. */
if (eval || is_callable_replace) {
memcpy(walkbuf, eval_result, eval_result_len);
- *result_len += eval_result_len;
+ safe_address_guarded(*result_len, 1, *result_len, eval_result_len);
STR_FREE(eval_result);
} else { /* do regular backreference copying */
walk = replace;
@@ -1203,9 +1245,9 @@ PHPAPI char *php_pcre_replace_impl(pcre_
memcpy(&result[*result_len], piece, 1);
(*result_len)++;
} else {
- new_len = *result_len + subject_len - start_offset;
+ safe_address_guarded(new_len, 1, *result_len, subject_len - start_offset);
if (new_len + 1 > alloc_len) {
- alloc_len = new_len + 1; /* now we know exactly how long it is */
+ safe_address_guarded(alloc_len, 1, new_len, 1); /* now we know exactly how long it is */
new_buf = safe_emalloc(alloc_len, sizeof(char), 0);
memcpy(new_buf, result, *result_len);
efree(result);
@@ -1213,7 +1255,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_
}
/* stick that last bit of string on our output */
memcpy(&result[*result_len], piece, subject_len - start_offset);
- *result_len += subject_len - start_offset;
+ safe_address_guarded(*result_len, 1, *result_len, subject_len - start_offset);
result[*result_len] = '\0';
break;
}