Edit report at https://bugs.php.net/bug.php?id=80460&edit=1
ID: 80460
Patch added by: cmb@php.net
Reported by: mirish at ibm dot com
Summary: ODBC doesn't account for SQL_NO_TOTAL indicator,
causing segmentation fault
Status: Analyzed
Type: Bug
Package: ODBC related
Operating System: RHEL 7.9
PHP Version: Irrelevant
Assigned To: cmb
Block user comment: N
Private report: N
New Comment:
The following pull request has been associated:
Patch Name: Fix #80460: ODBC doesn't account for SQL_NO_TOTAL indicator
On GitHub: https://github.com/php/php-src/pull/6809
Patch: https://github.com/php/php-src/pull/6809.patch
Previous Comments:
------------------------------------------------------------------------
[2021-03-26 10:16:36] cmb@php.net
Thanks for the clarification! Indeed, these SQL_NO_TOTAL checks
are necessary.
------------------------------------------------------------------------
[2021-03-25 20:04:34] mirish at ibm dot com
Ok, a little clarification: It looks like my particular error is generated from the StrLen_or_IndPtr
bound from SQLBindCol: https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindcol-function?view=sql-server-ver15
The valid values are still:
* The length of the data available to return
* SQL_NO_TOTAL
* SQL_NULL_DATA
Then, when odbc_fetch_row is called, SQLExtendedFetch gets called (in my particular test, but
SQLFetch and SQLGetData can equally return the same thing), populates that indicator bound in
SQLBindCol, and SQL_NO_TOTAL is returned to that StrLen_or_IndPtr, which is mapped in the php_odbc.c
code to result->values[field_ind].vallen.
Then, the code tries to generate a string from that length (-4), causing the seg fault.
Backtrace:
#0 0x00007f0d54ed9474 in __memcpy_ssse3_back () from /usr/lib64/libc.so.6
#1 0x0000000000591919 in zend_string_init (str=0x7f0d54491028 "",
len=18446744073709551612, persistent=0)
at /home/mirish/php-src/Zend/zend_string.h:157
#2 0x0000000000596dc1 in zif_odbc_result (execute_data=0x7f0d54414150, return_value=0x7f0d54414120)
at /home/mirish/php-src/ext/odbc/php_odbc.c:2224
#3 0x000000000085cbb1 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER () at
/home/mirish/php-src/Zend/zend_vm_execute.h:1313
#4 0x00000000008bc92c in execute_ex (ex=0x7f0d54414020) at
/home/mirish/php-src/Zend/zend_vm_execute.h:53564
#5 0x00000000008c09fa in zend_execute (op_array=0x7f0d5447e400, return_value=0x0)
at /home/mirish/php-src/Zend/zend_vm_execute.h:57664
#6 0x00000000007ef9fc in zend_execute_scripts (type=8, retval=0x0, file_count=3)
at /home/mirish/php-src/Zend/zend.c:1663
#7 0x000000000075bba0 in php_execute_script (primary_file=0x7ffd3adb2620) at
/home/mirish/php-src/main/main.c:2619
#8 0x00000000008c31c6 in do_cli (argc=2, argv=0x2c11700) at
/home/mirish/php-src/sapi/cli/php_cli.c:961
#9 0x00000000008c4105 in main (argc=2, argv=0x2c11700) at
/home/mirish/php-src/sapi/cli/php_cli.c:1352
And for good measure, here is the ODBC trace:
...
[ODBC][88259][1616702310.287337][SQLExecDirect.c][240]
Entry:
Statement = 0x1f66a90
SQL = [SELECT * FROM MIRISH.UTF8TEST][length = 29 (SQL_NTS)]
[ODBC][88259][1616702310.468968][SQLExecDirect.c][521]
Exit:[SQL_SUCCESS]
[ODBC][88259][1616702310.469037][SQLNumResultCols.c][156]
Entry:
Statement = 0x1f66a90
Column Count = 0x7effad458730
[ODBC][88259][1616702310.469087][SQLNumResultCols.c][251]
Exit:[SQL_SUCCESS]
Count = 0x7effad458730 -> 1
[ODBC][88259][1616702310.469128][SQLColAttribute.c][294]
Entry:
Statement = 0x1f66a90
Column Number = 1
Field Identifier = SQL_DESC_NAME
Character Attr = 0x7effad45c140
Buffer Length = 256
String Length = 0x7fff4dbce140
Numeric Attribute = (nil)
[ODBC][88259][1616702310.469188][SQLColAttribute.c][709]
Exit:[SQL_SUCCESS]
[ODBC][88259][1616702310.469217][SQLColAttribute.c][294]
Entry:
Statement = 0x1f66a90
Column Number = 1
Field Identifier = SQL_DESC_CONCISE_TYPE
Character Attr = (nil)
Buffer Length = 0
String Length = (nil)
Numeric Attribute = 0x7effad45c250
[ODBC][88259][1616702310.469252][SQLColAttribute.c][709]
Exit:[SQL_SUCCESS]
[ODBC][88259][1616702310.469280][SQLColAttribute.c][294]
Entry:
Statement = 0x1f66a90
Column Number = 1
Field Identifier = SQL_DESC_OCTET_LENGTH
Character Attr = (nil)
Buffer Length = 0
String Length = (nil)
Numeric Attribute = 0x7fff4dbce138
[ODBC][88259][1616702310.469309][SQLColAttribute.c][709]
Exit:[SQL_SUCCESS]
[ODBC][88259][1616702310.469338][SQLBindCol.c][236]
Entry:
Statement = 0x1f66a90
Column Number = 1
Target Type = 1 SQL_CHAR
Target Value = 0x7effad491028
Buffer Length = 2
StrLen Or Ind = 0x7effad45c248
[ODBC][88259][1616702310.469382][SQLBindCol.c][344]
Exit:[SQL_SUCCESS]
[ODBC][88259][1616702310.469452][SQLExtendedFetch.c][166]
Entry:
Statement = 0x1f66a90
Fetch Type = 1
Row = 1
PcRow = 0x7fff4dbce1a0
Row Status = 0x7fff4dbce190
[ODBC][88259][1616702310.560382][SQLExtendedFetch.c][339]
Exit:[SQL_SUCCESS]
------------------------------------------------------------------------
[2021-03-25 17:46:25] mirish at ibm dot com
Last comment should read:
result->values[i].vallen == SQL_NO_TOTAL
Similar to how it checks for SQL_NULL_DATA.
------------------------------------------------------------------------
[2021-03-25 17:44:23] mirish at ibm dot com
cmb@php.net noted:
"While the other bug report was indeed about SQL_NO_DATA, the fix
is supposed to cater to any result other than SQL_SUCCESS or
SQL_SUCCESS_WITH_INFO, and to return early in those cases."
I think I see the confusion now. When SQLGetData returns SQL_NO_TOTAL, it is doing so in the
StrLen_or_IndPtr parameter, not as the return code: https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetdata-function?view=sql-server-ver15
The issue you linked does indeed check the return code, but the issue here is that the valid values
returned from the StrLen_or_IndPtr are:
* The length of the data available to return
* SQL_NO_TOTAL
* SQL_NULL_DATA
The current code checks for SQL_NULL_DATA, and assigns a value of null if so. Otherwise, it just
assumes the value stored there is the length in bytes returned and tries to allocate a buffer of
that size. But that isn't the case when it returns SQL_NO_TOTAL (-4), which will try to
allocated a buffer of -4 and cause a segfault. There needs to be another check for:
result->values[i].vallen == SQL_NO_TOTAL
Similar to how it checks for SQL_NO_DATA.
I will work to generate a backtrace, might take a moment of relearning how to build PHP on my
system.
------------------------------------------------------------------------
[2021-02-08 13:30:14] cmb@php.net
> according to IBM support we have a related problem [â¦]
This ticket is about a segmentation fault in the ODBC extension;
your issue is about truncation of strings in the PDO_ODBC
extension. Please report that as separate ticket.
------------------------------------------------------------------------
The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
https://bugs.php.net/bug.php?id=80460
--
Edit this bug report at https://bugs.php.net/bug.php?id=80460&edit=1