php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #63327 Crash (Bus Error) in mysqlnd due to wrong alignment
Submitted: 2012-10-21 21:26 UTC Modified: 2012-10-23 11:43 UTC
Votes:3
Avg. Score:4.3 ± 0.9
Reproduced:1 of 2 (50.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: rainer dot jung at kippdata dot de Assigned: mysql
Status: Assigned Package: MySQL related
PHP Version: 5.3.18 OS: Solaris Sparc
Private report: No CVE-ID:
Have you experienced this issue?
Rate the importance of this bug to you:

 [2012-10-21 21:26 UTC] 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)


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-10-21 21:51 UTC] rainer dot jung at kippdata dot de
-Package: MSSQL related +Package: MySQL related
 [2012-10-21 21:51 UTC] rainer dot jung at kippdata dot de
Changed Package from MSSQL to MySQL. Original Package was chosen in error.
 [2012-10-23 05:32 UTC] laruence@php.net
-Assigned To: +Assigned To: uw
 [2012-10-23 11:43 UTC] uw@php.net
-Assigned To: uw +Assigned To: mysql
 [2014-01-08 01:10 UTC] nmalinoski at gmail dot com
I encountered this issue with PHP 5.5 on gentoo Linux on SPARC, but not 5.4.

The hardware is a Sun Ultra 10, with an UltraSPARC IIi (Sabre). I've verified it also occurs with my V210, which has an UltraSPARC IIIi (Jalapeno). I have PHP 5.4.23-pl0-gentoo and 5.5.7-pl0-gentoo installed, cli and mod_php for both.

Using 5.5 (this has happened with previous builds of 5.5 as well), my sites would load until they hit mysql_connect or mysqli_connect, at which point the connection would reset, and the error log would get one or two "child pid #### exit signal Bus error (10)". I built a small cli script (similar to the submitter's test script), which would fail at the same point, with "Bus error". 

No crash with sites not using MySQL (Sites with no databases)
No crash when mysqlnd.collect_memory_statistics is set to Off in php.ini
No crash when using 5.4.
No crash when using 5.5 on my x86 web server (Also gentoo, same build parameters as the SPARC boxes)
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Wed Aug 16 15:01:42 2017 UTC