php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78549 Stack overflow due to nested serialized input
Submitted: 2019-09-16 12:32 UTC Modified: 2019-09-30 08:32 UTC
From: marc dot schoenefeld at gmx dot org Assigned: nikic (profile)
Status: Closed Package: Reproducible crash
PHP Version: 7.3.9 OS: CentOS 7 / Generic
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: marc dot schoenefeld at gmx dot org
New email:
PHP Version: OS:

 

 [2019-09-16 12:32 UTC] marc dot schoenefeld at gmx dot org
Description:
------------
php 7.3.9 (tested with locally compiled build on CentOS 7) can be DoSed with serialized data, causing a stack overflow scenario [see below]. 
 


Test script:
---------------
<?php
$filehandle = fopen($argv[1], "r") or die("Unable to open file!");
$data = fread($filehandle,filesize($argv[1]));
fclose($filehandle);

$string = unserialize($data);
?>

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

Actual result:
--------------
php-7.3.9/sapi/cli/php sertest.php php_7_3_9_stack_overflow.ser 

 

Program received signal SIGSEGV, Segmentation fault.

php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dd240, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1345

1345 zend_long elements = parse_iv(start + 2);

Missing separate debuginfos, use: debuginfo-install libxml2-2.9.1-6.el7_2.3.x86_64 xz-libs-5.2.2-1.el7.x86_64

(gdb) bt

#0  php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dd240, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1345

#1  0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df268, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dd240) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

#2  php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dd100, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1370

#3  0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df230, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dd100) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

#4  php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dcfc0, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1370

#5  0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df1f8, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dcfc0) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

#6  php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dce80, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1370

#7  0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df1c0, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dce80) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

#8  php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dcd40, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1370

#9  0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df188, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dcd40) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

#10 php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dcc00, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1370

#11 0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df150, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dcc00) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

#12 php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dcac0, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1370

#13 0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df118, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dcac0) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

#14 php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dc980, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1370

#15 0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df0e0, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dc980) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

#16 php_var_unserialize_internal (rval=rval@entry=0x7fb2a70dc840, p=p@entry=0x7ffe3f34b120, max=max@entry=0x7fb2a833ad95 "", 

    var_hash=var_hash@entry=0x7ffe3f34b128, as_key=as_key@entry=0)

    at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:1370

#17 0x00000000006ca221 in process_nested_data (objprops=0, elements=0, ht=0x7fb2a70df0a8, var_hash=0x7ffe3f34b128, max=0x7fb2a833ad95 "", 

    p=0x7ffe3f34b120, rval=0x7fb2a70dc840) at /php/linux_build/php-7.3.9/ext/standard/var_unserializer.c:502

---Type <return> to continue, or q <return> to quit---  q

Quit

(gdb) disass $pc-12,$pc+10

Dump of assembler code from 0x6ca14e to 0x6ca164:

   0x00000000006ca14e <php_var_unserialize_internal+2430>: jne    0x6c9928 <php_var_unserialize_internal+344>

   0x00000000006ca154 <php_var_unserialize_internal+2436>: xor    esi,esi

   0x00000000006ca156 <php_var_unserialize_internal+2438>: add    r12,0x3

=> 0x00000000006ca15a <php_var_unserialize_internal+2442>: call   0x42ca21 <parse_iv2>

   0x00000000006ca15f <php_var_unserialize_internal+2447>: test   rbx,rbx

   0x00000000006ca162 <php_var_unserialize_internal+2450>: mov    r13,rax

End of assembler dump.

Patches

php_7_3_9_stack_overflow.ser.zip.b64.patch (last revision 2019-09-16 13:27 UTC by marc dot schoenefeld at gmx dot org)

Pull Requests

Pull requests:

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-09-16 12:52 UTC] nikic@php.net
What are the contents of php_7_3_9_stack_overflow.ser? I can imagine, but if you already have the test case...
 [2019-09-16 13:27 UTC] marc dot schoenefeld at gmx dot org
The following patch has been added/updated:

Patch Name: php_7_3_9_stack_overflow.ser.zip.b64.patch
Revision:   1568640455
URL:        https://bugs.php.net/patch-display.php?bug=78549&patch=php_7_3_9_stack_overflow.ser.zip.b64.patch&revision=1568640455
 [2019-09-16 13:51 UTC] nikic@php.net
https://gist.githubusercontent.com/nikic/200240ca7830c6e5d587b3b4aad927eb/raw/d8b02e0bb1deca31025dbaf50a46ab4ab792bf68/php_7_3_9_stack_overflow.ser

I doubt we're going to rewriting the unserializer in a non-recursive fashion, so we can either introduce an arbitrary depth limit, or just ignore this.
 [2019-09-23 11:38 UTC] nikic@php.net
-Status: Open +Status: Verified
 [2019-09-23 11:38 UTC] nikic@php.net
Fuzzing seems to hit this quite often, so we should probably do something about it.

Script to find the limit:

<?php
$str = 'i:0;';
for ($i = 0; $i < 10000; $i++) {
    $str = 'a:1:{i:0;' . $str . '}';
    if ($i % 10 == 0) {
        echo "$i\n";
        unserialize($str);
    }
}
var_dump($str);

I get 5620 on a debug build and 7690 on a release/assert build.

Trying the same with O:8:"stdClass":1 I get 4630 and 7690.

We can add an unserialize option for the max_depth and default it to something like 4000.
 [2019-09-24 10:18 UTC] nikic@php.net
The following pull request has been associated:

Patch Name: Add max_depth option to unserialize()
On GitHub:  https://github.com/php/php-src/pull/4742
Patch:      https://github.com/php/php-src/pull/4742.patch
 [2019-09-30 08:32 UTC] nikic@php.net
-Status: Verified +Status: Closed -Assigned To: +Assigned To: nikic
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 10:01:29 2024 UTC