php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80663 SplFixedArray writes to invalid memory if an element destructor calls setSize
Submitted: 2021-01-23 21:57 UTC Modified: 2021-01-24 00:43 UTC
From: tandre@php.net Assigned:
Status: Open Package: SPL related
PHP Version: 7.3.26 OS: Linux(any?)
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please — but make sure to vote on the bug!
Your email address:
MUST BE VALID
Solve the problem:
20 - 3 = ?
Subscribe to this entry?

 
 [2021-01-23 21:57 UTC] tandre@php.net
Description:
------------
I noticed when looking at spl classes that SplFixedArray (and probably SplObjectStorage for emalloced key-value entries) doesn't check if the underlying storage is freed in the middle of resizing, i.e. there are missing checks for re-entry due to __destruct being called.


It might be possible to fix those warnings by allocating a temporary buffer as a local variable, copying the range of zvals that need to be released, then calling those destructors. Or by explicitly forbidding resizing while modifying

Calling this a security bug may be excessive : In realistic applications, (it's probably the case that) nobody would write code like this and there might be other known issues, but it may be worse when combined with unserialize() issues or if buggy libraries using SplFixedArray are identified.

__set_state would have the same issue in zval_ptr_dtor_range (e.g. if an error handler triggered a call to setSize())

e.g. calling SplObjectStorage->detach() from __destruct when updating an existing SplObjectStorage entry may cause use after free, I haven't checked.

Mentioned in https://github.com/php/php-src/pull/6261

Test script:
---------------
<?php
// errors are reported with USE_ZEND_ALLOC=0 valgrind php, tested with 7.3, 7.4 and 8.0
class InvalidDestructor {
    public function __destruct() {
        $GLOBALS['obj']->setSize(0);
    }
}

$obj = new SplFixedArray(1000);
$obj[0] = new InvalidDestructor();
$obj->setSize(0);

Expected result:
----------------
No valgrind errors for double free or invalid memory accesses


Actual result:
--------------
```
...other valgrind spl errors
==99097== Invalid read of size 1
==99097==    at 0x7A40E4: zval_ptr_dtor (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x664023: zim_SplFixedArray_setSize (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x817557: execute_ex (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x817A59: zend_execute (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x7A6EFB: zend_execute_scripts (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x732B51: php_execute_script (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x8425DF: do_cli (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x363D91: main (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==  Address 0x949c0d9 is 25 bytes inside a block of size 16,000 free'd
==99097==    at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==99097==    by 0x664034: zim_SplFixedArray_setSize (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x817557: execute_ex (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x7969E7: zend_call_function (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x796E4A: zend_call_known_function (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x835BB3: zend_objects_destroy_object (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x83A672: zend_objects_store_del (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x664023: zim_SplFixedArray_setSize (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x817557: execute_ex (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x817A59: zend_execute (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x7A6EFB: zend_execute_scripts (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x732B51: php_execute_script (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==  Block was alloc'd at
==99097==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==99097==    by 0x76FF4C: __zend_malloc (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x663E1B: zim_SplFixedArray___construct (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x817557: execute_ex (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x817A59: zend_execute (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x7A6EFB: zend_execute_scripts (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x732B51: php_execute_script (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x8425DF: do_cli (in /path/to/php-8.0.1-nts-install/bin/php)
==99097==    by 0x363D91: main (in /path/to/php-8.0.1-nts-install/bin/php)
```

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-01-24 00:43 UTC] stas@php.net
-Type: Security +Type: Bug
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Fri Apr 16 15:01:24 2021 UTC