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

Wrong result or ASAN use-after-poison errors upon SELECT with GROUP BY

Details

    • Bug
    • Status: In Review (View Workflow)
    • Major
    • Resolution: Unresolved
    • 10.5, 10.6, 10.7(EOL), 10.8(EOL), 10.9(EOL), 10.11, 11.0(EOL), 11.1(EOL), 11.2(EOL), 11.3(EOL), 11.4
    • 10.5, 10.6, 10.11, 11.4
    • Optimizer

    Description

      --source include/have_innodb.inc
       
      CREATE TABLE t1 (pk INT PRIMARY KEY, a INT, KEY(a)) ENGINE=InnoDB;
      INSERT INTO t1 VALUES (1,3),(2,6),(3,9),(4,NULL),(5,NULL);
       
      ANALYZE TABLE t1; # optional, fails either way
      SELECT a, pk FROM t1 WHERE pk != 0 AND (a < 0 OR pk <= 0) GROUP BY a, pk;
       
      # Cleanup
      DROP TABLE t1;
      

      10.5 ffc5d064 ASAN

      ==1352396==ERROR: AddressSanitizer: use-after-poison on address 0x6210000d4af5 at pc 0x55c540c0bb39 bp 0x7ff9e84d9220 sp 0x7ff9e84d9210
      READ of size 1 at 0x6210000d4af5 thread T13
          #0 0x55c540c0bb38 in row_mysql_store_col_in_innobase_format(dfield_t*, unsigned char*, unsigned long, unsigned char const*, unsigned long, unsigned long) /data/src/10.5/storage/innobase/row/row0mysql.cc:434
          #1 0x55c540c9387d in row_sel_convert_mysql_key_to_innobase(dtuple_t*, unsigned char*, unsigned long, dict_index_t*, unsigned char const*, unsigned long) /data/src/10.5/storage/innobase/row/row0sel.cc:2602
          #2 0x55c540877d6f in ha_innobase::index_read(unsigned char*, unsigned char const*, unsigned int, ha_rkey_function) /data/src/10.5/storage/innobase/handler/ha_innodb.cc:8741
          #3 0x55c53fd75a6a in handler::index_read_map(unsigned char*, unsigned char const*, unsigned long, ha_rkey_function) /data/src/10.5/sql/handler.h:3798
          #4 0x55c53fd45927 in handler::ha_index_read_map(unsigned char*, unsigned char const*, unsigned long, ha_rkey_function) /data/src/10.5/sql/handler.cc:3123
          #5 0x55c53fd60869 in handler::read_range_first(st_key_range const*, st_key_range const*, bool, bool) /data/src/10.5/sql/handler.cc:6197
          #6 0x55c54016ba9c in QUICK_RANGE_SELECT::get_next_prefix(unsigned int, unsigned int, unsigned char*) /data/src/10.5/sql/opt_range.cc:12688
          #7 0x55c54017f118 in QUICK_GROUP_MIN_MAX_SELECT::next_prefix() /data/src/10.5/sql/opt_range.cc:15519
          #8 0x55c54017d81d in QUICK_GROUP_MIN_MAX_SELECT::get_next() /data/src/10.5/sql/opt_range.cc:15261
          #9 0x55c5401a0549 in rr_quick /data/src/10.5/sql/records.cc:403
          #10 0x55c53f37006d in READ_RECORD::read_record() /data/src/10.5/sql/records.h:80
          #11 0x55c53f67ab6b in sub_select(JOIN*, st_join_table*, bool) /data/src/10.5/sql/sql_select.cc:20707
          #12 0x55c53f678a09 in do_select /data/src/10.5/sql/sql_select.cc:20222
          #13 0x55c53f605378 in JOIN::exec_inner() /data/src/10.5/sql/sql_select.cc:4466
          #14 0x55c53f602963 in JOIN::exec() /data/src/10.5/sql/sql_select.cc:4246
          #15 0x55c53f606d37 in mysql_select(THD*, TABLE_LIST*, List<Item>&, Item*, unsigned int, st_order*, st_order*, Item*, st_order*, unsigned long long, select_result*, st_select_lex_unit*, st_select_lex*) /data/src/10.5/sql/sql_select.cc:4719
          #16 0x55c53f5d8575 in handle_select(THD*, LEX*, select_result*, unsigned long) /data/src/10.5/sql/sql_select.cc:417
          #17 0x55c53f541b71 in execute_sqlcom_select /data/src/10.5/sql/sql_parse.cc:6281
          #18 0x55c53f530a6c in mysql_execute_command(THD*) /data/src/10.5/sql/sql_parse.cc:3977
          #19 0x55c53f54ce6b in mysql_parse(THD*, char*, unsigned int, Parser_state*, bool, bool) /data/src/10.5/sql/sql_parse.cc:8062
          #20 0x55c53f523158 in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool, bool) /data/src/10.5/sql/sql_parse.cc:1889
          #21 0x55c53f51fa81 in do_command(THD*) /data/src/10.5/sql/sql_parse.cc:1370
          #22 0x55c53f96235b in do_handle_one_connection(CONNECT*, bool) /data/src/10.5/sql/sql_connect.cc:1410
          #23 0x55c53f961cbf in handle_one_connection /data/src/10.5/sql/sql_connect.cc:1312
          #24 0x55c540670200 in pfs_spawn_thread /data/src/10.5/storage/perfschema/pfs.cc:2201
          #25 0x7ff9f7a4c608 in start_thread /build/glibc-ZN95T4/glibc-2.31/nptl/pthread_create.c:477
          #26 0x7ff9f7622292 in __clone (/lib/x86_64-linux-gnu/libc.so.6+0x122292)
       
      0x6210000d4af5 is located 501 bytes inside of 4196-byte region [0x6210000d4900,0x6210000d5964)
      allocated by thread T13 here:
          #0 0x7ff9f7f3abc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)
          #1 0x55c54130e87c in sf_malloc /data/src/10.5/mysys/safemalloc.c:121
          #2 0x55c5412dbc81 in my_malloc /data/src/10.5/mysys/my_malloc.c:90
          #3 0x55c5412b7a78 in alloc_root /data/src/10.5/mysys/my_alloc.c:244
          #4 0x55c54017af82 in QUICK_GROUP_MIN_MAX_SELECT::init() /data/src/10.5/sql/opt_range.cc:14919
          #5 0x55c540179dcf in TRP_GROUP_MIN_MAX::make_quick(PARAM*, bool, st_mem_root*) /data/src/10.5/sql/opt_range.cc:14760
          #6 0x55c540129386 in SQL_SELECT::test_quick_select(THD*, Bitmap<64u>, unsigned long long, unsigned long long, bool, bool, bool, bool) /data/src/10.5/sql/opt_range.cc:3054
          #7 0x55c53f60743d in get_quick_record_count /data/src/10.5/sql/sql_select.cc:4763
          #8 0x55c53f60e394 in make_join_statistics /data/src/10.5/sql/sql_select.cc:5494
          #9 0x55c53f5ece6b in JOIN::optimize_inner() /data/src/10.5/sql/sql_select.cc:2255
          #10 0x55c53f5e634b in JOIN::optimize() /data/src/10.5/sql/sql_select.cc:1627
          #11 0x55c53f606b42 in mysql_select(THD*, TABLE_LIST*, List<Item>&, Item*, unsigned int, st_order*, st_order*, Item*, st_order*, unsigned long long, select_result*, st_select_lex_unit*, st_select_lex*) /data/src/10.5/sql/sql_select.cc:4705
          #12 0x55c53f5d8575 in handle_select(THD*, LEX*, select_result*, unsigned long) /data/src/10.5/sql/sql_select.cc:417
          #13 0x55c53f541b71 in execute_sqlcom_select /data/src/10.5/sql/sql_parse.cc:6281
          #14 0x55c53f530a6c in mysql_execute_command(THD*) /data/src/10.5/sql/sql_parse.cc:3977
          #15 0x55c53f54ce6b in mysql_parse(THD*, char*, unsigned int, Parser_state*, bool, bool) /data/src/10.5/sql/sql_parse.cc:8062
          #16 0x55c53f523158 in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool, bool) /data/src/10.5/sql/sql_parse.cc:1889
          #17 0x55c53f51fa81 in do_command(THD*) /data/src/10.5/sql/sql_parse.cc:1370
          #18 0x55c53f96235b in do_handle_one_connection(CONNECT*, bool) /data/src/10.5/sql/sql_connect.cc:1410
          #19 0x55c53f961cbf in handle_one_connection /data/src/10.5/sql/sql_connect.cc:1312
          #20 0x55c540670200 in pfs_spawn_thread /data/src/10.5/storage/perfschema/pfs.cc:2201
          #21 0x7ff9f7a4c608 in start_thread /build/glibc-ZN95T4/glibc-2.31/nptl/pthread_create.c:477
       
      Thread T13 created by T0 here:
          #0 0x7ff9f7e67805 in pthread_create (/lib/x86_64-linux-gnu/libasan.so.5+0x3a805)
          #1 0x55c54066b1a4 in my_thread_create /data/src/10.5/storage/perfschema/my_thread.h:38
          #2 0x55c5406705f3 in pfs_spawn_thread_v1 /data/src/10.5/storage/perfschema/pfs.cc:2252
          #3 0x55c53f2134fe in inline_mysql_thread_create /data/src/10.5/include/mysql/psi/mysql_thread.h:1323
          #4 0x55c53f229512 in create_thread_to_handle_connection(CONNECT*) /data/src/10.5/sql/mysqld.cc:6028
          #5 0x55c53f229b91 in create_new_thread(CONNECT*) /data/src/10.5/sql/mysqld.cc:6087
          #6 0x55c53f229eee in handle_accepted_socket(st_mysql_socket, st_mysql_socket) /data/src/10.5/sql/mysqld.cc:6152
          #7 0x55c53f22ab0d in handle_connections_sockets() /data/src/10.5/sql/mysqld.cc:6279
          #8 0x55c53f228d1f in mysqld_main(int, char**) /data/src/10.5/sql/mysqld.cc:5674
          #9 0x55c53f211d9c in main /data/src/10.5/sql/main.cc:25
          #10 0x7ff9f75270b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
       
      SUMMARY: AddressSanitizer: use-after-poison /data/src/10.5/storage/innobase/row/row0mysql.cc:434 in row_mysql_store_col_in_innobase_format(dfield_t*, unsigned char*, unsigned long, unsigned char const*, unsigned long, unsigned long)
      Shadow bytes around the buggy address:
        0x0c4280012900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
        0x0c4280012910: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
        0x0c4280012920: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x0c4280012930: 00 00 00 00 f7 00 02 f7 00 02 f7 00 00 00 00 00
        0x0c4280012940: 00 00 00 00 00 00 00 00 00 00 00 f7 04 f7 00 00
      =>0x0c4280012950: 00 00 00 f7 01 f7 05 f7 00 00 00 00 00 f7[05]f7
        0x0c4280012960: 01 f7 00 00 00 00 00 00 00 00 f7 f7 f7 f7 f7 f7
        0x0c4280012970: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
        0x0c4280012980: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
        0x0c4280012990: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
        0x0c42800129a0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
      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
        Freed heap region:       fd
        Stack left redzone:      f1
        Stack mid redzone:       f2
        Stack right redzone:     f3
        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
        Shadow gap:              cc
      ==1352396==ABORTING
      

      Reproducible on 10.5, 10.6.
      No obvious immediate problem on a non-ASAN build.
      No obvious failures with MyISAM instead of InnoDB.
      Upd: See comments for an alternative test case affecting MyISAM and Aria and causing problems on non-ASAN builds
      Not reproducible on 10.4.
      The commit below introduced in 10.5 a change of plan which leads to the failure. I don't know if the plan and the problem are possible to achieve in 10.4 by adjusting the test case.

      commit b3ab3105fdb34dae6c2d4270751bc0694c3d9df8
      Author: Monty
      Date:   Wed Mar 4 19:52:19 2020 +0200
       
          Removed double calls to records_in_range from distinct and group by
          
          Fixed by moving testing of get_best_group_min_max() after range testing.
      

      Plan after the change (failing)

      explain extended SELECT a, pk FROM t1 WHERE pk != 0 AND (a < 0 OR pk <= 0) GROUP BY a, pk;
      id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
      1	SIMPLE	t1	range	PRIMARY,a	a	9	NULL	1	100.00	Using where; Using index for group-by
      Warnings:
      Note	1003	select `test`.`t1`.`a` AS `a`,`test`.`t1`.`pk` AS `pk` from `test`.`t1` where `test`.`t1`.`pk` <> 0 and (`test`.`t1`.`a` < 0 or `test`.`t1`.`pk` <= 0) group by `test`.`t1`.`a`,`test`.`t1`.`pk`
      

      Plan before the change (not failing)

      explain extended SELECT a, pk FROM t1 WHERE pk != 0 AND (a < 0 OR pk <= 0) GROUP BY a, pk;
      id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
      1	SIMPLE	t1	index	PRIMARY,a	a	5	NULL	5	100.00	Using where; Using index
      Warnings:
      Note	1003	select `test`.`t1`.`a` AS `a`,`test`.`t1`.`pk` AS `pk` from `test`.`t1` where `test`.`t1`.`pk` <> 0 and (`test`.`t1`.`a` < 0 or `test`.`t1`.`pk` <= 0) group by `test`.`t1`.`a`,`test`.`t1`.`pk`
      

      Attachments

        Issue Links

          Activity

            making check_quick_select() not update param->quick_rows when update_tbl_stats==false breaks index_merge test cases.

            What if instead we make get_best_group_min_max() look at param->table->opt_range? This seems to work:

            diff --git a/sql/opt_range.cc b/sql/opt_range.cc
            index 391a04c2a1a..e2dbab4cff8 100644
            --- a/sql/opt_range.cc
            +++ b/sql/opt_range.cc
            @@ -14167,7 +14167,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
                 {
                   if ((cur_index_tree= tree->keys[cur_param_idx]))
                   {
            -        cur_quick_prefix_records= param->quick_rows[cur_index];
            +        cur_quick_prefix_records= param->table->opt_range[cur_index].rows;
                     if (unlikely(cur_index_tree && thd->trace_started()))
                     {
                       Json_writer_array trace_range(thd, "ranges");
            

            oleg.smirnov any thoughts?
            Also please file an MDEV that condition pushdown from HAVING to WHERE is not traced and fix it....

            psergei Sergei Petrunia added a comment - making check_quick_select() not update param->quick_rows when update_tbl_stats==false breaks index_merge test cases. What if instead we make get_best_group_min_max() look at param->table->opt_range? This seems to work: diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 391a04c2a1a..e2dbab4cff8 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -14167,7 +14167,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) { if ((cur_index_tree= tree->keys[cur_param_idx])) { - cur_quick_prefix_records= param->quick_rows[cur_index]; + cur_quick_prefix_records= param->table->opt_range[cur_index].rows; if (unlikely(cur_index_tree && thd->trace_started())) { Json_writer_array trace_range(thd, "ranges"); oleg.smirnov any thoughts? Also please file an MDEV that condition pushdown from HAVING to WHERE is not traced and fix it....
            oleg.smirnov Oleg Smirnov added a comment -

            But with this patch cur_quick_prefix_records is being assigned to an unreasonably large value 0x02131f9e98e0.

            And the explain changes to :

            MariaDB [mdev24819]> EXPLAIN SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7;
            +------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
            | id   | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                    |
            +------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
            |    1 | SIMPLE      | t1    | index | a,b           | b    | 9       | NULL | 3    | Using where; Using index |
            +------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
            

            We no longer have "Using index for group-by" so probably the initial problem is not fixed.
            By the way, the last explain plan ("Using index") is generated by the previous version of MariaDB 10.4 (and probably earlier versions) and MySQL 5.5-8.0. But starting from 10.5 the explain plan has changed to "Using index for group-by".

            I believe it would be good to have cur_quick_prefix_records = HA_POS_ERROR in that place, then the explain plan remains the same and the result of the query is correct.

            oleg.smirnov Oleg Smirnov added a comment - But with this patch cur_quick_prefix_records is being assigned to an unreasonably large value 0x02131f9e98e0. And the explain changes to : MariaDB [mdev24819]> EXPLAIN SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; + ------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | + ------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ | 1 | SIMPLE | t1 | index | a,b | b | 9 | NULL | 3 | Using where ; Using index | + ------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ We no longer have "Using index for group-by" so probably the initial problem is not fixed. By the way, the last explain plan ("Using index") is generated by the previous version of MariaDB 10.4 (and probably earlier versions) and MySQL 5.5-8.0. But starting from 10.5 the explain plan has changed to "Using index for group-by". I believe it would be good to have cur_quick_prefix_records = HA_POS_ERROR in that place, then the explain plan remains the same and the result of the query is correct.

            The fix I've suggested needs to be adjusted:

            -        cur_quick_prefix_records= param->quick_rows[cur_index];
            +        if (param->table->opt_range_keys.is_set(cur_index)  
            +          cur_quick_prefix_records= param->table->opt_range[cur_index].rows;
            +        else
            +          cur_quick_prefix_records= HA_POS_ERROR;
            

            psergei Sergei Petrunia added a comment - The fix I've suggested needs to be adjusted: - cur_quick_prefix_records= param->quick_rows[cur_index]; + if (param->table->opt_range_keys.is_set(cur_index) + cur_quick_prefix_records= param->table->opt_range[cur_index].rows; + else + cur_quick_prefix_records= HA_POS_ERROR;
            oleg.smirnov Oleg Smirnov added a comment - - edited

            Filed a separate MDEV-29179 for missing HAVING pushdown, otherwise ready for review.

            oleg.smirnov Oleg Smirnov added a comment - - edited Filed a separate MDEV-29179 for missing HAVING pushdown, otherwise ready for review.
            alice Alice Sherepa added a comment -

            please check also MDEV-27572 (probably the same bug)

            alice Alice Sherepa added a comment - please check also MDEV-27572 (probably the same bug)

            People

              psergei Sergei Petrunia
              elenst Elena Stepanova
              Votes:
              0 Vote for this issue
              Watchers:
              4 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.