[MDEV-22751] Uninitialized tbl_len in dict_acquire_mdl_shared() in purge after DDL Created: 2020-05-29  Updated: 2020-05-29  Resolved: 2020-05-29

Status: Closed
Project: MariaDB Server
Component/s: Storage Engine - InnoDB
Affects Version/s: 10.5.1
Fix Version/s: 10.5.4

Type: Bug Priority: Major
Reporter: Marko Mäkelä Assignee: Marko Mäkelä
Resolution: Fixed Votes: 0
Labels: corruption, crash, purge, regression

Issue Links:
Problem/Incident
is caused by MDEV-16678 Use MDL for innodb background threads... Closed

 Description   

A crash with the following (abbreviated) call stack was observed:

#10 0x0000558a21f88cef in memcpy (__len=<optimized out>, __src=0x14af1c3f0740, __dest=0x14af1c3f0670)
    at /usr/include/x86_64-linux-gnu/bits/string_fortified.h:34
#11 dict_acquire_mdl_shared<false> (table=table@entry=0x14aee838fef0, thd=thd@entry=0x558a24399468, 
    mdl=mdl@entry=0x558a24285b00, table_op=table_op@entry=DICT_TABLE_OP_NORMAL)
    at /test/10.5_opt/storage/innobase/dict/dict0dict.cc:904
#12 0x0000558a21f84110 in dict_table_open_on_id (table_id=98, dict_locked=dict_locked@entry=false, 
    table_op=table_op@entry=DICT_TABLE_OP_NORMAL, thd=0x558a24399468, mdl=mdl@entry=0x558a24285b00)
    at /test/10.5_opt/storage/innobase/dict/dict0dict.cc:946
#13 0x0000558a21e867e6 in row_purge_parse_undo_rec (thr=0x558a24285780, 
    updated_extern=0x14af1c3f0abe, undo_rec=0x558a24357470 "", node=0x558a24285960)
    at /test/10.5_opt/storage/innobase/row/row0purge.cc:933

When I debugged a different occurrence of this, I noticed that tbl_len was uninitialized in the memcpy() call:

  size_t db1_len, tbl1_len;
 
  table->parse_name<!trylock>(db_buf1, tbl_buf1, &db1_len, &tbl1_len);
 
  if (*mdl)
  {
    if (db_len == db1_len && tbl_len == tbl1_len &&
        !memcmp(db_buf, db_buf1, db_len) &&
        !memcmp(tbl_buf, tbl_buf1, tbl_len))
      return table;
 
    /* The table was renamed. Release MDL for the old name and
    try to acquire MDL for the new name. */
    mdl_context->release_lock(*mdl);
    *mdl= nullptr;
  }
 
  db_len= db1_len;
  tbl_len= tbl1_len;
 
  memcpy(tbl_buf, tbl_buf1, tbl_len + 1);
  memcpy(db_buf, db_buf1, db_len + 1);
  goto retry;
}

When the function parse_name() returns false, it would leave tbl1_len uninitialized. The following patch should help:

diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index e7cb05307d0..76537b2f41d 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -758,7 +758,10 @@ bool dict_table_t::parse_name(char (&db_name)[NAME_LEN + 1],
 
   if (tbl_len > TEMP_FILE_PREFIX_LENGTH
       && !strncmp(tbl_buf, TEMP_FILE_PREFIX, TEMP_FILE_PREFIX_LENGTH))
+  {
+    *tbl_name_len= tbl_len;
     return false;
+  }
 
   if (char* is_part= strchr(tbl_buf, '#'))
     *is_part= '\0';

It looks like db_len would always be initialized.



 Comments   
Comment by Marko Mäkelä [ 2020-05-29 ]

My proposed patch could have reintroduced MDEV-21344. I ended up handling the false return value of dict_table_t::parse_name() consistently, that is, releasing any MDL and returning the table handle.

Generated at Thu Feb 08 09:17:11 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.