From: [EMAIL PROTECTED] Operating system: Linux Redhat 7.1 PHP version: 4.0.6 PHP Bug Type: Reproducible crash Bug description: Double-free causes segfault in domxml
The domxml extension frees memory twice when an XPath query is used which results in an empty nodelist. I'm providing a PHP script which reproduces the bug, and a patch to fix it. How to reproduce: $xml = 'xml fragment'; $doc = xmldoc($xml); $xh = $doc->xpath_new_context(); $nodes = xpath_eval($xh, "xpath expression which doesn't match anything"); When the xpath expression doesn't match any nodes, then the xmlXPathObjectPtr which gets created by the query gets freed twice. Usually a segfault won't occur unless you do 8 or more non-matching XPath queries, since a single double-free won't always cause a segfault. But if you run php with ElectricFence, a malloc debugging library, then a single non-matching XPath query will generate the double-free error. The bug is caused in the php_xpathptr_eval() function in php_domxml.c. It calls xmlXPathEval() and saves the result in xpathobjp. It then inserts xpathobjp into a zend list, le_xpathobjectp. Later in the function, it checks whether xpathobjp contains an empty nodeset, and if it does, it calls xmlXPathFreeObject(xpathobjp) and returns FALSE. But it didn't remove the xpathobjp pointer from the zend list before deleting it, so when PHP exits and cleans up the objects, the pointer sitting on the le_xpathobjp list gets freed again. I fixed the bug by checking whether the nodeset is empty before it gets put on the zend list, and if it is empty, I free it and just return FALSE from the function. I'm appending my patch to php_domxml.c, a PHP program that triggers the bug in php 4.0.6, and also a stack trace showing where the crash occurs. -- patch to php_domxml.c -- *** php-4.0.6-orig/ext/domxml/php_domxml.c Thu May 24 08:41:46 2001 --- php-4.0.6/ext/domxml/php_domxml.c Tue Dec 11 01:06:08 2001 *************** *** 1681,1686 **** --- 1681,1690 ---- if (!xpathobjp) { RETURN_FALSE; } + if (xpathobjp->type == XPATH_NODESET && !xpathobjp->nodesetval) { + xmlXPathFreeObject(xpathobjp); + RETURN_FALSE; + } ret = zend_list_insert(xpathobjp, le_xpathobjectp); zend_list_addref(ret); -- end patch -- -- STACK TRACE -- Program received signal SIGSEGV, Segmentation fault. chunk_free (ar_ptr=0x402fea00, p=0x8185180) at malloc.c:3180 3180 in malloc.c (gdb) bt #0 chunk_free (ar_ptr=0x402fea00, p=0x8185180) at malloc.c:3180 #1 0x4024acd4 in __libc_free (mem=0x8185188) at malloc.c:3154 #2 0x4039b747 in xmlXPathFreeObject () from /usr/lib/libxml2.so.2 #3 0x40019f9d in php_free_xpath_object (rsrc=0x818514c) at php_domxml.c:188 #4 0x080c84b3 in list_entry_destructor (ptr=0x818514c) at zend_list.c:179 #5 0x080c70fa in zend_hash_apply_deleter (ht=0x8133fd0, p=0x8171b5c) at zend_hash.c:615 #6 0x080c722a in zend_hash_graceful_destroy (ht=0x8133fd0) at zend_hash.c:666 #7 0x080c85e0 in zend_destroy_rsrc_list () at zend_list.c:234 #8 0x080bb42d in shutdown_executor () at zend_execute_API.c:179 #9 0x080c3265 in zend_deactivate () at zend.c:540 #10 0x0805de9d in php_request_shutdown (dummy=0x0) at main.c:660 #11 0x0805cfdd in main (argc=4, argv=0xbffff7f4) at cgi_main.c:751 #12 0x401e6627 in __libc_start_main (main=0x805c740 <main>, argc=4, ubp_av=0xbffff7f4, init=0x805ad5c <_init>, fini=0x80f4920 <_fini>, rtld_fini=0x4000dcd4 <_dl_fini>, stack_end=0xbffff7ec) at ../sysdeps/generic/libc-start.c:129 -- END STACK TRACE -- -- program to demonstrate bug -- Call this program with the serverid parameter set to 1 or 2 will work fine, but call it with serverid=3 and it will segfault. <? $xml = <<<EOT <config type="kvp" customer="customer0"> <!-- ****************** STREAMING MEDIA SERVERS ************ --> <mediaserverlist> <mediaserver id="1" type="windowsmedia"> <host> cambridge.knumi.com </host> <baseurl> mms://cambridge.knumi.com/mysore-customer0 </baseurl> <user> realxfer </user> <password> xferreal </password> <basedir> mysore-customer0 </basedir> <basedeleteddir> deleted-mysore-customer0 </basedeleteddir> <rpcurl> http://cambridge.knumi.com/cgi-bin/deletemediafiles.pl </rpcurl> </mediaserver> <mediaserver id="2" type="real"> <host> colo3.knumi.net </host> <baseurl> rtsp://colo3.knumi.net/mysore-customer0 </baseurl> <user> realxfer </user> <password> krs@9555 </password> <basedir> mysore-customer0 </basedir> <basedeleteddir> deleted-mysore-customer0 </basedeleteddir> <rpcurl> http://cambridge.knumi.com/cgi-bin/deletemediafiles.pl </rpcurl> </mediaserver> </mediaserverlist> </config> EOT; $serverArray = array(); if (!isset($serverid)) { $serverid = 2; } $doc = xmldoc($xml); $xp = $doc->xpath_new_context(); readServer($xp, "media", $serverid, "real"); print_r($serverArray); function readServer($xp, $sType, $sId, $sSubType) { global $serverArray; $xPath = "/config/" . $sType . "serverlist/" . $sType . "server[@type='$sSubType' and @id='$sId']/"; $serverArray[$sType][$sId][$sSubType]["host"] = getNodeValue($xp, $xPath . "host"); $serverArray[$sType][$sId][$sSubType]["port"] = getNodeValue($xp, $xPath . "port"); $serverArray[$sType][$sId][$sSubType]["baseurl"] = getNodeValue($xp, $xPath . "baseurl"); $serverArray[$sType][$sId][$sSubType]["name"] = getNodeValue($xp, $xPath . "name"); $serverArray[$sType][$sId][$sSubType]["user"] = getNodeValue($xp, $xPath . "user"); $serverArray[$sType][$sId][$sSubType]["password"] = getNodeValue($xp, $xPath . "password"); $serverArray[$sType][$sId][$sSubType]["indexdir"] = getNodeValue($xp, $xPath . "indexdir"); $serverArray[$sType][$sId][$sSubType]["basedir"] = getNodeValue($xp, $xPath . "basedir"); $serverArray[$sType][$sId][$sSubType]["basedeleteddir"] = getNodeValue($xp, $xPath . "basedeleteddir"); $serverArray[$sType][$sId][$sSubType]["rpcurl"] = getNodeValue($xp, $xPath . "rpcurl"); } function &getNode($xpath_context, $xpath_expression) { if (! $xpath_expression) { return false; } $nodes = xpath_eval($xpath_context, $xpath_expression); if (!$nodes || count($nodes->nodeset) == 0) { return false; } else { return $nodes->nodeset[0]; } } function getNodeValue($xpath_context, $xpath_expression) { $node = getNode($xpath_context, $xpath_expression); return $node ? trim($node->content) : false; } ?> --- end test program -- -- Edit bug report at: http://bugs.php.net/?id=14443&edit=1 -- PHP Development Mailing List <http://www.php.net/> To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] To contact the list administrators, e-mail: [EMAIL PROTECTED]