php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #42765 PDO ODBC: Long binary field in query result crashes PHP ("Out of memory" error)
Submitted: 2007-09-26 11:00 UTC Modified: 2017-07-16 04:22 UTC
Votes:24
Avg. Score:4.9 ± 0.3
Reproduced:23 of 23 (100.0%)
Same Version:4 (17.4%)
Same OS:5 (21.7%)
From: sms at inbox dot ru Assigned:
Status: No Feedback Package: PDO ODBC
PHP Version: 5.2.4 OS: Windows 2000 SP4
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: sms at inbox dot ru
New email:
PHP Version: OS:

 

 [2007-09-26 11:00 UTC] sms at inbox dot ru
Description:
------------
With PDO ODBC I can't get long binary data from Microsoft SQL Server (image and varbinary(MAX) fields). PDO->query, PDOStatement->execute() always result in PHP "Out of memory" error, even if output contains no rows.
The same queries work fine with ODBC unified extension.


Reproduce code:
---------------
<?php
$dbh=new PDO('odbc:Driver={SQL Server};Server=localhost;Database=test','user','pass');
$dbh->query("select [nbin] from [atts] where [id]=1");
?>

Expected result:
----------------
No PHP fatal errors

Actual result:
--------------
PHP Fatal error:  Out of memory (allocated 262144) (tried to allocate 4294967295 bytes) in D:\Web\test.php on line 3

Patches

patch-blob-uninitialized-odbc_stmt.c (last revision 2016-09-21 15:26 UTC by amistry at am-productions dot biz)

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2007-09-27 18:07 UTC] carlton dot whitehead at cebesius dot com
I encountered this bug with this setup:
Windows Server 2003 SP2 32bit
IIS 6
PHP 5.2.4
MS SQL Server 2005 Express SP2

PDO ODBC reproduce code:
---------------
<?php
// lobtestPdoOdbc.php
try
{
    $db = new PDO('odbc:fmarchive_mssql', 'change', 'me');
  
    $stp = 'SELECT attdata FROM fm_faxin_att WHERE id = 119085913400004
AND attid = 0'; // statement to prepare
    $ps = $db->prepare($stp);
    $execResult = $ps->execute();
    var_export($execResult, true);
}
catch (PDOException $e)
{
    die($e->getMessage());
}
?>

Expected result:
----------------
output to browser: true

Actual result:
--------------
*Fatal error*: Out of memory (allocated 262144) (tried to allocate
4294967295 bytes) in *C:\Inetpub\wwwroot\FMarchive\lobtestPdoOdbc.php*
on line *9*


plain ODBC reproduce code:
--------------------------
<?php
// lobtestOdbc.php
$res = odbc_connect('fmarchive_mssql', 'change', 'me');
if (!$res) { die ('failed to connect'); }

$stp = 'SELECT attdata FROM fm_faxin_att WHERE id = 119085913400004 AND attid = 0'; // statement to prepare
$ps = odbc_prepare($res, $stp);
if (!$res) { die ('failed to prepare statement'); }

$execResult = odbc_execute($ps);
echo var_export($execResult, true);
?>

Expected result:
----------------
output to browser: true

Actual result:
--------------
output to browser: true
(this indicates odbc_execute worked correctly)
 [2008-06-10 09:06 UTC] csa at dside dot dyndns dot org
I got the same problem on Linux (64bit, php 5.2.6). Actually, the problem is existing in all configurations. I have take a brief look through php sources. The bug is in pdo_odbc code and affects all architectures and underlying database engines.

Actually it is in 'ext/pdo_odbc/odbc_stmt.c', function 'odbc_stmt_describe'. The 'displaysize' variable is expected to contain  estimated size of the column. This could be a negative number if BLOB or TEXT data is stored (since the record sizes could seriously vary). However, later in this function the check on data size does not consider negative numbers. This causes the described behavior. This patch solves problem for me:

http://dside.dyndns.org/projects/patches.dir/php-ds-odbc_blob.patch

A Linux user trying to access MSSQL over FreeTDS may be interested in the following patches as well: 
http://dside.dyndns.org/projects/patches.dir/freetds-ds-odbc.patch
http://dside.dyndns.org/projects/patches.dir/php-ds-odbc64.patch
 [2008-06-10 09:08 UTC] csa at dside dot dyndns dot org
By the way feel free to contact me on csa@dside.dyndns.org if you have problems with this patches.
 [2008-10-03 15:34 UTC] jeffreybolle at gmail dot com
I had the same problem recently. I'd like to thank csa for the great source code patch.  Recompiling the source under windows wasn't easy and it took me many hours to piece together all the software and libraries required.  The result was a fixed extension that can access large blob files, this has been tested under Windows Vista 32bit.
I thought I'd post a link for the compiled extension (PHP 5.2.6) in case any other windows users want to make use of this fix without going through the hassle of learning how to compile PHP from source.

http://s3.paramorphicdesigns.com/random/php/php_pdo_odbc.dll

If there are any problems feel free to contact me at Jeffreybolle@gmail.com

Jeffrey
 [2008-10-03 21:41 UTC] pajoye@php.net
Thanks for the patches and testing.

About compiling php on windows, take a look here:

http://wiki.php.net/internals/windows
 [2009-04-25 14:50 UTC] jani@php.net
Please try using this CVS snapshot:

  http://snaps.php.net/php5.2-latest.tar.gz
 
For Windows:

  http://windows.php.net/snapshots/


 [2009-04-29 10:44 UTC] skettler@php.net
Confirmed not fixed with latest PHP 5.2 snapshot VC6 x86 Thread Safe (2009-Apr-27 00:00:00):

Fatal error: Out of memory (allocated 262144) (tried to allocate 4294967295 bytes)

Current workaround is getting the length of the image, retrieving chunks of 4096 characters and putting them back together in PHP.

SQL-Queries for this workaround look like these:

    SELECT DATALENGTH(imagefield) AS imagelength FROM imagetable WHERE imageid = ?

    SELECT CAST(SUBSTRING(imagefield, offset, length) AS VARCHAR(4096)) AS imagechunk FROM imagetable WHERE imageid = ?

 [2010-06-20 17:43 UTC] pajoye@php.net
-Status: Assigned +Status: Open -Assigned To: pajoye +Assigned To:
 [2010-06-20 17:43 UTC] pajoye@php.net
hm, I don't maintain odbc.

However I would suggest SqlServer users on Windows to use SqlSrv instead, much more stable and features complete.
 [2011-08-30 13:07 UTC] andreas at codejungle dot org
Bug is still existing in PHP 5.3.5 (os ubuntu natty)

The chunk workaround from skettler is working, but 22 000 queries for a 40 MB file is not optimal.

I tried also the php-ds-odbc_blob.patch, and the "Out of memory" error was gone,
but for some reasons, now the files are twice as large :(
 [2014-01-01 12:53 UTC] felipe@php.net
-Package: PDO related +Package: PDO ODBC
 [2014-10-06 15:58 UTC] joachim dot havloujian at fauser dot ag
I am getting the exact same error when fetching a long binary field from a Microsoft SQL Server.

The problem is that I rely on a PHP extension which supports MSSQL and the latest PHP build. Besides ODBC there are none which are fully working with the newest PHP version. The extension PHP_MSSQL isn't stated as deprecated but there is no official update. SqlSrv, the official driver from Microsoft, does only support PHP up to 5.4 and it looks like there won't be any update in the near future too.

That's why me and my coworkers would appreciate if this bug will be fixed soon.

Best regards.
 [2015-06-05 17:37 UTC] cmb@php.net
> SqlSrv, the official driver from Microsoft, does only support
> PHP up to 5.4 and it looks like there won't be any update in the
> near future too.

The drivers are actively maintained (again?):
<https://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx>.
 [2015-06-05 20:43 UTC] cmb@php.net
It appears that csa is right. Unfortunately, the patch isn't
available anymore.

Anyhow, I see two potential problems with the relevant code[1].
The first is that displaysize is not initialized, but the MSDN
documentation[2] states:

| Please note that some drivers may only write the lower 32-bit or
| 16-bit of a buffer and leave the higher-order bit unchanged.
| Therefore, applications should initialize the value to 0 before
| calling this function.

The second is that displaysize (signed) is assigned to colsize
(unsigned). However, the MSDN documentation[3] states:

| If the driver cannot determine the column or parameter length of
| variable types, it returns SQL_NO_TOTAL.

SQL_NO_TOTAL is defined to be -4.

[1] <https://github.com/php/php-src/blob/php-5.6.9/ext/pdo_odbc/odbc_stmt.c#L568-L578>
[2] <https://msdn.microsoft.com/en-us/library/ms713558(v=vs.85).aspx>
[3] <https://msdn.microsoft.com/en-us/library/ms713974(v=vs.85).aspx>
 [2016-09-21 15:16 UTC] amistry at am-productions dot biz
The following fixes the issue for me.

--- odbc_stmt.c.orig	2016-09-21 10:57:45.052396000 -0400
+++ odbc_stmt.c	2016-09-21 11:05:02.584261000 -0400
@@ -551,8 +551,8 @@
 	struct pdo_column_data *col = &stmt->columns[colno];
 	RETCODE rc;
 	SWORD	colnamelen;
-	SQLULEN	colsize;
-	SQLLEN displaysize;
+	SQLLEN	colsize = 0;
+	SQLLEN displaysize = 0;
 
 	rc = SQLDescribeCol(S->stmt, colno+1, S->cols[colno].colname,
 			sizeof(S->cols[colno].colname)-1, &colnamelen,
 [2016-09-21 17:48 UTC] amistry at am-productions dot biz
As an update to my last post.  That patch is for php 5.6 or later.  If you're trying make this work with an older version of php (eg. 5.5)  You'll need to also backport the fixes from 5.6 to odbc_stmt.c with regard to SQL_VARCHAR ,etc. before it will function correctly.
 [2016-11-02 18:28 UTC] cmb@php.net
-Assigned To: +Assigned To: cmb
 [2016-11-02 18:28 UTC] cmb@php.net
Thanks for the patch, Anish! Haven't tested it yet, but it looks good
to me.
 [2016-12-12 17:49 UTC] amistry at am-productions dot biz
cmb, any update on getting this committed?
 [2017-06-21 09:31 UTC] cmb@php.net
-Assigned To: cmb +Assigned To:
 [2017-06-29 08:26 UTC] adambaratz@php.net
I'm trying to reproduce this. Could you confirm some details so I can make sure I'm changing the right thing? cmb's notes about initializing a value to 0 relate to a different function.

This report is for a few versions of PHP 5.x, but odbc_stmt_describe looks largely the same between the PHP-5.6 branch and master. I'm testing with master on Debian 8. I tried both the FreeTDS and the MS ODBC drivers, connecting to SQL Server 2016. I tried this simple script:

<?php
$pdo = new PDO(...);
$stmt = $pdo->query("select cast(1 as varbinary(max)) as col");
var_dump($stmt->fetchAll());

I didn't get an error with either driver.
 [2017-07-05 17:57 UTC] adambaratz@php.net
-Status: Open +Status: Feedback
 [2017-07-16 04:22 UTC] php-bugs at lists dot php dot net
No feedback was provided. The bug is being suspended because
we assume that you are no longer experiencing the problem.
If this is not the case and you are able to provide the
information that was requested earlier, please do so and
change the status of the bug back to "Re-Opened". Thank you.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 09:01:32 2024 UTC