ID: 14443
Updated by: mfischer
Reported By: [EMAIL PROTECTED]
Status: Feedback
Bug Type: Reproducible crash
Operating System: Linux Redhat 7.1
PHP Version: 4.0.6
New Comment:

'The code has changed quite a lot ...' ...

Previous Comments:
------------------------------------------------------------------------

[2001-12-12 02:18:52] [EMAIL PROTECTED]

Ok, just don't forget to give feedback. The quite has changed but doesn't necessarily 
mean the bug has been fixed ;-)

Feedback.

------------------------------------------------------------------------

[2001-12-12 02:04:49] [EMAIL PROTECTED]

I just looked at the php4 code in the CVS tree and it 
appears that this bug is already fixed, in a better way 
than I did it.  We'll have to look into upgrading to 4.1 I 
guess.  Thank you.

Adam


------------------------------------------------------------------------

[2001-12-11 21:48:45] [EMAIL PROTECTED]

The code has changed quite a lot since 4.0.6. I can't reproduce the crash with the 
current CVS version. Please give it yourself a try.

Feedback.

PS: Patches not against CVS are most of the time useless because code tends to change 
a lot.

------------------------------------------------------------------------

[2001-12-11 21:02:16] [EMAIL PROTECTED]

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 this 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]

Reply via email to