Bug #53517 [Opn->Csd]: segfault in pgsql_stmt_execute() when postgres is down
| From: | felipe@php.net | Date: | Fri, 10 Dec 2010 17:50:10 +0000 |
| Subject: | Bug #53517 [Opn->Csd]: segfault in pgsql_stmt_execute() when postgres is down | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-156883@lists.php.net to get a copy of this message | ||
Edit report at http://bugs.php.net/bug.php?id=53517&edit=1
ID: 53517
Updated by: felipe@php.net
Reported by: gyp at balabit dot hu
Summary: segfault in pgsql_stmt_execute() when postgres is
down
-Status: Open
+Status: Closed
Type: Bug
Package: PostgreSQL related
Operating System: Linux (Ubuntu)
PHP Version: 5.3SVN-2010-12-10 (snap)
-Assigned To:
+Assigned To: felipe
Block user comment: N
Private report: N
New Comment:
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!
Previous Comments:
------------------------------------------------------------------------
[2010-12-10 13:26:12] 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}
------------------------------------------------------------------------
--
Edit this bug report at http://bugs.php.net/bug.php?id=53517&edit=1