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

SIGSEGV in ha_maria::drop_table when free_tmp_table cleans up an internal temporary table that was created but never opened

    XMLWordPrintable

Details

    • Bug
    • Status: Open (View Workflow)
    • Major
    • Resolution: Unresolved
    • 10.11.19
    • None
    • Storage Engine - Aria
    • None
    • Debian 12, kernel 6.1.0-53-amd64, x86_64. wsrep/Galera enabled (not implicated).
    • Can result in unexpected behaviour

    Description

      ha_maria::drop_table dereferences file unconditionally on the line immediately following an assertion that explicitly permits it to be NULL:

      void ha_maria::drop_table(const char *name) // storage/maria/ha_maria.cc:2858

      { DBUG_ASSERT(!file || file->s->temporary); // 2860 - allows file == NULL file->s->deleting= 1; // 2861 - SIGSEGV (void) ha_close(); (void) maria_delete_table_files(name, 1, MY_WME); }

      That state is reachable through normal error handling. For an internal temp table requiring Aria:

      1. create_internal_tmp_table converts the distinct index to a unique constraint — sql/sql_select.cc:21912-21913, share->keys= 0; share->uniques= 1.
      2. maria_create succeeds, and sql/sql_select.cc:22035 calls table->set_created().
      3. open_tmp_table fails in ha_open. sql/sql_select.cc:21826 sets db_stat= 0 and returns 1. ha_maria::file remains NULL, and created from step 2 is never reset.
      4. create_tmp_table:21622 calls cleanup_on_failure → free_tmp_table.
      5. sql/sql_select.cc:22351 tests entry->file && entry->is_created() — both true. 22353 tests entry->db_stat, correctly skipping the statistics calls. 22361 then calls entry->file->drop_table(...) unconditionally.
      6. Crash at ha_maria.cc:2861.
      Note that free_tmp_table already anticipates created-but-not-opened at line 22353 ("The table was properly opened in open_tmp_table()"). ha_maria::drop_table does not. MyISAM does not override drop_table, so this is Aria-specific. Values from an intact core (mariadb-server-core-dbgsym 1:10.11.19+maria~deb12):

      #3 ha_maria::drop_table (this=0x563c4595f508,
      name=0x563c4595f018 "/database/tmp/#sql-temptable-112e0-20f9a62a-7700a")
      at ./storage/maria/ha_maria.cc:2861

      entry->db_stat = 0
      entry->file = (ha_maria *) 0x563c4595f508
      ((ha_maria*)entry->file)>file = (MARIA_HA *) 0x0 <- dereferenced
      entry->s->keys / uniques = 0 / 1
      entry->s->fields / blob_fields = 5 / 1
      entry->s->reclength = 198
      entry->field[3] = (Field_blob *) ..., field_length 16777215
      thd->m_stmt_da->m_sql_errno = 1032 (ER_KEY_NOT_FOUND)
      thd->m_stmt_da->m_message = "Can't find record in '(temporary)'"

      How to reproduce: The crash is independent of why open_tmp_table fails — any failure of ha_open on an Aria internal temp table reaches it. I do not have a reproducer for the failure we hit (filed separately as <link>), but the defect is visible by inspection, and forcing ha_open to fail by any means should demonstrate it. Exhausting file descriptors via a low --open-files-limit while a query materialises an Aria temp table looks like the cheapest deterministic route; I have not tested it. The query shape that produces the relevant temp table (on-disk Aria, one mediumtext, keys=0/uniques=1) is:
      CREATE TABLE t (
      id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
      value1 char(10) DEFAULT NULL, identifier char(32) DEFAULT NULL,
      user_id int unsigned DEFAULT NULL,
      created timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000',
      received timestamp(6) NOT NULL DEFAULT current_timestamp(6),
      need_reply tinyint(1) DEFAULT 0) ENGINE=InnoDB;

      WITH cte AS
      (SELECT created, received, value1, NULL AS user_id, NULL AS identifier
      FROM t WHERE need_reply = TRUE AND value1 IS NOT NULL
      UNION
      SELECT MIN(created), MAX(received), NULL, GROUP_CONCAT(DISTINCT user_id), identifier
      FROM t WHERE need_reply = TRUE AND identifier IS NOT NULL GROUP BY identifier)
      SELECT value1, user_id, identifier FROM cte ORDER BY created;

      This does not crash on its own — it only establishes the structure. Suggested fix In ha_maria::drop_table, honour what the assertion already states. Note the file deletion must still happen, or the temp files leak into tmpdir:

      void ha_maria::drop_table(const char *name)
      {
      DBUG_ASSERT(!file || file->s->temporary);
      if (file)

      { file->s->deleting= 1; (void) ha_close(); }

      (void) maria_delete_table_files(name, 1, MY_WME);
      }

      Guarding the call site at sql_select.cc:22361 with db_stat instead would avoid the crash but leak the created files. Impact A recoverable query error becomes a server crash. On a Galera cluster this evicts the node; in our case it was one of two coincident faults that cost the cluster its primary component.

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              xan@biblionix.com Xan Charbonnet
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:

                Git Integration

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