我们有一个使用 Microsoft SQL Server 2005 的旧版 Windows Web 应用程序。两年前,我们在 32 位虚拟化 Debian 系统上使用 PHP 和 ODBC 重写了该应用程序的一部分。该应用程序运行正常(大约一千个 SQL 请求中有一个会产生虚假数据,但这由应用程序处理)。使用的 Debian 软件包为:php5-odbc、odbcinst1debian1、tdsodbc、unixodbc、freetds-common。
现在我们想取消虚拟化,并将应用程序作为 Apache 虚拟主机安装在 64 位 Debian Lenny 系统上。但是 PHP 函数 odbc_fetch_object() 出现了问题。我有
echo "Before odbc_fetch_object(); $query\n"; flush();
if ($query) $row = odbc_fetch_object($query);
echo "After odbc_fetch_object();\n"; flush();
echo "Edition number $row->Id\n";
但是文本“After odbc_fetch_object()”及其后面的文本从未显示。
我通过 php5 直接调用它(php5-cli 包)来调试 PHP 文件。这次它确实从数据库获取数据(当前版本号,每周都会更改)。但在输出后我得到了错误消息字符串
ALERT - canary mismatch on efree() - heap overflow detected (attacker 'REMOTE_ADDR not set', file 'unknown')
您应该知道,Debian PHP5 已集成 Suhosin 补丁。它似乎发现了 odbc_fetch_object() 中的内存损坏。
我们尝试使用 valgrind 进行调试并抑制 Zend 内存分配:
USE_ZEND_ALLOC=0 valgrind --leak-check=full ./current.php
并得到以下输出:
==3831== Memcheck, a memory error detector.
==3831== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==3831== Using LibVEX rev 1854, a library for dynamic binary translation.
==3831== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==3831== Using valgrind-3.3.1-Debian, a dynamic binary instrumentation framework.
==3831== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==3831== For more details, rerun with: -v
==3831==
==3831== Invalid write of size 8
==3831== at 0xD64420C: (within /usr/lib/odbc/libtdsodbc.so)
==3831== by 0xB55E859: SQLColAttributes (in /usr/lib/libodbc.so.1.0.0)
==3831== by 0xB34AA37: odbc_bindcols (in /usr/lib/php5/20060613/odbc.so)
==3831== by 0xB350B86: zif_odbc_exec (in /usr/lib/php5/20060613/odbc.so)
==3831== by 0xBDEDC9C: (within /usr/lib/php5/20060613/suhosin.so)
==3831== by 0x6A5798: (within /usr/bin/php5)
==3831== by 0x691003: execute (in /usr/bin/php5)
==3831== by 0xBDEE125: (within /usr/lib/php5/20060613/suhosin.so)
==3831== by 0x66CDF7: zend_execute_scripts (in /usr/bin/php5)
==3831== by 0x627667: php_execute_script (in /usr/bin/php5)
==3831== by 0x6EBFF6: main (in /usr/bin/php5)
==3831== Address 0xd2b564c is 44 bytes inside a block of size 48 alloc'd
==3831== at 0x4C2260E: malloc (vg_replace_malloc.c:207)
==3831== by 0xB34A911: odbc_bindcols (in /usr/lib/php5/20060613/odbc.so)
==3831== by 0xB350B86: zif_odbc_exec (in /usr/lib/php5/20060613/odbc.so)
==3831== by 0xBDEDC9C: (within /usr/lib/php5/20060613/suhosin.so)
==3831== by 0x6A5798: (within /usr/bin/php5)
==3831== by 0x691003: execute (in /usr/bin/php5)
==3831== by 0xBDEE125: (within /usr/lib/php5/20060613/suhosin.so)
==3831== by 0x66CDF7: zend_execute_scripts (in /usr/bin/php5)
==3831== by 0x627667: php_execute_script (in /usr/bin/php5)
==3831== by 0x6EBFF6: main (in /usr/bin/php5)
Before odbc_fetch_object(): Resource id #6
After odbc_fetch_object()
Edition number 547
Some static text
==3831==
==3831== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 531 from 4)
==3831== malloc/free: in use at exit: 58,755 bytes in 1,558 blocks.
==3831== malloc/free: 22,559 allocs, 21,001 frees, 3,867,219 bytes allocated.
==3831== For counts of detected errors, rerun with: -v
==3831== searching for pointers to 1,558 not-freed blocks.
==3831== checked 1,223,080 bytes.
==3831==
==3831==
==3831== 2 bytes in 1 blocks are definitely lost in loss record 1 of 24
==3831== at 0x4C2260E: malloc (vg_replace_malloc.c:207)
==3831== by 0x7609D91: strdup (in /lib/libc-2.7.so)
==3831== by 0xBDDF74B: ???
==3831== by 0x68199D: zend_register_ini_entries (in /usr/bin/php5)
==3831== by 0xBDDFBCF: ???
==3831== by 0x6732DA: zend_startup_module_ex (in /usr/bin/php5)
==3831== by 0x67828A: zend_hash_apply (in /usr/bin/php5)
==3831== by 0x671B59: zend_startup_modules (in /usr/bin/php5)
==3831== by 0x628E22: php_module_startup (in /usr/bin/php5)
==3831== by 0x6EA71C: (within /usr/bin/php5)
==3831== by 0x6EAF31: main (in /usr/bin/php5)
==3831==
==3831==
==3831== 292 (52 direct, 240 indirect) bytes in 1 blocks are definitely lost in loss record 11 of 24
==3831== at 0x4C2260E: malloc (vg_replace_malloc.c:207)
==3831== by 0x766D52F: (within /lib/libc-2.7.so)
==3831== by 0x766DD06: __nss_database_lookup (in /lib/libc-2.7.so)
==3831== by 0xCC2631F: ???
==3831== by 0xCC2702C: ???
==3831== by 0x762C101: getpwuid_r (in /lib/libc-2.7.so)
==3831== by 0x762B9CE: getpwuid (in /lib/libc-2.7.so)
==3831== by 0xB59C2EF: ???
==3831== by 0xB599B2B: ???
==3831== by 0xB58A013: ???
==3831== by 0xB56307F: ???
==3831== by 0xB34896D: ???
==3831==
==3831==
==3831== 512 bytes in 1 blocks are definitely lost in loss record 17 of 24
==3831== at 0x4C22741: realloc (vg_replace_malloc.c:429)
==3831== by 0x678AC8: (within /usr/bin/php5)
==3831== by 0x678B44: (within /usr/bin/php5)
==3831== by 0x67AEF7: _zend_hash_add_or_update (in /usr/bin/php5)
==3831== by 0xBDED02C: ???
==3831== by 0xBDDE995: ???
==3831== by 0x677690: (within /usr/bin/php5)
==3831== by 0x6634B1: zend_llist_apply_with_del (in /usr/bin/php5)
==3831== by 0x677676: zend_startup_extensions (in /usr/bin/php5)
==3831== by 0x628E5B: php_module_startup (in /usr/bin/php5)
==3831== by 0x6EA71C: (within /usr/bin/php5)
==3831== by 0x6EAF31: main (in /usr/bin/php5)
==3831==
==3831== LEAK SUMMARY:
==3831== definitely lost: 566 bytes in 3 blocks.
==3831== indirectly lost: 240 bytes in 10 blocks.
==3831== possibly lost: 0 bytes in 0 blocks.
==3831== still reachable: 57,949 bytes in 1,545 blocks.
==3831== suppressed: 0 bytes in 0 blocks.
==3831== Reachable blocks (those to which a pointer was found) are not shown.
==3831== To see them, rerun with: --leak-check=full --show-reachable=yes
您能否建议一种解决方法来解决库 libtdsodbc.so 中似乎存在的内存分配错误?或者您知道除了获取源代码并自行修复错误之外我们还能做什么吗?
答案1
嗯,似乎 PHP odbc 驱动程序中还有更多 64 位错误。我报告了这个错误几年前转向 redhat;那是针对 RHEL4 上的 PHP 4.3.x,那时它已经在上游进行了修复,因此大概 Debian Lenny 已经有了修复版本。
除了自己修复,或者至少向上游提交错误报告,同时不在 64 位平台上使用 php-odbc 之外,我没有更好的建议。抱歉。
编辑:您可以尝试的一件事是根本不使用 ODBC,而是直接使用 TDS 接口。虽然我不确定是否存在 PHP 级别的 TDS 接口,但如果您使用 PHP 5.x 中提供的 PHP PDO 数据库接口,您至少可以做到这一点。请参阅 PDO_DBLIB 模块。
答案2
我给三位开发人员发了电子邮件http://freetds.org,在 PHP 提交了一个错误报告http://bugs.php.net/bug.php?id=50370以及 Debian (稍后会发布号码)。
对我们来说已经太晚了。我们打算取消这个项目,但我希望开发人员可以修复这个错误,这样其他人就不会像我们一样被如此残酷地阻止。
答案3
PHP 5.2.7 中不存在此错误,因为它们将 len 从 SDWORD 更改为正确的 SQLLEN 类型,该类型对于 64 位平台来说是 64 位。
问候 Frediano Ziglio (又名 freddy77)