]> granicus.if.org Git - php/commitdiff
fix bug #54238 (use-after-free in substr_replace())
authorStanislav Malyshev <stas@php.net>
Wed, 13 Apr 2011 06:32:41 +0000 (06:32 +0000)
committerStanislav Malyshev <stas@php.net>
Wed, 13 Apr 2011 06:32:41 +0000 (06:32 +0000)
ext/standard/string.c
ext/standard/tests/strings/bug54238.phpt [new file with mode: 0644]

index 5eb0411b18e33604233514050ea14c5ea9c8b53d..9381aaaeeacd1e8095037e90a0ca9dd9fb5d24c5 100644 (file)
@@ -2352,20 +2352,35 @@ PHP_FUNCTION(substr_replace)
 
                zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(str), &pos_str);
                while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(str), (void **) &tmp_str, &pos_str) == SUCCESS) {
-                       convert_to_string_ex(tmp_str);
+                       zval *orig_str;
+                       zval dummy;
+                       if(Z_TYPE_PP(tmp_str) != IS_STRING) {
+                               dummy = **tmp_str;
+                               orig_str = &dummy;
+                               zval_copy_ctor(orig_str);
+                               convert_to_string(orig_str);
+                       } else {
+                               orig_str = *tmp_str;
+                       }
 
                        if (Z_TYPE_PP(from) == IS_ARRAY) {
                                if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(from), (void **) &tmp_from, &pos_from)) {
-                                       convert_to_long_ex(tmp_from);
+                                       if(Z_TYPE_PP(tmp_from) != IS_LONG) {
+                                               zval dummy = **tmp_from;
+                                               zval_copy_ctor(&dummy);
+                                               convert_to_long(&dummy);
+                                               f = Z_LVAL(dummy);
+                                       } else {
+                                               f = Z_LVAL_PP(tmp_from);
+                                       }
 
-                                       f = Z_LVAL_PP(tmp_from);
                                        if (f < 0) {
-                                               f = Z_STRLEN_PP(tmp_str) + f;
+                                               f = Z_STRLEN_P(orig_str) + f;
                                                if (f < 0) {
                                                        f = 0;
                                                }
-                                       } else if (f > Z_STRLEN_PP(tmp_str)) {
-                                               f = Z_STRLEN_PP(tmp_str);
+                                       } else if (f > Z_STRLEN_P(orig_str)) {
+                                               f = Z_STRLEN_P(orig_str);
                                        }
                                        zend_hash_move_forward_ex(Z_ARRVAL_PP(from), &pos_from);
                                } else {
@@ -2374,72 +2389,94 @@ PHP_FUNCTION(substr_replace)
                        } else {
                                f = Z_LVAL_PP(from);
                                if (f < 0) {
-                                       f = Z_STRLEN_PP(tmp_str) + f;
+                                       f = Z_STRLEN_P(orig_str) + f;
                                        if (f < 0) {
                                                f = 0;
                                        }
-                               } else if (f > Z_STRLEN_PP(tmp_str)) {
-                                       f = Z_STRLEN_PP(tmp_str);
+                               } else if (f > Z_STRLEN_P(orig_str)) {
+                                       f = Z_STRLEN_P(orig_str);
                                }
                        }
 
                        if (argc > 3 && Z_TYPE_PP(len) == IS_ARRAY) {
                                if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(len), (void **) &tmp_len, &pos_len)) {
-                                       convert_to_long_ex(tmp_len);
+                                       if(Z_TYPE_PP(tmp_len) != IS_LONG) {
+                                               zval dummy = **tmp_len;
+                                               zval_copy_ctor(&dummy);
+                                               convert_to_long(&dummy);
+                                               l = Z_LVAL(dummy);
+                                       } else {
+                                               l = Z_LVAL_PP(tmp_len);
+                                       }
 
                                        l = Z_LVAL_PP(tmp_len);
                                        zend_hash_move_forward_ex(Z_ARRVAL_PP(len), &pos_len);
                                } else {
-                                       l = Z_STRLEN_PP(tmp_str);
+                                       l = Z_STRLEN_P(orig_str);
                                }
                        } else if (argc > 3) { 
                                l = Z_LVAL_PP(len);
                        } else {
-                               l = Z_STRLEN_PP(tmp_str);
+                               l = Z_STRLEN_P(orig_str);
                        }
 
                        if (l < 0) {
-                               l = (Z_STRLEN_PP(tmp_str) - f) + l;
+                               l = (Z_STRLEN_P(orig_str) - f) + l;
                                if (l < 0) {
                                        l = 0;
                                }
                        }
 
-                       if ((f + l) > Z_STRLEN_PP(tmp_str)) {
-                               l = Z_STRLEN_PP(tmp_str) - f;
+                       if ((f + l) > Z_STRLEN_P(orig_str)) {
+                               l = Z_STRLEN_P(orig_str) - f;
                        }
 
-                       result_len = Z_STRLEN_PP(tmp_str) - l;
+                       result_len = Z_STRLEN_P(orig_str) - l;
 
                        if (Z_TYPE_PP(repl) == IS_ARRAY) {
                                if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(repl), (void **) &tmp_repl, &pos_repl)) {
-                                       convert_to_string_ex(tmp_repl);
-                                       result_len += Z_STRLEN_PP(tmp_repl);
+                                       zval *repl_str;
+                                       zval zrepl;
+                                       if(Z_TYPE_PP(tmp_repl) != IS_STRING) {
+                                               zrepl = **tmp_repl;
+                                               repl_str = &zrepl;
+                                               zval_copy_ctor(repl_str);
+                                               convert_to_string(repl_str);
+                                       } else {
+                                               repl_str = *tmp_repl;
+                                       }
+
+                                       result_len += Z_STRLEN_P(repl_str);
                                        zend_hash_move_forward_ex(Z_ARRVAL_PP(repl), &pos_repl);        
                                        result = emalloc(result_len + 1);
 
-                                       memcpy(result, Z_STRVAL_PP(tmp_str), f);
-                                       memcpy((result + f), Z_STRVAL_PP(tmp_repl), Z_STRLEN_PP(tmp_repl));
-                                       memcpy((result + f + Z_STRLEN_PP(tmp_repl)), Z_STRVAL_PP(tmp_str) + f + l, Z_STRLEN_PP(tmp_str) - f - l);
+                                       memcpy(result, Z_STRVAL_P(orig_str), f);
+                                       memcpy((result + f), Z_STRVAL_P(repl_str), Z_STRLEN_P(repl_str));
+                                       memcpy((result + f + Z_STRLEN_P(repl_str)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l);
+                                       if(Z_TYPE_PP(tmp_repl) != IS_STRING) {
+                                               zval_dtor(repl_str);
+                                       }
                                } else {
                                        result = emalloc(result_len + 1);
        
-                                       memcpy(result, Z_STRVAL_PP(tmp_str), f);
-                                       memcpy((result + f), Z_STRVAL_PP(tmp_str) + f + l, Z_STRLEN_PP(tmp_str) - f - l);
+                                       memcpy(result, Z_STRVAL_P(orig_str), f);
+                                       memcpy((result + f), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l);
                                }
                        } else {
                                result_len += Z_STRLEN_PP(repl);
 
                                result = emalloc(result_len + 1);
 
-                               memcpy(result, Z_STRVAL_PP(tmp_str), f);
+                               memcpy(result, Z_STRVAL_P(orig_str), f);
                                memcpy((result + f), Z_STRVAL_PP(repl), Z_STRLEN_PP(repl));
-                               memcpy((result + f + Z_STRLEN_PP(repl)), Z_STRVAL_PP(tmp_str) + f + l, Z_STRLEN_PP(tmp_str) - f - l);
+                               memcpy((result + f + Z_STRLEN_PP(repl)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l);
                        }
 
                        result[result_len] = '\0';
                        add_next_index_stringl(return_value, result, result_len, 0);
-
+                       if(Z_TYPE_PP(tmp_str) != IS_STRING) {
+                               zval_dtor(orig_str);
+                       }
                        zend_hash_move_forward_ex(Z_ARRVAL_PP(str), &pos_str);
                } /*while*/
        } /* if */
diff --git a/ext/standard/tests/strings/bug54238.phpt b/ext/standard/tests/strings/bug54238.phpt
new file mode 100644 (file)
index 0000000..0f60098
--- /dev/null
@@ -0,0 +1,25 @@
+--TEST--
+Bug #54238 (use-after-free in substr_replace())
+--INI--
+error_reporting=E_ALL&~E_NOTICE
+--FILE--
+<?php
+$f = array(array('A', 'A'));
+
+$z = substr_replace($f, $f, $f, 1);
+var_dump($z, $f);
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  string(9) "AArrayray"
+}
+array(1) {
+  [0]=>
+  array(2) {
+    [0]=>
+    string(1) "A"
+    [1]=>
+    string(1) "A"
+  }
+}