php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #72146 Integer overflow on substr_replace
Submitted: 2016-05-03 19:06 UTC Modified: 2016-05-13 02:18 UTC
From: fernando at null-life dot com Assigned:
Status: Open Package: *General Issues
PHP Version: 5.6.21 OS: Linux
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2016-05-03 19:06 UTC] fernando at null-life dot com
Description:
------------
This was tested on a 32 bits build with ASAN enabled.

Supplying len as the MAX_INT value on substr_replace is able to evade some checks on the code and generates an integer overflow calculating offsets during memcpy calls.

The memcpy in line 2580  ext/standard/string.c seems to be the guilty one:

memcpy(result, Z_STRVAL_P(orig_str), f);
memcpy((result + f), Z_STRVAL_PP(repl), Z_STRLEN_PP(repl));
memcpy((result + f + Z_STRLEN_PP(repl)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l); 

l is the MAX_INT value and when added with f and orig_str length it will generate a negative offset (overflow)

After setting a bp on that memcpy and checking the stack you can see that orig_str points to 0xf5986ac0, after calculating the offset adding f and l it will start pointing to 0x75986ac2 (src memcpy). 

The 0x80000003  value on the stack is also a negative value that becomes a huge one when used inside memcpy since it expects an unsigned value (size memcpy)

Reading symbols from /home/user/php/php56dbg/sapi/cli/php...done.
(gdb) b *0x838d9fa
Breakpoint 1 at 0x838d9fa: file /home/user/php/php56dbg/ext/standard/string.c, line 2580.
(gdb) r substr-rep.php
Starting program: /home/user/php/php56dbg/sapi/cli/php substr-rep.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".

Breakpoint 1, 0x0838d9fa in zif_substr_replace (ht=4, return_value=0xf5a8b9e8, return_value_ptr=0xf5a700f4, this_ptr=0x0, return_value_used=0) at /home/user/php/php56dbg/ext/standard/string.c:2580
2580                                    memcpy((result + f + Z_STRLEN_PP(repl)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l);
(gdb) x/20x $esp
0xffff9f80:     0x7570403e      0x75986ac2      0x80000003      0x00000983
0xffff9f90:     0x08b47438      0x00000000      0xf5a700f4      0xf5a8b9e8
0xffff9fa0:     0xf5a701c8      0xf5a701d0      0xf5a701d4      0xf5a701cc
0xffff9fb0:     0xf5a8b7d8      0x00000009      0xffff9fe8      0x0845335b
0xffff9fc0:     0xf5a8b7e4      0x00000000      0x00000000      0x00000000
(gdb) p l
$1 = 2147483647
(gdb) p *orig_str
$6 = {value = {lval = -174560576, dval = 1.2645730362071774e-313, str = {val = 0xf5986ac0 "ABCDE", len = 5}, ht = 0xf5986ac0, obj = {handle = 4120406720, handlers = 0x5}, ast = 0xf5986ac0}, refcount__gc = 1, type = 6 '\006',
  is_ref__gc = 0 '\000'}


Test script:
---------------
<?php
ini_set('memory_limit', -1);

$var1=["ABCDE"];
$var2="123";
$var3=3;
$var4=2147483647; // int max

substr_replace($var1, $var2, $var3, $var4);

Expected result:
----------------
No crash

Actual result:
--------------
=================================================================
==20971==ERROR: AddressSanitizer: memcpy-param-overlap: memory ranges [0x70cbf836,0xf0cbf839) and [0x725192ba, 0xf25192bd) overlap
    #0 0xf72a28ca in __asan_memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8a8ca)
    #1 0xf72a2c2f in memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8ac2f)
    #2 0x90973f1 in memcpy /usr/include/i386-linux-gnu/bits/string3.h:53
    #3 0x90973f1 in zif_substr_replace /ramdisk/php-56/ext/standard/string.c:2580
    #4 0x9a62ed1 in zend_do_fcall_common_helper_SPEC /ramdisk/php-56/Zend/zend_vm_execute.h:558
    #5 0x98cfa0f in execute_ex /ramdisk/php-56/Zend/zend_vm_execute.h:363
    #6 0x9a57204 in zend_execute /ramdisk/php-56/Zend/zend_vm_execute.h:388
    #7 0x95b24b1 in zend_execute_scripts /ramdisk/php-56/Zend/zend.c:1341
    #8 0x92d8f4b in php_execute_script /ramdisk/php-56/main/main.c:2613
    #9 0x9a6c4c9 in do_cli /ramdisk/php-56/sapi/cli/php_cli.c:994
    #10 0x808e5b4 in main /ramdisk/php-56/sapi/cli/php_cli.c:1378
    #11 0xf6aba636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #12 0x808eb6a  (/ramdisk/php-56/sapi/cli/php+0x808eb6a)

0x70cbf836 is located 54 bytes inside of 2147745792-byte region [0x70cbf800,0xf0cff800)
allocated by thread T0 here:
    #0 0xf72aed06 in malloc (/usr/lib/i386-linux-gnu/libasan.so.2+0x96d06)
    #1 0x941fcb7 in zend_mm_mem_malloc_alloc /ramdisk/php-56/Zend/zend_alloc.c:287

0xf0cff800 is located 0 bytes to the right of 2147745792-byte region [0x70cbf800,0xf0cff800)
allocated by thread T0 here:
    #0 0xf72aed06 in malloc (/usr/lib/i386-linux-gnu/libasan.so.2+0x96d06)
    #1 0x941fcb7 in zend_mm_mem_malloc_alloc /ramdisk/php-56/Zend/zend_alloc.c:287

SUMMARY: AddressSanitizer: memcpy-param-overlap ??:0 __asan_memcpy
==20971==ABORTING

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-05-13 02:18 UTC] stas@php.net
-Type: Security +Type: Bug
 [2016-05-13 02:18 UTC] stas@php.net
Doesn't look like security issue. May be size_t/int confusion again.
 [2016-12-07 18:41 UTC] fernando at null-life dot com
This issue (or a similar one) also affects PHP 7.1, keeps popping up on my server.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Fri May 24 21:01:26 2019 UTC