af9dc8
From 5b597a2e5b28e2d5a52fc1be13f425f08f47cb62 Mon Sep 17 00:00:00 2001
af9dc8
From: Stanislav Malyshev <stas@php.net>
af9dc8
Date: Sat, 18 Jun 2016 21:48:39 -0700
af9dc8
Subject: [PATCH] Fix bug #72402: _php_mb_regex_ereg_replace_exec - double free
af9dc8
af9dc8
---
af9dc8
 ext/mbstring/php_mbregex.c       | 65 ++++++++++++++++++++--------------------
af9dc8
 ext/mbstring/tests/bug72402.phpt | 17 +++++++++++
af9dc8
 2 files changed, 49 insertions(+), 33 deletions(-)
af9dc8
 create mode 100644 ext/mbstring/tests/bug72402.phpt
af9dc8
af9dc8
diff --git a/ext/mbstring/php_mbregex.c b/ext/mbstring/php_mbregex.c
af9dc8
index d73c848..6cdee23 100644
af9dc8
--- a/ext/mbstring/php_mbregex.c
af9dc8
+++ b/ext/mbstring/php_mbregex.c
af9dc8
@@ -32,7 +32,7 @@
af9dc8
 #include "ext/standard/info.h"
af9dc8
 #include "php_mbregex.h"
af9dc8
 #include "mbstring.h"
af9dc8
- 
af9dc8
+
af9dc8
 #include "php_onig_compat.h" /* must come prior to the oniguruma header */
af9dc8
 #include <oniguruma.h>
af9dc8
 #undef UChar
af9dc8
@@ -55,7 +55,7 @@ struct _zend_mb_regex_globals {
af9dc8
 #define MBREX(g) (MBSTRG(mb_regex_globals)->g)
af9dc8
 
af9dc8
 /* {{{ static void php_mb_regex_free_cache() */
af9dc8
-static void php_mb_regex_free_cache(php_mb_regex_t **pre) 
af9dc8
+static void php_mb_regex_free_cache(php_mb_regex_t **pre)
af9dc8
 {
af9dc8
 	onig_free(*pre);
af9dc8
 }
af9dc8
@@ -78,7 +78,7 @@ static int _php_mb_regex_globals_ctor(zend_mb_regex_globals *pglobals TSRMLS_DC)
af9dc8
 /* }}} */
af9dc8
 
af9dc8
 /* {{{ _php_mb_regex_globals_dtor */
af9dc8
-static void _php_mb_regex_globals_dtor(zend_mb_regex_globals *pglobals TSRMLS_DC) 
af9dc8
+static void _php_mb_regex_globals_dtor(zend_mb_regex_globals *pglobals TSRMLS_DC)
af9dc8
 {
af9dc8
 	zend_hash_destroy(&pglobals->ht_rc);
af9dc8
 }
af9dc8
@@ -466,7 +466,7 @@ static php_mb_regex_t *php_mbregex_compile_pattern(const char *pattern, int patl
af9dc8
 		retval = *rc;
af9dc8
 	}
af9dc8
 out:
af9dc8
-	return retval; 
af9dc8
+	return retval;
af9dc8
 }
af9dc8
 /* }}} */
af9dc8
 
af9dc8
@@ -483,7 +483,7 @@ static size_t _php_mb_regex_get_option_string(char *str, size_t len, OnigOptionT
af9dc8
 			--len_left;
af9dc8
 			*(p++) = 'i';
af9dc8
 		}
af9dc8
-		++len_req;	
af9dc8
+		++len_req;
af9dc8
 	}
af9dc8
 
af9dc8
 	if ((option & ONIG_OPTION_EXTEND) != 0) {
af9dc8
@@ -491,7 +491,7 @@ static size_t _php_mb_regex_get_option_string(char *str, size_t len, OnigOptionT
af9dc8
 			--len_left;
af9dc8
 			*(p++) = 'x';
af9dc8
 		}
af9dc8
-		++len_req;	
af9dc8
+		++len_req;
af9dc8
 	}
af9dc8
 
af9dc8
 	if ((option & (ONIG_OPTION_MULTILINE | ONIG_OPTION_SINGLELINE)) ==
af9dc8
@@ -500,14 +500,14 @@ static size_t _php_mb_regex_get_option_string(char *str, size_t len, OnigOptionT
af9dc8
 			--len_left;
af9dc8
 			*(p++) = 'p';
af9dc8
 		}
af9dc8
-		++len_req;	
af9dc8
+		++len_req;
af9dc8
 	} else {
af9dc8
 		if ((option & ONIG_OPTION_MULTILINE) != 0) {
af9dc8
 			if (len_left > 0) {
af9dc8
 				--len_left;
af9dc8
 				*(p++) = 'm';
af9dc8
 			}
af9dc8
-			++len_req;	
af9dc8
+			++len_req;
af9dc8
 		}
af9dc8
 
af9dc8
 		if ((option & ONIG_OPTION_SINGLELINE) != 0) {
af9dc8
@@ -515,22 +515,22 @@ static size_t _php_mb_regex_get_option_string(char *str, size_t len, OnigOptionT
af9dc8
 				--len_left;
af9dc8
 				*(p++) = 's';
af9dc8
 			}
af9dc8
-			++len_req;	
af9dc8
+			++len_req;
af9dc8
 		}
af9dc8
-	}	
af9dc8
+	}
af9dc8
 	if ((option & ONIG_OPTION_FIND_LONGEST) != 0) {
af9dc8
 		if (len_left > 0) {
af9dc8
 			--len_left;
af9dc8
 			*(p++) = 'l';
af9dc8
 		}
af9dc8
-		++len_req;	
af9dc8
+		++len_req;
af9dc8
 	}
af9dc8
 	if ((option & ONIG_OPTION_FIND_NOT_EMPTY) != 0) {
af9dc8
 		if (len_left > 0) {
af9dc8
 			--len_left;
af9dc8
 			*(p++) = 'n';
af9dc8
 		}
af9dc8
-		++len_req;	
af9dc8
+		++len_req;
af9dc8
 	}
af9dc8
 
af9dc8
 	c = 0;
af9dc8
@@ -566,7 +566,7 @@ static size_t _php_mb_regex_get_option_string(char *str, size_t len, OnigOptionT
af9dc8
 		--len_left;
af9dc8
 		*(p++) = '\0';
af9dc8
 	}
af9dc8
-	++len_req;	
af9dc8
+	++len_req;
af9dc8
 	if (len < len_req) {
af9dc8
 		return len_req;
af9dc8
 	}
af9dc8
@@ -577,11 +577,11 @@ static size_t _php_mb_regex_get_option_string(char *str, size_t len, OnigOptionT
af9dc8
 
af9dc8
 /* {{{ _php_mb_regex_init_options */
af9dc8
 static void
af9dc8
-_php_mb_regex_init_options(const char *parg, int narg, OnigOptionType *option, OnigSyntaxType **syntax, int *eval) 
af9dc8
+_php_mb_regex_init_options(const char *parg, int narg, OnigOptionType *option, OnigSyntaxType **syntax, int *eval)
af9dc8
 {
af9dc8
 	int n;
af9dc8
 	char c;
af9dc8
-	int optm = 0; 
af9dc8
+	int optm = 0;
af9dc8
 
af9dc8
 	*syntax = ONIG_SYNTAX_RUBY;
af9dc8
 
af9dc8
@@ -636,13 +636,13 @@ _php_mb_regex_init_options(const char *parg, int narg, OnigOptionType *option, O
af9dc8
 					*syntax = ONIG_SYNTAX_POSIX_EXTENDED;
af9dc8
 					break;
af9dc8
 				case 'e':
af9dc8
-					if (eval != NULL) *eval = 1; 
af9dc8
+					if (eval != NULL) *eval = 1;
af9dc8
 					break;
af9dc8
 				default:
af9dc8
 					break;
af9dc8
 			}
af9dc8
 		}
af9dc8
-		if (option != NULL) *option|=optm; 
af9dc8
+		if (option != NULL) *option|=optm;
af9dc8
 	}
af9dc8
 }
af9dc8
 /* }}} */
af9dc8
@@ -860,11 +860,11 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
af9dc8
 	} else {
af9dc8
 		/* FIXME: this code is not multibyte aware! */
af9dc8
 		convert_to_long_ex(arg_pattern_zval);
af9dc8
-		pat_buf[0] = (char)Z_LVAL_PP(arg_pattern_zval);	
af9dc8
+		pat_buf[0] = (char)Z_LVAL_PP(arg_pattern_zval);
af9dc8
 		pat_buf[1] = '\0';
af9dc8
 
af9dc8
 		arg_pattern = pat_buf;
af9dc8
-		arg_pattern_len = 1;	
af9dc8
+		arg_pattern_len = 1;
af9dc8
 	}
af9dc8
 	/* create regex pattern buffer */
af9dc8
 	re = php_mbregex_compile_pattern(arg_pattern, arg_pattern_len, options, MBREX(current_mbctype), syntax TSRMLS_CC);
af9dc8
@@ -934,7 +934,7 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
af9dc8
 					}
af9dc8
 				}
af9dc8
 			}
af9dc8
-				
af9dc8
+
af9dc8
 			if (eval) {
af9dc8
 				zval v;
af9dc8
 				/* null terminate buffer */
af9dc8
@@ -953,32 +953,31 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
af9dc8
 				eval_buf.len = 0;
af9dc8
 				zval_dtor(&v);
af9dc8
 			} else if (is_callable) {
af9dc8
-				zval *retval_ptr;
af9dc8
+				zval *retval_ptr = NULL;
af9dc8
 				zval **args[1];
af9dc8
 				zval *subpats;
af9dc8
 				int i;
af9dc8
-				
af9dc8
+
af9dc8
 				MAKE_STD_ZVAL(subpats);
af9dc8
 				array_init(subpats);
af9dc8
-				
af9dc8
+
af9dc8
 				for (i = 0; i < regs->num_regs; i++) {
af9dc8
 					add_next_index_stringl(subpats, string + regs->beg[i], regs->end[i] - regs->beg[i], 1);
af9dc8
-				}				
af9dc8
-				
af9dc8
+				}
af9dc8
+
af9dc8
 				args[0] = &subpats;
af9dc8
 				/* null terminate buffer */
af9dc8
 				smart_str_0(&eval_buf);
af9dc8
-				
af9dc8
+
af9dc8
 				arg_replace_fci.param_count = 1;
af9dc8
 				arg_replace_fci.params = args;
af9dc8
 				arg_replace_fci.retval_ptr_ptr = &retval_ptr;
af9dc8
-				if (zend_call_function(&arg_replace_fci, &arg_replace_fci_cache TSRMLS_CC) == SUCCESS && arg_replace_fci.retval_ptr_ptr) {
af9dc8
+				if (zend_call_function(&arg_replace_fci, &arg_replace_fci_cache TSRMLS_CC) == SUCCESS && arg_replace_fci.retval_ptr_ptr && retval_ptr) {
af9dc8
 					convert_to_string_ex(&retval_ptr);
af9dc8
 					smart_str_appendl(&out_buf, Z_STRVAL_P(retval_ptr), Z_STRLEN_P(retval_ptr));
af9dc8
 					eval_buf.len = 0;
af9dc8
 					zval_ptr_dtor(&retval_ptr);
af9dc8
 				} else {
af9dc8
-					efree(description);
af9dc8
 					if (!EG(exception)) {
af9dc8
 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call custom replacement function");
af9dc8
 					}
af9dc8
@@ -991,7 +990,7 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
af9dc8
 				pos = (OnigUChar *)string + n;
af9dc8
 			} else {
af9dc8
 				if (pos < string_lim) {
af9dc8
-					smart_str_appendl(&out_buf, pos, 1); 
af9dc8
+					smart_str_appendl(&out_buf, pos, 1);
af9dc8
 				}
af9dc8
 				pos++;
af9dc8
 			}
af9dc8
@@ -1013,7 +1012,7 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
af9dc8
 	smart_str_free(&eval_buf);
af9dc8
 
af9dc8
 	if (err <= -2) {
af9dc8
-		smart_str_free(&out_buf);	
af9dc8
+		smart_str_free(&out_buf);
af9dc8
 		RETVAL_FALSE;
af9dc8
 	} else {
af9dc8
 		smart_str_appendc(&out_buf, '\0');
af9dc8
@@ -1063,7 +1062,7 @@ PHP_FUNCTION(mb_split)
af9dc8
 
af9dc8
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &arg_pattern, &arg_pattern_len, &string, &string_len, &count) == FAILURE) {
af9dc8
 		RETURN_FALSE;
af9dc8
-	} 
af9dc8
+	}
af9dc8
 
af9dc8
 	if (count > 0) {
af9dc8
 		count--;
af9dc8
@@ -1317,7 +1316,7 @@ PHP_FUNCTION(mb_ereg_search_init)
af9dc8
 	if (zend_parse_parameters(argc TSRMLS_CC, "z|ss", &arg_str, &arg_pattern, &arg_pattern_len, &arg_options, &arg_options_len) == FAILURE) {
af9dc8
 		return;
af9dc8
 	}
af9dc8
-	
af9dc8
+
af9dc8
 	if (argc > 1 && arg_pattern_len == 0) {
af9dc8
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty pattern");
af9dc8
 		RETURN_FALSE;
af9dc8
@@ -1416,7 +1415,7 @@ PHP_FUNCTION(mb_ereg_search_setpos)
af9dc8
 /* }}} */
af9dc8
 
af9dc8
 /* {{{ php_mb_regex_set_options */
af9dc8
-static void _php_mb_regex_set_options(OnigOptionType options, OnigSyntaxType *syntax, OnigOptionType *prev_options, OnigSyntaxType **prev_syntax TSRMLS_DC) 
af9dc8
+static void _php_mb_regex_set_options(OnigOptionType options, OnigSyntaxType *syntax, OnigOptionType *prev_options, OnigSyntaxType **prev_syntax TSRMLS_DC)
af9dc8
 {
af9dc8
 	if (prev_options != NULL) {
af9dc8
 		*prev_options = MBREX(regex_default_options);
af9dc8
diff --git a/ext/mbstring/tests/bug72402.phpt b/ext/mbstring/tests/bug72402.phpt
af9dc8
new file mode 100644
af9dc8
index 0000000..abb290b
af9dc8
--- /dev/null
af9dc8
+++ b/ext/mbstring/tests/bug72402.phpt
af9dc8
@@ -0,0 +1,17 @@
af9dc8
+--TEST--
af9dc8
+Bug #72402: _php_mb_regex_ereg_replace_exec - double free
af9dc8
+--SKIPIF--
af9dc8
+
af9dc8
+--FILE--
af9dc8
+
af9dc8
+function throwit() {
af9dc8
+	throw new Exception('it');
af9dc8
+}
af9dc8
+$var10 = "throwit";
af9dc8
+try {
af9dc8
+	$var14 = mb_ereg_replace_callback("", $var10, "");
af9dc8
+} catch(Exception $e) {}
af9dc8
+?>
af9dc8
+DONE
af9dc8
+--EXPECT--
af9dc8
+DONE
af9dc8
\ No newline at end of file