From:             arkadi at mebius dot lv
Operating system: Any, FreeBSD
PHP version:      4.3.7RC1
PHP Bug Type:     MySQL related
Bug description:  Incorrect use of mysql_thread_init/end() functions

Description:
------------
When PHP is compiled with --enable-experimental-zts (for Apache 2 Worker
MPM) the following code is enabled in ext/mysql/php_mysql.c in
ZEND_MODULE_STARTUP_D(mysql) and PHP_MSHUTDOWN_FUNCTION(mysql) functions:

init:
#ifdef ZTS
# if MYSQL_VERSION_ID >= 40000
        mysql_thread_init();
# endif
#endif

shutdown:
#ifdef ZTS
# if MYSQL_VERSION_ID >= 40000
        mysql_thread_end();
# endif
#endif

The use of these function is incorrect and may lead to segmentation fault
dependending on pthreads and mysql client library internal implementation
details. In case of FreeBSD 4.x pthreads it is a reproducible process
crash on module shutdown time.
mysql_thread_init() must be called at least once for every thread that use
mysql, but only after mysql_init() is called somewhere in the program.
mysql_thread_end() must be called only in case mysql_init() is called
before or else it will call pthread_getspecific() with an unallocated
pthread key as an argument with an undefined results (according to
specification). Then (on FreeBSD and in my particular PHP build) it will
try to deallocate condition variable using bogus memory address leading to
segfault.

Program received signal SIGSEGV, Segmentation fault.
0x28483089 in _atomic_lock () from /usr/lib/libc_r.so.4
(gdb) bt
#0  0x28483089 in _atomic_lock () from /usr/lib/libc_r.so.4
#1  0x28482ffb in _spinlock_debug () from /usr/lib/libc_r.so.4
#2  0x2848a53a in pthread_cond_destroy () from /usr/lib/libc_r.so.4
#3  0x281f1f28 in my_thread_end () at my_thr_init.c:209
#4  0x281e6460 in mysql_thread_end () at libmysql.c:136
#5  0x807c2ee in zm_shutdown_mysql ()
#6  0x0 in ?? ()
(gdb) up 3
#3  0x281f1f28 in my_thread_end () at my_thr_init.c:209
209         pthread_cond_destroy(&tmp->suspend);
(gdb) p *tmp
$9 = {thr_errno = 136209024, suspend = 0x17, mutex = 0x81bb000,
current_mutex = 0x0, current_cond = 0x81d6080, 
  pthread_self = 0x0, id = 0, cmp_length = 0, abort = 1130656883, init =
108 'l'}

On a related note I wish to say that PHP build process must link to
libmysqlclient_r.so (instead of libmysqlclient.so) in case thread safety
is enabled. There is patch easily found by Google to ext/mysql/config.m4,
for now I just edited Makefile.
 
Additional information:
- http://dev.mysql.com/doc/mysql/en/Threaded_clients.html (near the end of
the page)
- MySQL source: mysql-4.0.20/mysys/my_thr_init.c
mysql_thread_* function are just wrappers that calls my_thread_*.

my_bool my_thread_init(void)
{
  struct st_my_thread_var *tmp;
  my_bool error=0;

#ifdef EXTRA_DEBUG_THREADS
  fprintf(stderr,"my_thread_init(): thread_id=%ld\n",pthread_self());
#endif  
#if !defined(__WIN__) || defined(USE_TLS) || ! defined(SAFE_MUTEX)
  pthread_mutex_lock(&THR_LOCK_lock);
#endif

#if !defined(__WIN__) || defined(USE_TLS)
  if (my_pthread_getspecific(struct st_my_thread_var *,THR_KEY_mysys))
  {
#ifdef EXTRA_DEBUG_THREADS
    fprintf(stderr,"my_thread_init() called more than once in thread
%ld\n",
                pthread_self());
#endif    
    goto end;
  }
  if (!(tmp= (struct st_my_thread_var *) calloc(1, sizeof(*tmp))))
  {
    error= 1;
    goto end;
  }
  pthread_setspecific(THR_KEY_mysys,tmp);

#else
  /*
    Skip initialization if the thread specific variable is already
initialized
  */
  if (THR_KEY_mysys.id)
    goto end;
  tmp= &THR_KEY_mysys;
#endif
  tmp->id= ++thread_id;
#if defined(__WIN__) && defined(EMBEDDED_LIBRARY)
  tmp->thread_self= (pthread_t)getpid();
#endif
  pthread_mutex_init(&tmp->mutex,MY_MUTEX_INIT_FAST);
  pthread_cond_init(&tmp->suspend, NULL);
  tmp->init= 1;

end:
#if !defined(__WIN__) || defined(USE_TLS) || ! defined(SAFE_MUTEX)
  pthread_mutex_unlock(&THR_LOCK_lock);
#endif
  return error;
}

void my_thread_end(void)
{
  struct st_my_thread_var *tmp;
  tmp= my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);

#ifdef EXTRA_DEBUG_THREADS
  fprintf(stderr,"my_thread_end(): tmp=%p,thread_id=%ld\n",
          tmp,pthread_self());
#endif  
  if (tmp && tmp->init)
  {
#if !defined(DBUG_OFF)
    /* tmp->dbug is allocated inside DBUG library */
    if (tmp->dbug)
    {
      free(tmp->dbug);
      tmp->dbug=0;
    }
#endif
#if !defined(__bsdi__) || defined(HAVE_mit_thread) /* bsdi dumps core here
*/
    pthread_cond_destroy(&tmp->suspend);
#endif
    pthread_mutex_destroy(&tmp->mutex);
#if (!defined(__WIN__) && !defined(OS2)) || defined(USE_TLS)
    free(tmp);
#else
    tmp->init= 0;
#endif
  }
  /* The following free has to be done, even if my_thread_var() is 0 */
#if (!defined(__WIN__) && !defined(OS2)) || defined(USE_TLS)
  pthread_setspecific(THR_KEY_mysys,0);
#endif
}



-- 
Edit bug report at http://bugs.php.net/?id=28547&edit=1
-- 
Try a CVS snapshot (php4):  http://bugs.php.net/fix.php?id=28547&r=trysnapshot4
Try a CVS snapshot (php5):  http://bugs.php.net/fix.php?id=28547&r=trysnapshot5
Fixed in CVS:               http://bugs.php.net/fix.php?id=28547&r=fixedcvs
Fixed in release:           http://bugs.php.net/fix.php?id=28547&r=alreadyfixed
Need backtrace:             http://bugs.php.net/fix.php?id=28547&r=needtrace
Need Reproduce Script:      http://bugs.php.net/fix.php?id=28547&r=needscript
Try newer version:          http://bugs.php.net/fix.php?id=28547&r=oldversion
Not developer issue:        http://bugs.php.net/fix.php?id=28547&r=support
Expected behavior:          http://bugs.php.net/fix.php?id=28547&r=notwrong
Not enough info:            http://bugs.php.net/fix.php?id=28547&r=notenoughinfo
Submitted twice:            http://bugs.php.net/fix.php?id=28547&r=submittedtwice
register_globals:           http://bugs.php.net/fix.php?id=28547&r=globals
PHP 3 support discontinued: http://bugs.php.net/fix.php?id=28547&r=php3
Daylight Savings:           http://bugs.php.net/fix.php?id=28547&r=dst
IIS Stability:              http://bugs.php.net/fix.php?id=28547&r=isapi
Install GNU Sed:            http://bugs.php.net/fix.php?id=28547&r=gnused
Floating point limitations: http://bugs.php.net/fix.php?id=28547&r=float

Reply via email to