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

Reply via email to