php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #53517 segfault in pgsql_stmt_execute() when postgres is down
Submitted: 2010-12-10 13:26 UTC Modified: 2010-12-10 18:50 UTC
From: gyp at balabit dot hu Assigned: felipe (profile)
Status: Closed Package: PostgreSQL related
PHP Version: 5.3SVN-2010-12-10 (snap) OS: Linux (Ubuntu)
Private report: No CVE-ID: None
 [2010-12-10 13:26 UTC] gyp at balabit dot hu
Description:
------------
I've happened to encounter a segfault when I'm using persistent PostgreSQL connections in PHP 5.3.2. I haven't had the time (and won't have in the upcoming couple of weeks, sorry) to play around with it to generate a narrowed-down example, but for me, it's happened in the following scenario:

1) PHP running in a Lighttpd + FastCGI setup
2) database connection is made with a PDO pgsql connection to the local host with PDO::ATTR_PERSISTENT set to true
3) use the database connection sucessfully
4) turn off the database server
5) try to use the database connection again

After this, instead of getting a "Connection failure"-style PDOException, PHP segfaults. (see backtrace below)

After a small investigation, it turned out to happen because of the lack of an error check in pgsql_statement.c. Specifically:

We're getting a segfault here, at line 187:

179 »···»···»···»···default: {
180 »···»···»···»···»···char *sqlstate = pdo_pgsql_sqlstate(S->result);
181 »···»···»···»···»···/* 42P05 means that the prepared statement already existed. this can happen if you use·
182 »···»···»···»···»··· * a connection pooling software line pgpool which doesn't close the db-connection once·
183 »···»···»···»···»··· * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no·
184 »···»···»···»···»··· * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we·
185 »···»···»···»···»··· * deallocate it and retry ONCE (thies 2005.12.15)
186 »···»···»···»···»··· */
187 »···»···»···»···»···if (!strcmp(sqlstate, "42P05")) {

Where pdo_pgsql_sqlstate() is:

92 #define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE)

Which, based on its documentation, can indeed return a NULL:

PQresultErrorField
    Returns an individual field of an error report.

    char *PQresultErrorField(const PGresult *res, int fieldcode);

    fieldcode is an error field identifier; see the symbols listed below. NULL is returned if the PGresult is not an error or warning result, or does not
include the specified field.


The attached patch fixed this problem for me. It trivially won't break the !strcmp... code path, and on the other branch
of the "if" statement also won't fail as sqlstate is passed over to pdo_pgsql_error_stmt(), which handles
it being NULL properly.

I've seen it happen with 5.3.2, but after peeking into the php-trunk source snapshots, it seems like this bug is still there.



Expected result:
----------------
A PDOException telling us that the database connection has failed.



Actual result:
--------------
PHP segfaults with the following backtrace:

#0  0x00007f7d06a655d9 in pgsql_stmt_execute (stmt=0x230ac08) at /usr/src/php5/php5-5.3.2-1ubuntu4.2.zorpos40.1/ext/pdo_pgsql/pgsql_statement.c:187
        sqlstate = 0x0
        S = 0x2349878
        H = 0x1f10030
        status = PGRES_FATAL_ERROR
#1  0x00007f7d06c7051a in zim_PDO_query (ht=1, return_value=0x2349530, return_value_ptr=0x230ac9c, this_ptr=0x22fc400, return_value_used=1)
    at /usr/src/php5/php5-5.3.2-1ubuntu4.2.zorpos40.1/ext/pdo/pdo_dbh.c:1109
        stmt = 0x230ac08
        statement = 0x1dd6920 "00000"
        statement_len = 0
#2  0x00000000006e594a in zend_do_fcall_common_helper_SPEC (execute_data=0x1e176d0) at
/usr/src/php5/php5-5.3.2-1ubuntu4.2.zorpos40.1/Zend/zend_vm_execute.h:313
        opline = 0x21416a8
        should_change_scope = 184 '\270'
#3  0x00000000006bcc30 in execute (op_array=0x1598470) at /usr/src/php5/php5-5.3.2-1ubuntu4.2.zorpos40.1/Zend/zend_vm_execute.h:104
        ret = 0
        execute_data = 0x1e176d0
        nested = 0 '\000'
        original_in_execution = 0 '\000'
#4  0x000000000069495d in zend_execute_scripts (type=0, retval=0x7fffe11a3440, file_count=3) at /usr/src/php5/php5-5.3.2-1ubuntu4.2.zorpos40.1/Zend/zend.c:1266
        files = 0x7fffe11a3418
        i = 1
        file_handle = 0x7fffe11a5a40
        orig_op_array = 0x0
        orig_retval_ptr_ptr = 0xd8fd30
#5  0x0000000000640608 in php_execute_script (primary_file=0x0) at /usr/src/php5/php5-5.3.2-1ubuntu4.2.zorpos40.1/main/main.c:2288
        __orig_bailout = 0x400000001
        __bailout = {{__jmpbuf = {0, 0, 0, 0, 32337904, 0, 181912586, 32637}, __mask_was_saved = 32, __saved_mask = {__val = {31439440, 0, 0, 0, 4294967295, 0,
0, 0, 
                181913152, 32637, 3776586748, 32767, 0, 0, 5, 0}}}}
        prepend_file_p = 0x0
        append_file_p = 0x0
        prepend_file = {type = 11236857, filename = 0x7f7d0aceee40 "", opened_path = 0x22fe5d4 ")))]", handle = {fd = 72, fp = 0x48, stream = {handle = 0x48, 
              isatty = 1, mmap = {len = 31424656, pos = 0, map = 0x7f7d0a9ec58e, buf = 0x7fffe11a2b40 "\310+\032\341\377\177", old_handle = 0x36034da734074900, 
                old_closer = 0x5}, reader = 0x1dfba50, fsizer = 0, closer = 0x1dfba50}}, free_filename = 212 '\324'}
        append_file = {type = 31439440, filename = 0x0, opened_path = 0xffffffff <Address 0xffffffff out of bounds>, handle = {fd = 181914002, fp =
0x7f7d0ad7c992, 
            stream = {handle = 0x7f7d0ad7c992, isatty = -518380544, mmap = {len = 181902120, pos = 3776586804, map = 0x7f7d0acee0e0, 
                buf = 0x7fffe11a2ac0 "(,\032\341\377\177", old_handle = 0x1dfba50, old_closer = 0}, reader = 0x5, fsizer = 0, closer = 0x7f7d0ad7cb30}}, 
          free_filename = 0 '\000'}
        retval = 0
#6  0x00000000007224f4 in main (argc=32767, argv=0x0) at /usr/src/php5/php5-5.3.2-1ubuntu4.2.zorpos40.1/sapi/cgi/cgi_main.c:2110
        __bailout = {{__jmpbuf = {0, 0, 0, 0, 2158641176, 3795055810, 14222272, 0}, __mask_was_saved = -1536278504, __saved_mask = {__val = {0 <repeats 16
times>}}}}
        free_query_string = 16777216
        exit_status = 0
        cgi = 0
        c = 0
        i = 14218272
        len = 14218272
        file_handle = {type = 165214479, filename = 0x4 <Address 0x4 out of bounds>, opened_path = 0x1335488 "/opt/scb/html/index.php", handle = {fd = 0, fp =
0x0, 
            stream = {handle = 0x0, isatty = 20260736, mmap = {len = 0, pos = 3953, map = 0x0, buf = 0x7f7d0d266000 <Address 0x7f7d0d266000 out of bounds>, 
                old_handle = 0x7f7d0d266000, old_closer = 0x13dea10}, reader = 0x6aa480 <zend_stream_stdio_closer>, fsizer = 0x6aaac0
<zend_stream_stdio_reader>, 
              closer = 0x6aa540 <zend_stream_stdio_fsizer>}}, free_filename = 64 '@'}
        s = 0x1334428 "/opt/scb/html/index.php"
        behavior = 0
        no_headers = 0
        orig_optind = 0
        orig_optarg = 0x0
        script_file = 0x100000000 <Address 0x100000000 out of bounds>
        max_requests = 1
        requests = 0
        fastcgi = 1
        bindpath = 0x100000001 <Address 0x100000001 out of bounds>
        fcgi_fd = 14218272
        request = {listen_socket = 0, fd = 11, id = 0, keep = 3, closed = 1, in_len = 0, in_pad = 0, out_hdr = 0x0, out_pos = 0x0, 
          out_buf = " [\032\341\377\177\000\000\001\006\000\001\bX\000\000\\r\\n    <\\/div>\\r\\n    \\r\\n    <div id=\\\"sm_disk_usage_tooltip\\\"
style=\\\"display: none\\\" class=\\\"tooltipWindow systemMonitorTooltip\\\">\\r\\n        <span class=\\\"systemMonitorTooltip\\\">\\r\\"..., 
          reserved = "<\\/span>\000\000\000\000\000\000\000", env = 0x0}
        repeats = 0
        benchmark = 0
        start = {tv_sec = 0, tv_usec = 0}
        end = {tv_sec = 0, tv_usec = 0}



Patches

pdo_psql_segfault_fix (last revision 2010-12-16 20:48 UTC by faisal_sm1987 at yahoo dot com)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-12-10 18:50 UTC] felipe@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: felipe
 [2010-12-10 18:50 UTC] felipe@php.net
This bug has been fixed in SVN.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.

Thanks for the patch!
 [2010-12-10 18:50 UTC] felipe@php.net
Automatic comment from SVN on behalf of felipe
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=306192
Log: - Fixed bug #53517 (segfault in pgsql_stmt_execute() when postgres is down)
  patch by: gyp at balabit dot hu
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 02:01:28 2024 UTC