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

ASAN use-after-poison in PageBulk::insertPage on table rebuild

Details

    Description

      --source include/have_innodb.inc
       
      CREATE TABLE t1 (
        pk timestamp,
        a timestamp NULL DEFAULT '0000-00-00 00:00:00',
        PRIMARY KEY (pk),
        KEY (a)
      ) ENGINE=InnoDB;
       
      INSERT INTO t1 VALUES ('2020-02-17 11:15:11.0272',NULL);
      INSERT INTO t1 VALUES ('2020-02-17 11:15:13.0272',NULL);
      INSERT INTO t1 VALUES ('2020-02-17 11:15:17.0272',NULL);
      INSERT INTO t1 VALUES ('2020-02-17 11:15:20.0272',NULL);
      INSERT INTO t1 (pk) VALUES ('0000-00-00 00:00:00.0000');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:03.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:06.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:08.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:16.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:01.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:15.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:00.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:12.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:14.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:18.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:09.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:10.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:19.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:07.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:04.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:05.0272');
      INSERT INTO t1 (pk) VALUES ('2020-02-17 11:15:02.0272');
       
      ALTER TABLE t1 ADD b VARCHAR(38044);
       
      # Cleanup
      DROP TABLE t1;
      

      10.5 41fe972d

      ==4847==ERROR: AddressSanitizer: use-after-poison on address 0x6190000f6b8e at pc 0x562c1c2535a9 bp 0x7fc9ce86f5c0 sp 0x7fc9ce86f5b8
      READ of size 1 at 0x6190000f6b8e thread T13
          #0 0x562c1c2535a8 in void PageBulk::insertPage<(PageBulk::format)1>(unsigned char*, unsigned short*) /data/src/10.5/storage/innobase/btr/btr0bulk.cc:273
          #1 0x562c1c25046b in PageBulk::insert(unsigned char const*, unsigned short*) /data/src/10.5/storage/innobase/btr/btr0bulk.cc:329
          #2 0x562c1c24e477 in BtrBulk::insert(dtuple_t*, unsigned long) /data/src/10.5/storage/innobase/btr/btr0bulk.cc:1099
          #3 0x562c1c01bf23 in BtrBulk::insert(dtuple_t*) /data/src/10.5/storage/innobase/include/btr0bulk.h:301
          #4 0x562c1c07616b in row_merge_insert_index_tuples /data/src/10.5/storage/innobase/row/row0merge.cc:3631
          #5 0x562c1c070882 in row_merge_read_clustered_index /data/src/10.5/storage/innobase/row/row0merge.cc:2615
          #6 0x562c1c07b695 in row_merge_build_indexes(trx_t*, dict_table_t*, dict_table_t*, bool, dict_index_t**, unsigned long const*, unsigned long, TABLE*, dtuple_t const*, unsigned long const*, unsigned long, ib_sequence_t&, bool, ut_stage_alter_t*, dict_add_v_col_t const*, TABLE*, bool) /data/src/10.5/storage/innobase/row/row0merge.cc:4661
          #7 0x562c1be4206d in ha_innobase::inplace_alter_table(TABLE*, Alter_inplace_info*) /data/src/10.5/storage/innobase/handler/handler0alter.cc:8285
          #8 0x562c1af45711 in handler::ha_inplace_alter_table(TABLE*, Alter_inplace_info*) /data/src/10.5/sql/handler.h:4365
          #9 0x562c1af2c71a in mysql_inplace_alter_table /data/src/10.5/sql/sql_table.cc:7743
          #10 0x562c1af3b43f in mysql_alter_table(THD*, st_mysql_const_lex_string const*, st_mysql_const_lex_string const*, HA_CREATE_INFO*, TABLE_LIST*, Alter_info*, unsigned int, st_order*, bool) /data/src/10.5/sql/sql_table.cc:10149
          #11 0x562c1b0a78a0 in Sql_cmd_alter_table::execute(THD*) /data/src/10.5/sql/sql_alter.cc:523
          #12 0x562c1acfc6ed in mysql_execute_command(THD*) /data/src/10.5/sql/sql_parse.cc:5878
          #13 0x562c1ad0866d in mysql_parse(THD*, char*, unsigned int, Parser_state*, bool, bool) /data/src/10.5/sql/sql_parse.cc:7918
          #14 0x562c1ace2825 in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool, bool) /data/src/10.5/sql/sql_parse.cc:1845
          #15 0x562c1acdf7de in do_command(THD*) /data/src/10.5/sql/sql_parse.cc:1364
          #16 0x562c1b090fca in do_handle_one_connection(CONNECT*, bool) /data/src/10.5/sql/sql_connect.cc:1422
          #17 0x562c1b0909c0 in handle_one_connection /data/src/10.5/sql/sql_connect.cc:1319
          #18 0x562c1bcc6289 in pfs_spawn_thread /data/src/10.5/storage/perfschema/pfs.cc:1869
          #19 0x7fc9dec484a3 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x74a3)
          #20 0x7fc9dcd7cd0e in __clone (/lib/x86_64-linux-gnu/libc.so.6+0xe8d0e)
       
      0x6190000f6b8e is located 526 bytes inside of 1128-byte region [0x6190000f6980,0x6190000f6de8)
      allocated by thread T13 here:
          #0 0x7fc9def1fd28 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc1d28)
          #1 0x562c1bf666dd in mem_heap_create_block_func(mem_block_info_t*, unsigned long, char const*, unsigned int, unsigned long) /data/src/10.5/storage/innobase/mem/mem0mem.cc:277
          #2 0x562c1c24678c in mem_heap_create_func /data/src/10.5/storage/innobase/include/mem0mem.ic:375
          #3 0x562c1c24a47b in PageBulk::init() /data/src/10.5/storage/innobase/btr/btr0bulk.cc:48
          #4 0x562c1c24db53 in BtrBulk::insert(dtuple_t*, unsigned long) /data/src/10.5/storage/innobase/btr/btr0bulk.cc:1002
          #5 0x562c1c01bf23 in BtrBulk::insert(dtuple_t*) /data/src/10.5/storage/innobase/include/btr0bulk.h:301
          #6 0x562c1c07616b in row_merge_insert_index_tuples /data/src/10.5/storage/innobase/row/row0merge.cc:3631
          #7 0x562c1c070882 in row_merge_read_clustered_index /data/src/10.5/storage/innobase/row/row0merge.cc:2615
          #8 0x562c1c07b695 in row_merge_build_indexes(trx_t*, dict_table_t*, dict_table_t*, bool, dict_index_t**, unsigned long const*, unsigned long, TABLE*, dtuple_t const*, unsigned long const*, unsigned long, ib_sequence_t&, bool, ut_stage_alter_t*, dict_add_v_col_t const*, TABLE*, bool) /data/src/10.5/storage/innobase/row/row0merge.cc:4661
          #9 0x562c1be4206d in ha_innobase::inplace_alter_table(TABLE*, Alter_inplace_info*) /data/src/10.5/storage/innobase/handler/handler0alter.cc:8285
          #10 0x562c1af45711 in handler::ha_inplace_alter_table(TABLE*, Alter_inplace_info*) /data/src/10.5/sql/handler.h:4365
          #11 0x562c1af2c71a in mysql_inplace_alter_table /data/src/10.5/sql/sql_table.cc:7743
          #12 0x562c1af3b43f in mysql_alter_table(THD*, st_mysql_const_lex_string const*, st_mysql_const_lex_string const*, HA_CREATE_INFO*, TABLE_LIST*, Alter_info*, unsigned int, st_order*, bool) /data/src/10.5/sql/sql_table.cc:10149
          #13 0x562c1b0a78a0 in Sql_cmd_alter_table::execute(THD*) /data/src/10.5/sql/sql_alter.cc:523
          #14 0x562c1acfc6ed in mysql_execute_command(THD*) /data/src/10.5/sql/sql_parse.cc:5878
          #15 0x562c1ad0866d in mysql_parse(THD*, char*, unsigned int, Parser_state*, bool, bool) /data/src/10.5/sql/sql_parse.cc:7918
          #16 0x562c1ace2825 in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool, bool) /data/src/10.5/sql/sql_parse.cc:1845
          #17 0x562c1acdf7de in do_command(THD*) /data/src/10.5/sql/sql_parse.cc:1364
          #18 0x562c1b090fca in do_handle_one_connection(CONNECT*, bool) /data/src/10.5/sql/sql_connect.cc:1422
          #19 0x562c1b0909c0 in handle_one_connection /data/src/10.5/sql/sql_connect.cc:1319
          #20 0x562c1bcc6289 in pfs_spawn_thread /data/src/10.5/storage/perfschema/pfs.cc:1869
          #21 0x7fc9dec484a3 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x74a3)
       
      Thread T13 created by T0 here:
          #0 0x7fc9dee8ef59 in __interceptor_pthread_create (/usr/lib/x86_64-linux-gnu/libasan.so.3+0x30f59)
          #1 0x562c1bcc6676 in spawn_thread_v1 /data/src/10.5/storage/perfschema/pfs.cc:1919
          #2 0x562c1aa2c728 in inline_mysql_thread_create /data/src/10.5/include/mysql/psi/mysql_thread.h:1275
          #3 0x562c1aa4039f in create_thread_to_handle_connection(CONNECT*) /data/src/10.5/sql/mysqld.cc:6065
          #4 0x562c1aa40914 in create_new_thread(CONNECT*) /data/src/10.5/sql/mysqld.cc:6124
          #5 0x562c1aa40ad1 in handle_accepted_socket(st_mysql_socket, st_mysql_socket) /data/src/10.5/sql/mysqld.cc:6189
          #6 0x562c1aa414b4 in handle_connections_sockets() /data/src/10.5/sql/mysqld.cc:6316
          #7 0x562c1aa3fbfb in mysqld_main(int, char**) /data/src/10.5/sql/mysqld.cc:5728
          #8 0x562c1aa2a60f in main /data/src/10.5/sql/main.cc:25
          #9 0x7fc9dccb42e0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202e0)
       
      SUMMARY: AddressSanitizer: use-after-poison /data/src/10.5/storage/innobase/btr/btr0bulk.cc:273 in void PageBulk::insertPage<(PageBulk::format)1>(unsigned char*, unsigned short*)
      Shadow bytes around the buggy address:
        0x0c3280016d20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
        0x0c3280016d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x0c3280016d40: f7 00 02 f7 00 00 00 02 f7 00 02 f7 00 00 00 02
        0x0c3280016d50: f7 00 00 00 02 f7 00 02 f7 00 00 00 02 f7 00 00
        0x0c3280016d60: 00 02 f7 00 02 f7 00 00 00 02 f7 00 00 00 02 f7
      =>0x0c3280016d70: 00[06]f7 00 00 00 02 f7 00 00 00 02 f7 f7 f7 f7
        0x0c3280016d80: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
        0x0c3280016d90: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
        0x0c3280016da0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
        0x0c3280016db0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 fa fa fa
        0x0c3280016dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      Shadow byte legend (one shadow byte represents 8 application bytes):
        Addressable:           00
        Partially addressable: 01 02 03 04 05 06 07 
        Heap left redzone:       fa
        Heap right redzone:      fb
        Freed heap region:       fd
        Stack left redzone:      f1
        Stack mid redzone:       f2
        Stack right redzone:     f3
        Stack partial redzone:   f4
        Stack after return:      f5
        Stack use after scope:   f8
        Global redzone:          f9
        Global init order:       f6
        Poisoned by user:        f7
        Container overflow:      fc
        Array cookie:            ac
        Intra object redzone:    bb
        ASan internal:           fe
        Left alloca redzone:     ca
        Right alloca redzone:    cb
      ==4847==ABORTING
      

      No obvious effect on a non-ASAN build.
      The failure appeared in 10.5 recently, I didn't bisect for the exact revision.

      Attachments

        Issue Links

          Activity

            We were dereferencing the pointers before checking their validity:

            diff --git a/storage/innobase/btr/btr0bulk.cc b/storage/innobase/btr/btr0bulk.cc
            index aeeb5850713..1171de544a7 100644
            --- a/storage/innobase/btr/btr0bulk.cc
            +++ b/storage/innobase/btr/btr0bulk.cc
            @@ -270,9 +270,11 @@ inline void PageBulk::insertPage(rec_t *rec, offset_t *offsets)
                   byte *bd= insert_rec;
                   const byte *rd= rec;
                   /* Skip any unchanged prefix of the record. */
            -      for (; *bd == *rd; cd++, bd++, rd++)
            +      for (;; cd++, bd++, rd++)
                     if (bd == insert_rec_end)
                       goto no_data;
            +        else if (*bd != *rd)
            +          break;
             
                   /* Try to copy any data bytes of the preceding record. */
                   if (c_end - cd > 2)
            

            It looks like we would access at most one byte beyond the end of the payload of rec. I do not think that there would be any observable impact outside AddressSanitizer or Valgrind.

            marko Marko Mäkelä added a comment - We were dereferencing the pointers before checking their validity: diff --git a/storage/innobase/btr/btr0bulk.cc b/storage/innobase/btr/btr0bulk.cc index aeeb5850713..1171de544a7 100644 --- a/storage/innobase/btr/btr0bulk.cc +++ b/storage/innobase/btr/btr0bulk.cc @@ -270,9 +270,11 @@ inline void PageBulk::insertPage(rec_t *rec, offset_t *offsets) byte *bd= insert_rec; const byte *rd= rec; /* Skip any unchanged prefix of the record. */ - for (; *bd == *rd; cd++, bd++, rd++) + for (;; cd++, bd++, rd++) if (bd == insert_rec_end) goto no_data; + else if (*bd != *rd) + break; /* Try to copy any data bytes of the preceding record. */ if (c_end - cd > 2) It looks like we would access at most one byte beyond the end of the payload of rec . I do not think that there would be any observable impact outside AddressSanitizer or Valgrind.

            Another puzzle is: why was ALGORITHM=INSTANT not chosen for adding the VARCHAR column? The function instant_alter_column_possible() is computing a way too big max_size. It appears to include the full size of the VARCHAR to the maximum length, even though the locally stored length would be 20 bytes for ROW_FORMAT=DYNAMIC and 788 bytes for ROW_FORMAT=COMPACT or ROW_FORMAT=REDUNDANT. That should be fixed in MDEV-20194.

            marko Marko Mäkelä added a comment - Another puzzle is: why was ALGORITHM=INSTANT not chosen for adding the VARCHAR column? The function instant_alter_column_possible() is computing a way too big max_size . It appears to include the full size of the VARCHAR to the maximum length, even though the locally stored length would be 20 bytes for ROW_FORMAT=DYNAMIC and 788 bytes for ROW_FORMAT=COMPACT or ROW_FORMAT=REDUNDANT . That should be fixed in MDEV-20194 .

            I simplified the test case. It suffices to rebuild a table with 2 rows:

            CREATE TABLE t1 (pk TIMESTAMP PRIMARY KEY, a TIMESTAMP NULL UNIQUE)
            ENGINE=InnoDB;
            INSERT INTO t1 VALUES
              ('2020-03-10 10:21:00', NULL),
              ('0000-00-00 00:00:00', '0000-00-00 00:00:00');
            ALTER TABLE t1 FORCE, ALGORITHM=INPLACE;
            DROP TABLE t1;
            

            For some reason, the column needs to be explicitly declared as TIMESTAMP NULL. Otherwise, it would seem to become NOT NULL, and NULL values would be silently mapped to something else.

            marko Marko Mäkelä added a comment - I simplified the test case. It suffices to rebuild a table with 2 rows: CREATE TABLE t1 (pk TIMESTAMP PRIMARY KEY , a TIMESTAMP NULL UNIQUE ) ENGINE=InnoDB; INSERT INTO t1 VALUES ( '2020-03-10 10:21:00' , NULL ), ( '0000-00-00 00:00:00' , '0000-00-00 00:00:00' ); ALTER TABLE t1 FORCE , ALGORITHM=INPLACE; DROP TABLE t1; For some reason, the column needs to be explicitly declared as TIMESTAMP NULL . Otherwise, it would seem to become NOT NULL , and NULL values would be silently mapped to something else.

            People

              marko Marko Mäkelä
              elenst Elena Stepanova
              Votes:
              0 Vote for this issue
              Watchers:
              2 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.