Bug #73171 [NEW]: heap corruption due to integer overflow

From: Date: Mon, 26 Sep 2016 07:44:08 +0000
Subject: Bug #73171 [NEW]: heap corruption due to integer overflow
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-204267@lists.php.net to get a copy of this message
From: minhrau dot vc dot 365 at gmail dot com Operating system: ALL PHP version: 5.6.26 Package: PCRE related Bug Type: Bug Bug description:heap corruption due to integer overflow Description: ------------ Overflow in function php_pcre_replace_impl will caused to heap corruption. Please check the comment below and the crash analysis: PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *replace_val, int is_callable_replace, int *result_len, int limit, int *replace_count TSRMLS_DC) { ... alloc_len = 2 * subject_len + 1; result = safe_emalloc(alloc_len, sizeof(char), 0); ... if (new_len + 1 > alloc_len) { alloc_len = 1 + alloc_len + 2 * new_len; //the alloc_len here overflow new_buf = emalloc(alloc_len); memcpy(new_buf, result, *result_len); efree(result); result = new_buf; } /* copy the part of the string before the match */ memcpy(&result[*result_len], piece, match-piece); *result_len += match-piece; /* copy replacement and backrefs */ walkbuf = result + *result_len; /* If evaluating or using custom function, copy result to the buffer * and clean up. */ if (eval || is_callable_replace) { memcpy(walkbuf, eval_result, eval_result_len); *result_len += eval_result_len; STR_FREE(eval_result); } else { /* do regular backreference copying */ walk = replace; walk_last = 0; while (walk < replace_end) { // this loop for 2147483635 time if ('\\' == *walk || '$' == *walk) { if (walk_last == '\\') { *(walkbuf-1) = *walk++; walk_last = 0; continue; } if (preg_get_backref(&walk, &backref)) { if (backref < count) { match_len = offsets[(backref<<1)+1] - offsets[backref<<1]; memcpy(walkbuf, subject + offsets[backref<<1], match_len); walkbuf += match_len; } continue; } } *walkbuf++ = *walk++; //crash walk_last = walk[-1]; } Test script: --------------- <?php ini_set('memory_limit', -1); $string = 'April 15, 2003'.str_repeat('a', 100); $pattern = '/(\w+) (\d+), (\d+)/i'; $replacement = '${1}1,'.str_repeat('a', 0xffffffff/2-20).'$3'; $a = preg_replace($pattern, $replacement, $string); ?> Expected result: ---------------- No Crash Actual result: -------------- Starting program: /home/minhrau/PHP-5.6.26/sapi/cli/php ~/phptestcase/testpreg_replace.php [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Breakpoint 1, php_pcre_replace_impl (pce=0x1622ed0, subject=0x1623720 "April 15, 2003", 'a' <repeats 100 times>, subject_len=114, replace_val=0x1622160, is_callable_replace=0, result_len=0x7fffffffa61c, limit=-1, replace_count=0x7fffffffa5ec) at /home/minhrau/PHP-5.6.26/ext/pcre/php_pcre.c:1213 1213 if (new_len + 1 > alloc_len) { (gdb) p new_len+1 $6 = 2147483639 (gdb) p alloc_len $7 = 229 (gdb) b php_pcre.c:1149 Breakpoint 2 at 0x503c5f: file /home/minhrau/PHP-5.6.26/ext/pcre/php_pcre.c, line 1149. (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/minhrau/PHP-5.6.26/sapi/cli/php ~/phptestcase/testpreg_replace.php [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Breakpoint 2, php_pcre_replace_impl (pce=0x1622ed0, subject=0x1623720 "April 15, 2003", 'a' <repeats 100 times>, subject_len=114, replace_val=0x1622160, is_callable_replace=0, result_len=0x7fffffffa61c, limit=-1, replace_count=0x7fffffffa5ec) at /home/minhrau/PHP-5.6.26/ext/pcre/php_pcre.c:1149 1149 result = safe_emalloc(alloc_len, sizeof(char), 0); (gdb) p alloc_len $8 = 229 (gdb) p subject_len $9 = 114 (gdb) c Continuing. Breakpoint 1, php_pcre_replace_impl (pce=0x1622ed0, subject=0x1623720 "April 15, 2003", 'a' <repeats 100 times>, subject_len=114, replace_val=0x1622160, is_callable_replace=0, result_len=0x7fffffffa61c, limit=-1, replace_count=0x7fffffffa5ec) at /home/minhrau/PHP-5.6.26/ext/pcre/php_pcre.c:1213 1213 if (new_len + 1 > alloc_len) { (gdb) step 1214 alloc_len = 1 + alloc_len + 2 * new_len; (gdb) p 1 + alloc_len + 2 * new_len $10 = 210 (gdb) p alloc_len $11 = 229 (gdb) p new_len $12 = 2147483638 (gdb) step 1215 new_buf = emalloc(alloc_len); (gdb) p alloc_len $13 = 210 (gdb) step _emalloc (size=210) at /home/minhrau/PHP-5.6.26/Zend/zend_alloc.c:2426 2426 if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) { (gdb) 2427 return AG(mm_heap)->_malloc(size); (gdb) 2430 } (gdb) php_pcre_replace_impl (pce=0x1622ed0, subject=0x1623720 "April 15, 2003", 'a' <repeats 100 times>, subject_len=114, replace_val=0x1622160, is_callable_replace=0, result_len=0x7fffffffa61c, limit=-1, replace_count=0x7fffffffa5ec) at /home/minhrau/PHP-5.6.26/ext/pcre/php_pcre.c:1216 1216 memcpy(new_buf, result, *result_len); (gdb) 1217 efree(result); (gdb) _efree (ptr=0x1622f80) at /home/minhrau/PHP-5.6.26/Zend/zend_alloc.c:2436 2436 if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) { (gdb) 2437 AG(mm_heap)->_free(ptr); (gdb) 2438 return; (gdb) 2441 } (gdb) php_pcre_replace_impl (pce=0x1622ed0, subject=0x1623720 "April 15, 2003", 'a' <repeats 100 times>, subject_len=114, replace_val=0x1622160, is_callable_replace=0, result_len=0x7fffffffa61c, limit=-1, replace_count=0x7fffffffa5ec) at /home/minhrau/PHP-5.6.26/ext/pcre/php_pcre.c:1218 1218 result = new_buf; (gdb) 1221 memcpy(&result[*result_len], piece, match-piece); (gdb) p match-piece $14 = 0 (gdb) step 1222 *result_len += match-piece; (gdb) 1225 walkbuf = result + *result_len; (gdb) p *result_len $15 = 0 (gdb) p result $16 = 0x1623070 "(\\1\361\377\177" (gdb) c Continuing. Breakpoint 3, php_pcre_replace_impl (pce=0x1622ed0, subject=0x1623720 "April 15, 2003", 'a' <repeats 100 times>, subject_len=114, replace_val=0x1622160, is_callable_replace=0, result_len=0x7fffffffa61c, limit=-1, replace_count=0x7fffffffa5ec) at /home/crazyboy/PHP-5.6.26/ext/pcre/php_pcre.c:1236 1236 while (walk < replace_end) { (gdb) p replace_end - walk $17 = 2147483635 Program received signal SIGSEGV, Segmentation fault. 0x00000000005041e9 in php_pcre_replace_impl (pce=0x1622ed0, subject=0x1623720 'a' <repeats 200 times>..., subject_len=114, replace_val=0x1622160, is_callable_replace=0, result_len=0x7fffffffa61c, limit=-1, replace_count=0x7fffffffa5ec) at /home/minhrau/PHP-5.6.26/ext/pcre/php_pcre.c:1252 1252 *walkbuf++ = *walk++; (gdb) -- Edit bug report at https://bugs.php.net/bug.php?id=73171&edit=1 -- Try a snapshot (PHP 5.4): https://bugs.php.net/fix.php?id=73171&r=trysnapshot54 Try a snapshot (PHP 5.5): https://bugs.php.net/fix.php?id=73171&r=trysnapshot55 Try a snapshot (trunk): https://bugs.php.net/fix.php?id=73171&r=trysnapshottrunk Fixed in SVN: https://bugs.php.net/fix.php?id=73171&r=fixed Fixed in release: https://bugs.php.net/fix.php?id=73171&r=alreadyfixed Need backtrace: https://bugs.php.net/fix.php?id=73171&r=needtrace Need Reproduce Script: https://bugs.php.net/fix.php?id=73171&r=needscript Try newer version: https://bugs.php.net/fix.php?id=73171&r=oldversion Not developer issue: https://bugs.php.net/fix.php?id=73171&r=support Expected behavior: https://bugs.php.net/fix.php?id=73171&r=notwrong Not enough info: https://bugs.php.net/fix.php?id=73171&r=notenoughinfo Submitted twice: https://bugs.php.net/fix.php?id=73171&r=submittedtwice register_globals: https://bugs.php.net/fix.php?id=73171&r=globals PHP 4 support discontinued: https://bugs.php.net/fix.php?id=73171&r=php4 Daylight Savings: https://bugs.php.net/fix.php?id=73171&r=dst IIS Stability: https://bugs.php.net/fix.php?id=73171&r=isapi Install GNU Sed: https://bugs.php.net/fix.php?id=73171&r=gnused Floating point limitations: https://bugs.php.net/fix.php?id=73171&r=float No Zend Extensions: https://bugs.php.net/fix.php?id=73171&r=nozend MySQL Configuration Error: https://bugs.php.net/fix.php?id=73171&r=mysqlcfg

« previous php.bugs (#204267) next »