Bug #5387 Core dump on mysql_stmt_execute
Submitted: 3 Sep 2004 11:42 Modified: 13 Sep 2004 21:10
Reporter: Bill McCaffrey Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server Severity:S2 (Serious)
Version:4.1.4 OS:Linux (Mandrake 9)
Assigned to: Assigned Account CPU Architecture:Any

[3 Sep 2004 11:42] Bill McCaffrey
Description:
I have an app that using a large number of stmt handles to run many different queries. After a time the client process segfaults on an assert in the update_stmt_fields function. Here is a chunk of the trace -

#5  0x409f50ad in __assert_fail () from /lib/i686/libc.so.6
#6  0x4007a781 in update_stmt_fields (stmt=0x6) at libmysql.c:2078
#7  0x4007b302 in mysql_stmt_execute (stmt=0x8635ec0) at libmysql.c:2711

The one thing that all the dumps have in common is that the stmt value is being changed to 6 somewhere within mysql_stmt_execute. I have verified in the core  dumps that the stmt and bind structures are not being corrupted. Changing the number of open stmt handles has not had an effect unless it is set very low (around 5).

Before the dump, I begin to see 1210 errors on a few of the queries going through this connection.

How to repeat:
I have been able to repeat the problem by running a sequence of 450 queries. Any changes to the queries or the sequence results in no segfault. These 450 were gathered from a capture of 800 queries run on a live connection and whittled down through trial and error. I have not been able to isolate any one thing or type of query.
Unfortunately I an not able to release a code sample as this is a module in a proprietary software package and legal will not even allowed to talk to anyone about it's design.
I have done extensive testing and review on the app and I can not find any errors that could cause this.
[6 Sep 2004 9:24] Konstantin Osipov
Stack trace clearly shows that stmt pointer given to update_stmt_fields is damaged.
This is the same pointer as you give to mysql_stmt_execute.
So we're most likely seeing a stack overrun, either inside libmysql, or inside your application.
A repeatable test case is required to debug the problem.
Could you provide us with one?
Thank you.
[8 Sep 2004 5:56] Bill McCaffrey
I have found an interesting things here. If I free all statements as soon as I get a 1210 error, then the segfault never happens.
Here is a program to create the 1210 "Incorrect arguments to mysql_stmt_execute" error. I am still working on some sample code to get the segfault.
Note that setting SIZE below 97 does not generate any errors. This was tested on server and client versions 4.1.3 and 4.1.4 witht he same results

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <mysql.h>

// values above 96 generate 1210 errors
#define SIZE    97

#define SERVER          ""
#define USERNAME        ""
#define PASSWORD        ""
#define DEFAULTDB       ""

//////////////////////////////////////////////////////////////////////
// statement cache entry

typedef struct {
     long bindcount;
     MYSQL_STMT* stmt;
     MYSQL_BIND* bind;
     long length[SIZE];
     my_bool isnull[SIZE];
     char data[SIZE][21];
} mycacheitem;

void populate_table(MYSQL* dbconn);
mycacheitem* build_cache(MYSQL* dbconn,int items);
void run_query(MYSQL* dbconn,mycacheitem* myitem,int start);

//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
  MYSQL* dbconn = 0;
  int cv,sv;
  int i,j;
  mycacheitem* cache[SIZE];

  dbconn = mysql_init(0);
  if(dbconn == 0) {
    puts("init error");
    exit(0);
  }

  if(mysql_real_connect(dbconn,SERVER,USERNAME,PASSWORD,DEFAULTDB,0,0,0) == 0) {
    puts("connect error");
    exit(0);
  }

  cv = mysql_get_client_version();
  sv = mysql_get_server_version(dbconn);
  printf("client version is %d\n",cv);
  printf("server version is %d\n",sv);

  if(cv < 40103) {
    puts("upgrade client libraries to at least 4.1.3");
    exit(0);
  }

  if(sv < 40103) {
    puts("upgrade server to at least 4.1.3");
    exit(0);
  }

  populate_table(dbconn);

  printf("building cache:\n");
  for(i=0;i<SIZE;i++) {
    putchar('.');
    cache[i] = build_cache(dbconn,i+1);
  }

  for(i=0;i<10;i++) {
    printf("\n%d:",i);
    for(j=0;j<SIZE;j++) {
      run_query(dbconn,cache[j],i);
      putchar('.');
    }
  }

  mysql_close(dbconn);
  return 0;
}

//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////

void populate_table(MYSQL* dbconn)
{
  char query[500];
  int i;

  if(mysql_query(dbconn,"drop table test") != 0) {
    printf("drop table: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
  }
  if(mysql_query(dbconn,"create table test (f1 int primary key)") != 0) {
    printf("create table: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
    exit(0);
  }

  puts("building table");
  for(i=0;i<SIZE*3;i++) {
    sprintf(query,"insert into test values (%d)",i);
    putchar('.');
    if(mysql_query(dbconn,query) != 0) {
      printf("insert: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
      exit(0);
    }
  }
  puts("done");
}

//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////

mycacheitem* build_cache(MYSQL* dbconn,int items)
{
  char query[SIZE*4+100];
  int i;
  int bindcount = 0;
  unsigned long size;
  MYSQL_BIND* bind;
  mycacheitem* myitem;

  myitem = malloc(sizeof(mycacheitem));
  memset(myitem,0,sizeof(mycacheitem));

  strcpy(query,"select * from test where f1 in ( ?");
  for(i=1;i<items;i++)
    strcat(query,", ?");
  strcat(query,")");

  myitem->stmt = mysql_stmt_init(dbconn);
  if (!myitem->stmt) {
    printf("stmt_init: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
    exit(0);
  }

  if (mysql_stmt_prepare(myitem->stmt, query, strlen(query))) {
    printf("stmt_prepare: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
    exit(0);
  }

  bindcount= mysql_stmt_param_count(myitem->stmt);
  if (bindcount <= 0) {
    printf("bindcount: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
    exit(0);
  }

  size = bindcount*sizeof(MYSQL_BIND);
  bind = malloc(size);
  memset(bind,0,size);
  myitem->bind = bind;
  myitem->bindcount = bindcount;

  for(i=0;i<bindcount;i++) {
    bind->buffer_type = MYSQL_TYPE_VAR_STRING;
    bind->buffer_length = 20;
    bind->buffer = myitem->data[i];
    bind->is_null = &(myitem->isnull[i]);
    bind->length = &(myitem->length[i]);
    bind++;
  }

  if (mysql_stmt_bind_param(myitem->stmt, myitem->bind)) {
    printf("bind param: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
    exit(0);
  }

  return myitem;
}

//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////

void run_query(MYSQL* dbconn,mycacheitem* myitem,int start)
{
  int i;
  MYSQL_BIND* bind;
  int myerr;

  bind = myitem->bind;

  for(i = 0;i < myitem->bindcount;i++) {
    sprintf(bind->buffer,"%d",start+i*2);
    *(bind->length) = strlen(bind->buffer);
    bind++;
  }

  if(mysql_stmt_execute(myitem->stmt)) {
    myerr = mysql_errno(dbconn);
    printf("\nexecute: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
    if(myerr == 1210)
      return;
    else
      exit(0);
  }
  
  if(mysql_stmt_store_result(myitem->stmt)) {
    printf("store result: %d %s\n",mysql_errno(dbconn),mysql_error(dbconn));
    exit(0);
  }
}
[8 Sep 2004 8:46] Konstantin Osipov
97 is the number here.
This is most likely the same bug as Bug#5399, which was closed just a few minutes ago.
Could you check that the problem doesn't exist on the latest BK tree?
Thank you.
[9 Sep 2004 4:41] Bill McCaffrey
I needed to apply that change to sql/sql_class.cc as well and I am no longer seeing the 1210 errors. I will have to do some more testing to see if the segfault issue is gone.
[9 Sep 2004 5:21] Bill McCaffrey
The program that I used to produce the segfault is now working fine with the updated server and client lib.
[13 Sep 2004 21:10] Konstantin Osipov
Good, then I'm closing the bug report (duplicate of bug #5399).