php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #40926 shared pgsql and curl extensions cause segfault
Submitted: 2007-03-26 21:42 UTC Modified: 2008-12-25 20:03 UTC
Votes:34
Avg. Score:4.5 ± 0.8
Reproduced:32 of 32 (100.0%)
Same Version:14 (43.8%)
Same OS:18 (56.2%)
From: seanius at debian dot org Assigned: hholzgra (profile)
Status: Closed Package: PostgreSQL related
PHP Version: 5.2CVS-2008-05-15 OS: Debian GNU/Linux
Private report: No CVE-ID: None
 [2007-03-26 21:42 UTC] seanius at debian dot org
Description:
------------
note that this might not be a bug in php, but php is certainly affected by it so it's worth at least a bogus entry in your db so other people can google their way to it.

if you compile curl and pgsql as shared extensions and then load them in the same order from php.ini, any script that establishes a postgres connection will result in a segfault before the script quits.

the problem seems to be that the postgresql libpq library registers a callback function (pq_lockingcallback) for openssl-related locking.  around exit time, when php unloads the various extensions, if any modules reference openssl routines in their shutdown methods that indirectly call openssl locking routines, the ssl library will try and call the callback function, which now points at invalid memory since the libpq library has already been dlclose()'d somewhere.  if it's been closed directly by php (you guys would know better than me) then i'd say it's a php bug, but if it's closed indirectly by some pq shutdown routine, then you're just innocent victims.

anyway, there's a pretty simple workaround for the time being. if you reverse the module loading so order so that pgsql is loaded first (and thus unloaded last by the current php engine), then the callback function never references invalid memory and no segfault happens.

btw this was reported a couple times in the debian bts, most of the information can be found at 

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=411982

(this is for 5.2.0, but i've verified it against the latest 5.2.1 as well)

also, i found #36152 in your bts after writing this up, but it seems that was marked closed in 2006.  i think the suggestion in there about overriding libpq's callbacks with your own would probably be the most appropriate if possible.

Reproduce code:
---------------
<?php

// curl.so is loaded before pgsql.so in php.ini

$conn_string = "host=localhost dbname=data user=user password=pword";
$dbconn = pg_connect($conn_string);
$query = "SELECT count(1) FROM table";
$result = pg_query($dbconn, $query);
pg_close($dbconn);

?>




Expected result:
----------------
either a successfull connection or error messages about failed connection

Actual result:
--------------
the expected errors/success, followed by a segfault:

copelandia[~]23:30:08$ php foo.php

Warning: pg_connect(): Unable to connect to PostgreSQL server: FATAL:  password authentication failed for user "user" in /home/seanius/foo.php on line 6

Warning: pg_query(): supplied argument is not a valid PostgreSQL link resource in /home/seanius/foo.php on line 8

Warning: pg_close(): supplied argument is not a valid PostgreSQL link resource in /home/seanius/foo.php on line 9

zsh: segmentation fault  php foo.php

(gdb) bt
#0  0x00002b71ee8889a0 in ?? ()
#1  0x00002b71edf446df in int_err_del () at err.c:353
#2  0x00002b71ee4e9ef9 in Curl_ossl_cleanup ()
at ../../../lib/ssluse.c:580
#3  0x00002b71ee4f93e2 in Curl_ssl_cleanup ()
at ../../../lib/sslgen.c:185
#4  0x00002b71ee4f2233 in curl_global_cleanup ()
at ../../../lib/easy.c:294
#5  0x00002b71ee3a3699 in zm_shutdown_curl (type=9, module_number=1)
    at /tmp/buildd/php5-5.2.0/ext/curl/interface.c:668
...

where 0x00002b71ee8889a0 was formerly the address of the above mentioned locking callback function


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2007-03-26 22:00 UTC] tony2001@php.net
Assigned to the maintainer.
See also bug #36152.
 [2007-11-12 14:45 UTC] sam at zoy dot org
Hello, I did read the sources and studied them, and I can confirm
that it is a matter of callback jumping to an invalid address.

libpq's init_ssl_system() installs callbacks by calling
CRYPTO_set_id_callback() and CRYPTO_set_locking_callback(). This
function is called each time initialize_SSL() is called (for instance
through the PHP pg_connect() function) and does not keep a reference
counter, so libpq's destroy_SSL() has no way to know that it should
call a destroy_ssl_system() function, and there is no such function
anyway. So the callbacks are never removed.

But then, upon cleanup, PHP calls zend_shutdown() which properly
unloads pgsql.so and therefore the unused libpq.

Finally, the zend_shutdown procedure calls zm_shutdown_curl()
which in turn calls curl_global_cleanup() which leads to an
ERR_free_strings() call and eventually a CRYPTO_lock() call.
CRYPTO_lock() checks whether there are any callbacks to call,
finds one (the one installed by libpg), calls it, and crashes
because libpq was unloaded and hence the callback is no longer
in mapped memory.

There are a few ways to fix the problem, all of which are highly
unsatisfying or irrealist:

 - always ensure that pgsql.so is loaded before (and therefore
unloaded after) any other SSL-using library.

 - fix libpq so that it keeps a reference count when
initialize_SSL() is called, updates it when destroy_SSL() is
called, and remove SSL callbacks when the reference count
reaches zero.

 - fix libpq so that it removes the SSL callbacks when unloaded
(done in the library's .fini section)

 - hack PHP's module_destructor() so that it does not unload
a module if its name was pgsql.so (or maybe there is already
a mechanism for that).

None of these proposals is really safe because there might be other
conflicts due to libssl not being context-aware.

There is also the possibility to fix libssl by making it reentrant
or context-aware (just kidding, lol). In all cases, libssl can be
copiously blamed.
 [2008-03-01 14:17 UTC] jbq at caraldi dot com
Another workaround is to recompile PostgreSQL's libpq without OpenSSL support (ie the --with-openssl configure switch).  After all, OpenSSL is rarely needed in a typical LAPP installation.
 [2008-10-28 22:27 UTC] jani@php.net
yohgaki seems to have left us so de-assigning. 
 [2008-12-01 20:33 UTC] hholzgra@php.net
This is being worked on on the PG side, see "libpq callback unregistration" on 
http://wiki.postgresql.org/wiki/CommitFest_2008-11#Clients
 [2008-12-04 04:17 UTC] miracee at miracee dot de
PostgreSQL fixed this bug.

http://archives.postgresql.org/pgsql-committers/2008-12/msg00037.php

Fix will be available in PostgreSQL version 8.4+
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Thu Aug 22 09:01:27 2019 UTC