Bug #93767 INSTALL COMPONENT fails - handled segfault during installation nothing in log
Submitted: 30 Dec 2018 22:22 Modified: 22 Jan 13:19
Reporter: Justin Swanhart Email Updates:
Status: Need Feedback Impact on me:
None 
Category:MySQL Server Severity:S2 (Serious)
Version:8.0.13 (git source) OS:Debian (9.6)
Assigned to: CPU Architecture:x86

[30 Dec 2018 22:22] Justin Swanhart
Description:
I have created a C++ service using the component framework.  This service won't install with INSTALL COMPONENT:
mysql> install component 'file://sql_shim';
ERROR 3529 (HY000): Cannot load component from specified URN: 'file://sql_shim'

When I attach gdb I get a handled segfaul in PFS:
Thread 43 "mysqld" received signal SIGSEGV, Segmentation fault.
my_thread_get_THR_PFS () at /root/warpsql-server/storage/perfschema/pfs.cc:2094
2094    static inline PFS_thread *my_thread_get_THR_PFS() { return THR_PFS; }
(gdb)
handle_fatal_signal (sig=11) at /root/warpsql-server/sql/signal_handler.cc:80
80      extern "C" void handle_fatal_signal(int sig) {
(gdb)
81        if (segfaulted) {
(gdb)
96        const time_t curr_time = time(NULL);
(gdb) bt
#0  handle_fatal_signal (sig=11) at /root/warpsql-server/sql/signal_handler.cc:96
#1  <signal handler called>
#2  my_thread_get_THR_PFS () at /root/warpsql-server/storage/perfschema/pfs.cc:2094
#3  pfs_set_thread_start_time_v1 (start_time=1546208093) at /root/warpsql-server/storage/perfschema/pfs.cc:3127
#4  0x000055555626445c in THD::set_time (this=0x7fff60000b50) at /root/warpsql-server/sql/sql_class.h:2582
#5  dispatch_command (thd=0x7fff60000b50, com_data=0x7fffdc1ebd40, command=COM_QUERY) at /root/warpsql-server/sql/sql_parse.cc:1445
#6  0x0000555556267b50 in do_command (thd=thd@entry=0x7fff60000b50) at /root/warpsql-server/sql/sql_parse.cc:1260
#7  0x0000555556364528 in handle_connection (arg=arg@entry=0x555558ce45c0) at /root/warpsql-server/sql/conn_handler/connection_handler_per_thread.cc:308
#8  0x00005555575a0ddf in pfs_spawn_thread (arg=0x555558f859b0) at /root/warpsql-server/storage/perfschema/pfs.cc:2836
#9  0x00007ffff7bc3494 in start_thread (arg=0x7fffdc1ec700) at pthread_create.c:333
#10 0x00007ffff6114acf in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:97
(gdb)

How to repeat:
here is the header:
#ifndef MYSQL_SHIM_SERVICE_H
#define MYSQL_SHIM_SERVICE_H

#include "mysql/components/service_implementation.h"
#include "../../components/mysql_server/server_component.h"
#include "mysql/psi/psi_memory.h"
#include <locale>
#include "sql/sql_parse.h"
#include "sql/sql_class.h"
#include "sql/sql_client.h"

void my_plugin_shim_init();

class my_plugin_shim_imp {
 public: /* service implementations */
  /**
    Query rewriter that allows queries to run on the THD inside of the plugin to get internal resultsets using SQLClient
    @param [in] thd - the THD object on which to run queries
    @param [in] com_data      - the original COM_DATA* from the do_command function
    @param [in] command       - the original command from the do_command function
    @param [out] new_com_data - pointer to a new COM_DATA to run when the function returns
    @param [out] new_command  - pointer to a new enum_server_command to run when the function returns
    @return return value indicates wether or not the command was rewritten
    @retval false no rewrite
    @retval true query was rewritten
  */
  static DEFINE_BOOL_METHOD(query_injection_point,(THD* thd, COM_DATA *com_data, enum enum_server_command command,
  COM_DATA* new_com_data, enum enum_server_command* new_command));
};
#endif /* MYSQL_SHIM_SERVICE_H */

And here is the implementation:
#include "../../components/mysql_server/my_plugin_shim.h"

PSI_memory_key key_memory_shim_iterator;

/**
  Its a dummy initialization function. And it will be called from
  mysql_component_infrastructure_init(). Else linker, is cutting out (as
  library optimization) the string service code because libsql code is not
  calling any functions of it.
*/
void my_plugin_shim_init() { return; }

/*
    Query rewriter that allows queries to run on the THD inside of the plugin to get internal resultsets using SQLClient
    @param [in] thd - the THD object on which to run queries
    @param [in] com_data      - the original COM_DATA* from the do_command function
    @param [in] command       - the original command from the do_command function
    @param [out] new_com_data - pointer to a new COM_DATA to run when the function returns
    @param [out] new_command  - pointer to a new enum_server_command to run when the function returns
    @return return value indicates wether or not the command was rewritten
    @retval false no rewrite
    @retval true query was rewritten
  */
  DEFINE_BOOL_METHOD(my_plugin_shim_imp::query_injection_point,(THD* thd, COM_DATA *com_data, enum enum_server_command command,
    COM_DATA* new_com_data, enum enum_server_command* new_command)) {
  std::string new_query;
  SQLClient conn(thd);
  SQLCursor* stmt;
  SQLRow* row;
  try {
    /* example rewrite rule for SHOW PASSWORD*/
    if(command == COM_QUERY)
    {
      std::locale loc;
      std::string old_query(com_data->com_query.query,com_data->com_query.length);
      for(unsigned int i=0;i<com_data->com_query.length;++i) {
        old_query[i] = std::toupper(old_query[i], loc);
      }

      if(old_query == "SHOW PASSWORD")
      {
        if(conn.query("pw,user","select authentication_string as pw,user from mysql.user where concat(user,'@',host) = USER() or user = USER() LIMIT 1", &stmt))
        {
          if(stmt != NULL)
          {
            if((row = stmt->next()))
            {
              new_query = "SELECT '" + row->at(0) + "'";
            }
          } else
          {
            delete stmt;
            return false;
          }
        } else {
          return false;
        }

        /* replace the command sent to the server */
        if(new_query != "")
        {
          thd->get_protocol_classic()->create_command(new_com_data, COM_QUERY, (uchar *) strdup(new_query.c_str()), new_query.length());
          *new_command = COM_QUERY;
        } else {
          if(stmt) delete stmt;
          return false;
        }
        if(stmt) delete stmt;
        return true;
      }
    }

    /* don't replace command */
    return false;
  } catch (...) {
    if(stmt != NULL) delete stmt;
    mysql_components_handle_std_exception(__func__);
    return false;
  }
  return true;
};

Here is the CMakeLists.txt - I've put the code in sql/shim:
IF(WITHOUT_SERVER)
  MESSAGE(STATUS "The shim plugin is for server only")
  RETURN()
ENDIF()

SET(MYSQL_SHIM_SOURCES
  my_plugin_shim.cc
)
ADD_CONVENIENCE_LIBRARY(plugin_shim ${MYSQL_SHIM_SOURCES})
IF(INSTALL_STATIC_LIBRARIES)
  INSTALL(TARGETS plugin_shim
    DESTINATION ${INSTALL_LIBDIR} COMPONENT Development)
ENDIF()
ADD_DEPENDENCIES(plugin_shim GenError)

Suggested fix:
Not sure.  I don't know why the PFS segfaults.
[30 Dec 2018 22:27] Justin Swanhart
changed the title to indicate the handled segfault
[30 Dec 2018 22:54] Justin Swanhart
I commented out the body of query_injection_point() to see if that would change anything, but it still segfaults.
[11 Jan 15:16] Sinisa Milivojevic
Hello Justin,

Let me know how are you !!!!!

Both, our mutual friend from Bulgaria and I are delighted that you are trying to write components.

Regarding the one that you have displayed for us here, I do have some bad news for you.  

I am afraid that you are assuming that you can, in a component, alter internal state of another component.

Unfortunately, this is simply not possible with components.

Plugins can do that for other plugins, but this does not work with components. 

I will provide you here with one useful URL for the documentation:

https://dev.mysql.com/doc/dev/mysql-server/latest/

Hoping to hear from you soon !!!!!
[21 Jan 18:23] Justin Swanhart
Hi,

There is only one component here.  I'm not trying to modify the state of another component, everything is normal library calls.

I've modified sql_cilent.h and sql_client.cc in my installation, so this code won't compile out of the box.  I will provide a simpler example with an empty function (remember I tried with the body commented out) to demonstrate the problem more clearly.
[21 Jan 19:10] Justin Swanhart
Okay, so it turns out this happens when you try to install a component that is not found, instead of a nice error message like 'could not locate component'...  I suggest you improve the error messages around component installation in that case.

Now I can't figure out how to install my component.  It is installed in /usr/local/mysql/lib/plugin/libplugin_shim.so

All of the following fail:
mysql> install component 'file://shim';
ERROR 3529 (HY000): Cannot load component from specified URN: 'file://shim'.
mysql> install component 'file://plugin_shim';
ERROR 3529 (HY000): Cannot load component from specified URN: 'file://plugin_shim'.
mysql> install component 'file://libplugin_shim';
ERROR 3529 (HY000): Cannot load component from specified URN: 'file://libplugin_shim'.

How do I load my shared library.  Is it named wrong?
[21 Jan 20:01] Justin Swanhart
From the docs you gave I must be missing something.  I'll look at the example/ directory to see what I'm missing.
[22 Jan 13:19] Sinisa Milivojevic
First of all, waiting on the example.

Second, try giving a full path of your component in its subdirectory under components/ directory.