
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;
 			}
