Bug #63327 [Opn]: Crash (Bus Error) in mysqlnd due to wrong alignment
| From: | rainer dot jung at kippdata dot de | Date: | Sun, 21 Oct 2012 21:51:09 +0000 |
| Subject: | Bug #63327 [Opn]: Crash (Bus Error) in mysqlnd due to wrong alignment | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-174211@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=63327&edit=1
ID: 63327
User updated by: rainer dot jung at kippdata dot de
Reported by: rainer dot jung at kippdata dot de
Summary: Crash (Bus Error) in mysqlnd due to wrong alignment
Status: Open
Type: Bug
-Package: MSSQL related
+Package: MySQL related
Operating System: Solaris Sparc
PHP Version: 5.3.18
Block user comment: N
Private report: N
New Comment:
Changed Package from MSSQL to MySQL. Original Package was chosen in error.
Previous Comments:
------------------------------------------------------------------------
[2012-10-21 21:26:30] rainer dot jung at kippdata dot de
Description:
------------
All info refers to PHP 5.3.18. The problem goes back to much older 5.3 versions though. It seems
current HEAD contains the same problematic code.
The misalignment can happen as soon as mysqlnd.collect_memory_statistics=On. It occurs on Solaris
Sparc for 32 Bit builds. It is strictly reproducible on my Solaris 8 system, but not on Solaris 10.
This is due to the fact, that a memory allocation for a size that is only divisibleby 4 but not by 8
can return an address only divisible by 4 (this happens on my Solaris 8 system) but it can also
return a block of memory starting at an address divisible by 8 (happens by incident on my Solaris 10
system).
Crash happens in stack:
#0 0x002880f0 in php_mysqlnd_conn_init_pub (conn=0x70dcf4) at
/shared/build/autobuild/workdirs/20121021_163211/bld/php53/ext/mysqlnd/mysqlnd.c:2354
No locals.
#1 0x002880a0 in _mysqlnd_init (persistent=0 '\0') at
/shared/build/autobuild/workdirs/20121021_163211/bld/php53/ext/mysqlnd/mysqlnd.c:2380
ret = (MYSQLND *) 0x70dcf4
#2 0xfee742e0 in php_mysql_do_connect (ht=<value optimized out>, return_value=0x70dc40,
return_value_ptr=0x0, this_ptr=<value optimized out>, return_value_used=1,
persistent=<value optimized out>) at
/shared/build/autobuild/workdirs/20121021_163211/bld/php53/ext/mysql/php_mysql.c:965
index_ptr = (zend_rsrc_list_entry *) 0x70e758
new_index_ptr = {ptr = 0x69e8f0, type = 2778356, refcount = 7393472}
user = 0x70db98 "myuser"
passwd = 0x70dc00 "mypass"
host_and_port = 0x70e808 "myserv:3306"
socket = 0x0
tmp = <value optimized out>
host = 0x70dc10 "myserv"
user_len = 6
passwd_len = 6
host_len = 11
hashed_details = 0x70dc80 "mysql_myserv:3306_myuser_mypass_131072"
hashed_details_length = 38
port = 3306
client_flags = 131072
mysql = <value optimized out>
free_host = 1 '\001'
new_link = 1 '\001'
connect_timeout = 60
Analysis shows, that actually the crash happens immediately before entering
php_mysqlnd_conn_init_pub(), namely in line 2352 of ext/mysqlnd/mysqlnd.c:
2346 /* {{{ mysqlnd_conn::init */
2347 static enum_func_status
2348 MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC)
2349 {
2350 DBG_ENTER("mysqlnd_conn::init");
2351 mysqlnd_stats_init(&conn->stats, STAT_LAST);
2352 SET_ERROR_AFF_ROWS(conn);
The macro SET_ERROR_AFF_ROWS is defined in ext/mysqlnd/mysqlnd_priv.h as:
#define SET_ERROR_AFF_ROWS(s) (s)->upsert_status.affected_rows = (uint64_t) ~0
So it is important, that "affected_rows" is aligned correctly for 64 Bits. So lets check
the alignment of conn. On my Solaris Sparc system it has size sizeof(MYSQLND), which is 776 so
divisible by 8 and the structure should be correctly aligned.
But: this is only true if memory statistics for mysqlnd are turned off. If they are turned On, the
allocation of conn is actually done for 776 +sizeof(size_t) bytes. This is due to the followinglines
in ext/mysqlnd/mysqlnd_debug.c:
814 #define REAL_SIZE(s) (collect_memory_statistics? (s) + sizeof(size_t) : (s))
...
919 /* {{{ _mysqlnd_pecalloc */
920 void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent
MYSQLND_MEM_D)
921 {
...
932 ret = pecalloc(nmemb, REAL_SIZE(size), persistent);
So here instead of allocating 776 bytes, we allocate 776+sizeof(size_t) = 776+4 = 780 bytes which is
no longer divisible by 8! So memory allocation not necessarily allocates at 8 bytes alignment.
To fix it I expect you need to replace sizeof(size_t) in the following lines by a size that does not
reduce alignment for any allocation done in mysqlnd_debug.c. Using sizeof(uint64_t) might suffice
for the time being.
814 #define REAL_SIZE(s) (collect_memory_statistics? (s) + sizeof(size_t) : (s))
815 #define REAL_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) -
sizeof(size_t)) : (p))
816 #define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) +
sizeof(size_t)) : (p))
In HEAD you can find the same problem in file mysqlnd_alloc.c.
Note: this problem is *not* the same as the alignment problem in mysqlnd I reported 2.5 years ago in
https://bugs.php.net/bug.php?id=51583.
Regards,
Rainer
Test script:
---------------
<h2>MySQL Test</h2>
<?php
ini_set ('display_errors', true);
// Enter your database connection info here
$db_server = 'myserv';
$db_port = '3306';
$db_name = 'mysql';
$db_username = 'myuser';
$db_password = 'mypass';
echo 'Connecting ...<br>';
$connection = mysql_connect("$db_server:$db_port", $db_username, $db_password,
$db_name);
if (! $connection) {
echo "<br><b>ERROR - Unable to connect to database server
'$db_server:$db_port': ". mysql_error() . '</b><br>';
} else {
mysql_close($connection);
}
?>
Actual result:
--------------
<h2>MySQL Test</h2>
Connecting ...<br>Bus Error (core dumped)
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=63327&edit=1