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