Search code examples
mysqlclinuxlibmysql

c linux - memset raise Segmentation Fault in Release build, printf somehow can 'fix' it?


I'm getting segmentation fault on CentOS 7 64-bit, Release build (there's no issue in debug build)

I tried adding printf to locate where exactly the segfault happens, for below code, segfault raises at: memset(len_p, 0, sizeof(*len_p));

Also, If I add printf to print something right before memeset, there's no segfault, as if the printf is doing some magic here.

Please help me to understand the issue here.

core dump uploaded

Thanks a lot!

code snippet (mysql prepared statement to query something):

(this function was called couple of times, it doesn't always raise segfault)

int mysql_odb_read(void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid)
{
  MYSQL_BIND bind_buffers[2];
  MYSQL_BIND result_buffers[3];

  memset(bind_buffers, 0, sizeof(bind_buffers));

  // bind the repo passed to the statement
  bind_buffers[0].buffer = &(backend->mysql->repo);
  bind_buffers[0].buffer_length = sizeof(backend->mysql->repo);
  bind_buffers[0].length = &bind_buffers[0].buffer_length;
  bind_buffers[0].buffer_type = MYSQL_TYPE_LONGLONG;

  // bind the oid passed to the statement
  bind_buffers[1].buffer = (void*)oid->id;
  bind_buffers[1].buffer_length = 20;
  bind_buffers[1].length = &bind_buffers[1].buffer_length;
  bind_buffers[1].buffer_type = MYSQL_TYPE_BLOB;

  if (mysql_stmt_bind_param(backend->mysql->odb_read, bind_buffers) != 0)
      return GIT_ERROR;

  // execute the statement
  if (mysql_stmt_execute(backend->mysql->odb_read) != 0)
      return GIT_ERROR;

  if (mysql_stmt_store_result(backend->mysql->odb_read) != 0)
      return GIT_ERROR;

  if (mysql_stmt_num_rows(backend->mysql->odb_read) == 1) {

    memset(result_buffers, 0, sizeof(result_buffers));

    result_buffers[0].buffer_type = MYSQL_TYPE_TINY;
    result_buffers[0].buffer = type_p;
    result_buffers[0].buffer_length = sizeof(*type_p);
    result_buffers[0].is_null = 0;
    result_buffers[0].length = &result_buffers[0].buffer_length;
    memset(type_p, 0, sizeof(*type_p));

    result_buffers[1].buffer_type = MYSQL_TYPE_LONG;
    result_buffers[1].buffer = len_p;
    result_buffers[1].buffer_length = sizeof(*len_p);
    result_buffers[1].is_null = 0;
    result_buffers[1].length = &result_buffers[1].buffer_length;
    printf("len_p:%lu\n",*len_p);//if i remove this printf call, there will be segfault
    memset(len_p, 0, sizeof(*len_p));

    result_buffers[2].buffer_type = MYSQL_TYPE_LONG_BLOB;
    result_buffers[2].buffer = *data_p;
    result_buffers[2].is_null = 0;
    result_buffers[2].buffer_length = data_len;
    result_buffers[2].length = &data_len;
    //memset(&data_len, 0, sizeof(data_len));

    if (mysql_stmt_bind_result(backend->mysql->odb_read, result_buffers) != 0)
      return GIT_ERROR;

    error = mysql_stmt_fetch(backend->mysql->odb_read);

    if (data_len > 0) {
      *data_p = malloc(data_len);
      if(*data_p){
    result_buffers[2].buffer_type = MYSQL_TYPE_LONG_BLOB;
        result_buffers[2].buffer = *data_p;
    result_buffers[2].is_null = 0;
        result_buffers[2].buffer_length = data_len;
    result_buffers[2].length = &data_len;
      if (mysql_stmt_fetch_column(backend->mysql->odb_read, &result_buffers[2], 2, 0) != 0)
             return GIT_ERROR;
      }
      else{
        printf("odb_read, malloc returned NULL\n");
        return GITERR_NOMEMORY;
      }
    }

    error = GIT_OK;
  } else
    error = GIT_ENOTFOUND;

  // free result
  if (mysql_stmt_free_result(backend->mysql->odb_read) != 0)
      return GIT_ERROR;

  mysql_free_result(meta_result);

  // reset the statement for further use
  if (mysql_stmt_reset(backend->mysql->odb_read) != 0)
      return GIT_ERROR;

  return error;
}

Solution

  • It's difficult to tell the exact problem without dumps or other informations, but a possible explanation may be that the len_p is a dangling pointer.

    This triggers the segfault, however, by calling printf, you are probably filling up the stack area where len_p is pointing, with printf data, hence remapping the pointer with a new value, avoiding the segmentation fault.