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