Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-41372

Grant_tables::open_and_lock() frees its TABLE_LIST array while the opened privilege tables still point into it

    XMLWordPrintable

Details

    • Not for Release Notes

    Description

      Grant_tables::open_and_lock() allocates the TABLE_LIST array for the privilege tables with my_malloc() and frees it with my_free() before returning. The heap allocation came in with MDEV-34388 (Alpine stack overflow), which moved the array off the stack.

      The privilege tables opened from this array stay open after the function returns. They remain on thd->open_tables until close_mysql_tables() or the end of the statement. Each of these TABLE objects keeps TABLE::pos_in_table_list pointing into the freed array. So for the rest of the GRANT/REVOKE/CREATE USER/FLUSH PRIVILEGES/... statement, every opened privilege table holds a dangling pointer.

      The following happens on DROP USER and custom storage engine which reads table->pos_in_table_list in table->file->ha_index_read_idx_map().

      ...
      sql/handler.cc:7515(handler::index_read_idx_map(unsigned char*, unsigned int, unsigned char const*, unsigned long, ha_rkey_function))[0x5629d887f28f]
      sql/handler.cc:3986(handler::ha_index_read_idx_map(unsigned char*, unsigned int, unsigned char const*, unsigned long, ha_rkey_function))[0x5629d8869437]
      sql/sql_acl.cc:10532(handle_grant_table(THD*, Grant_table_base const&, enum_acl_tables, bool, LEX_USER*, LEX_USER*))[0x5629d8e9e5b7]
      sql/sql_acl.cc:11184(handle_grant_data(THD*, Grant_tables&, bool, LEX_USER*, LEX_USER*))[0x5629d8e7ea82]
      sql/sql_acl.cc:11437(mysql_drop_user(THD*, List<LEX_USER>&, bool))[0x5629d8e7f2a4]
      sql/sql_parse.cc:0(mysql_execute_command(THD*, bool))[0x5629d9054b32]
      sql/sql_class.h:3284(mysql_parse(THD*, char*, unsigned int, Parser_state*))[0x5629d904a7f2]
      sql/sql_parse.cc:0(dispatch_command(enum_server_command, THD*, char*, unsigned int, bool))[0x5629d9044b09]
      sql/sql_parse.cc:1457(do_command(THD*, bool))[0x5629d904b47c]
      sql/sql_connect.cc:1511(do_handle_one_connection(CONNECT*, bool))[0x5629d947956d]
      sql/sql_connect.cc:1427(handle_one_connection)[0x5629d9478dab]
      perfschema/pfs.cc:2200(pfs_spawn_thread)[0x5629d99e968c]
      crtstuff.c:0(asan_thread_start(void*))[0x5629d86c890b]
      nptl/pthread_create.c:454(start_thread)[0x7f74996a3dfa]
      x86_64/clone3.S:80(__GI___clone3)[0x7f74997375cc]
      

      InnoDB and ARIA do not read table->pos_in_table_list thus no issues observed

      To check it:

      --source include/have_innodb.inc
       
      CREATE USER 'gp_admin'@'localhost' IDENTIFIED BY 'Admin123!';
      GRANT ALL PRIVILEGES ON *.* TO 'gp_admin'@'localhost' WITH GRANT OPTION;
       
      RENAME TABLE mysql.global_priv TO mysql.global_priv_local;
       
      CREATE TABLE IF NOT EXISTS mysql.global_priv (
        Host CHAR(255) BINARY NOT NULL,
        User CHAR(128) BINARY NOT NULL,
        Priv JSON NOT NULL,
        PRIMARY KEY (Host, User)
      ) ENGINE=InnoDB CHARACTER SET utf8mb3 COLLATE utf8mb3_bin;
       
      SELECT engine
        FROM information_schema.tables
       WHERE table_schema = 'mysql' AND table_name = 'global_priv';
       
      INSERT INTO mysql.global_priv SELECT * FROM mysql.global_priv_local;
      FLUSH PRIVILEGES;
       
      connect (joiner, localhost, gp_admin, Admin123!);
      USE mysql;
      disconnect joiner;
       
      connection default;
      DROP TABLE mysql.global_priv;
      RENAME TABLE mysql.global_priv_local TO mysql.global_priv;
      DROP USER 'gp_admin'@'localhost';
      

      Proposed fix:

      diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
      index 70984625879..8235d82ea1f 100644
      --- a/sql/sql_acl.cc
      +++ b/sql/sql_acl.cc
      @@ -2739,10 +2739,8 @@ class Grant_tables
         {
           DBUG_ENTER("Grant_tables::open_and_lock");
       
      -    TABLE_LIST *first= nullptr, *tables=
      -      static_cast<TABLE_LIST*>(my_malloc(PSI_NOT_INSTRUMENTED,
      -                                         (USER_TABLE + 1) * sizeof *tables,
      -                                         MYF(MY_WME)));
      +    TABLE_LIST *first= nullptr,
      +               *tables= thd->calloc<TABLE_LIST>(USER_TABLE + 1);
           int res= -1;
           uint counter;
       
      @@ -2802,7 +2800,6 @@ class Grant_tables
           }
       
       func_exit:
      -    my_free(tables);
           DBUG_RETURN(res);
         }
       
      

      Attachments

        Issue Links

          Activity

            People

              valentyn.doroshchuk Val Doroshchuk
              valentyn.doroshchuk Val Doroshchuk
              Val Doroshchuk Val Doroshchuk
              Daniel Black Daniel Black
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.