]> granicus.if.org Git - php/commitdiff
Fixed bug #73174 - heap overflow in php_pcre_replace_impl
authorStanislav Malyshev <stas@php.net>
Thu, 29 Sep 2016 05:29:25 +0000 (22:29 -0700)
committerStanislav Malyshev <stas@php.net>
Thu, 29 Sep 2016 05:29:59 +0000 (22:29 -0700)
ext/pcre/php_pcre.c

index 7589a7803cac47462ac450ea8733e6afd376e0e2..2a8ff199b8ffcc2f49491dceb77d84f3b6753135 100644 (file)
@@ -1075,8 +1075,8 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub
        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                              eval_result_len=0;     /* Length of the eval'ed or
                                                                                   function-returned string */
        int                              match_len;                     /* Length of the current match */
@@ -1146,8 +1146,8 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub
 
        offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
 
-       alloc_len = 2 * subject_len + 1;
-       result = safe_emalloc(alloc_len, sizeof(char), 0);
+       result = safe_emalloc(subject_len, 2*sizeof(char), 1);
+       alloc_len = 2 * (size_t)subject_len + 1;
 
        /* Initialize */
        match = NULL;
@@ -1212,8 +1212,8 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub
                        }
 
                        if (new_len + 1 > alloc_len) {
-                               alloc_len = 1 + alloc_len + 2 * new_len;
-                               new_buf = emalloc(alloc_len);
+                               new_buf = safe_emalloc(2, new_len + 1, alloc_len);
+                               alloc_len = 1 + alloc_len + 2 * (size_t)new_len;
                                memcpy(new_buf, result, *result_len);
                                efree(result);
                                result = new_buf;
@@ -1276,8 +1276,8 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub
                        } else {
                                new_len = *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 */
-                                       new_buf = safe_emalloc(alloc_len, sizeof(char), 0);
+                                       new_buf = safe_emalloc(new_len, sizeof(char), 1);
+                                       alloc_len = (size_t)new_len + 1; /* now we know exactly how long it is */
                                        memcpy(new_buf, result, *result_len);
                                        efree(result);
                                        result = new_buf;
@@ -1308,6 +1308,12 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub
        efree(offsets);
        efree(subpat_names);
 
+       if(result && (size_t)(*result_len) > INT_MAX) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result is too big, max is %d", INT_MAX);
+               efree(result);
+               result = NULL;
+       }
+
        return result;
 }
 /* }}} */