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

Server crashes in prepare_fk_prelocking_list() when allocating a TABLE_LIST for a referencing table fails

    XMLWordPrintable

Details

    • Bug
    • Status: In Review (View Workflow)
    • Critical
    • Resolution: Unresolved
    • 10.11, 11.4, 11.8, 12.3, 13.1, 13.2
    • 10.11
    • Locking

    Description

      prepare_fk_prelocking_list() in sql/sql_base.cc allocates a TABLE_LIST for every foreign key that references the table being modified and passes it to TABLE_LIST::init_one_table_for_prelocking() without checking the allocation. When the allocation fails, the server crashes with signal 11 instead of failing the statement. The memory root's error handler has already raised ER_OUT_OF_RESOURCES at that point. Release builds are affected.

      On 10.11 and 11.4:

          TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST));
          tl->init_one_table_for_prelocking(fk->foreign_db,
              fk->foreign_table,
              NULL, lock_type,
              TABLE_LIST::PRELOCK_FK,
              table_list->belong_to_view, op,
              &prelocking_ctx->query_tables_last,
              table_list->for_insert_data);
      

      From 11.8 on, the same code reads:

          TABLE_LIST *tl= thd->alloc<TABLE_LIST>(1);
          tl->init_one_table_for_prelocking(fk->foreign_db, fk->foreign_table,
              NULL, lock_type, TABLE_LIST::PRELOCK_FK, table_list->belong_to_view,
              op, &prelocking_ctx->query_tables_last, table_list->for_insert_data);
      

      add_internal_tables(), a few functions above in the same file, checks the same kind of allocation for NULL and returns an error.

      How to repeat

      The allocation only fails when the server runs out of memory, so this needs a debug build and a debug hook that makes exactly this allocation fail. The existing simulate_out_of_memory keyword fails the next alloc_root() call and then clears itself, so the hook below sets it right before the allocation (10.11):

          DBUG_EXECUTE_IF("fk_prelocking_alloc_fail",
                          DBUG_SET("+d,simulate_out_of_memory"););
          TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST));
      

      --source include/have_innodb.inc
      --source include/have_debug.inc
      CREATE TABLE p (a INT PRIMARY KEY) ENGINE=InnoDB;
      CREATE TABLE c (a INT, FOREIGN KEY (a) REFERENCES p(a)) ENGINE=InnoDB;
      INSERT INTO p VALUES (1), (2);
      SET debug_dbug= '+d,fk_prelocking_alloc_fail';
      --error ER_OUT_OF_RESOURCES
      DELETE FROM p WHERE a = 1;
      SET debug_dbug= '';
      DROP TABLE c, p;
      

      Expected: the DELETE fails with ER_OUT_OF_RESOURCES.
      Actual:

      mysqltest: At line 8: query 'DELETE FROM p WHERE a = 1' failed with wrong errno <Unknown> (2013): 'Lost connection to server during query', instead of ER_OUT_OF_RESOURCES (1041)...
       
      [ERROR] mariadbd got signal 11 ;
      ...
      sql/table.h:2355(TABLE_LIST::reset())
      sql/table.h:2370(TABLE_LIST::init_one_table(st_mysql_const_lex_string const*, st_mysql_const_lex_string const*, st_mysql_const_lex_string const*, thr_lock_type))
      sql/table.h:2398(TABLE_LIST::init_one_table_for_prelocking(st_mysql_const_lex_string const*, st_mysql_const_lex_string const*, st_mysql_const_lex_string const*, thr_lock_type, TABLE_LIST::prelocking_types, TABLE_LIST*, unsigned char, TABLE_LIST***, char))
      sql/sql_base.cc:5192(prepare_fk_prelocking_list(THD*, Query_tables_list*, TABLE_LIST*, bool*, unsigned char))
      sql/sql_base.cc:5282(DML_prelocking_strategy::handle_table(THD*, Query_tables_list*, TABLE_LIST*, bool*))
      sql/sql_base.cc:4016(extend_table_list(THD*, TABLE_LIST*, Prelocking_strategy*, bool))
      sql/sql_base.cc:4322(open_and_process_table(THD*, TABLE_LIST*, unsigned int*, unsigned int, Prelocking_strategy*, bool, Open_table_context*))
      sql/sql_base.cc:4747(open_tables(THD*, DDL_options_st const&, TABLE_LIST**, unsigned int*, unsigned int, Prelocking_strategy*))
      sql/sql_base.cc:5781(open_and_lock_tables(THD*, DDL_options_st const&, TABLE_LIST*, bool, unsigned int, Prelocking_strategy*))
      sql/sql_base.h:519(open_and_lock_tables(THD*, TABLE_LIST*, bool, unsigned int))
      sql/sql_delete.cc:416(mysql_delete(THD*, TABLE_LIST*, Item*, SQL_I_List<st_order>*, unsigned long long, unsigned long long, select_result*))
      

      The line numbers in sql/sql_base.cc are those of 10.11 with the hook added. The code is the same on 10.11, 11.4, 11.8, 12.3, 13.1 and main, apart from the form of the allocation shown above.

      Attachments

        Issue Links

          Activity

            People

              monty Michael Widenius
              arcivanov Arcadiy Ivanov
              Michael Widenius Michael Widenius
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:

                Time Tracking

                  Estimated:
                  Original Estimate - 0d
                  0d
                  Remaining:
                  Remaining Estimate - 1h
                  1h
                  Logged:
                  Time Spent - Not Specified
                  Not Specified

                  Git Integration

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