[MDEV-30052] Crash with a query containing nested WINDOW clauses Created: 2022-11-21  Updated: 2023-12-20  Resolved: 2023-01-20

Status: Closed
Project: MariaDB Server
Component/s: Parser
Affects Version/s: 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 10.10, 10.11
Fix Version/s: 10.11.2, 10.3.38, 10.4.28, 10.5.19, 10.6.12, 10.7.8, 10.8.7, 10.9.5, 10.10.3

Type: Bug Priority: Critical
Reporter: Roel Van de Paar Assignee: Oleksandr Byelkin
Resolution: Fixed Votes: 0
Labels: ASAN, affects-tests, regression-10.4

Issue Links:
Duplicate
is duplicated by MDEV-28515 Assertion `field->table == table' fai... Closed
is duplicated by MDEV-29359 Server crashed with heap-use-after-fr... Closed
is duplicated by MDEV-32036 Server crash in find_field_in_table Closed
PartOf
includes MDEV-25643 Assertion `table->no_keyread || !tabl... Closed
includes MDEV-28501 SIGSEGV in update_depend_map_for_orde... Closed
includes MDEV-28505 Server crash in sql/sql_select.cc:198... Closed
includes MDEV-28510 SIGSEGV in get_sort_by_table and SIGS... Closed
includes MDEV-28516 SIGSEGV in get_sort_by_table, UBSAN: ... Closed
includes MDEV-28799 SIGSEGV in JOIN_CACHE::reset_join and... Closed
includes MDEV-29052 SIGSEGV's in hp_rec_hashnr and my_has... Closed
includes MDEV-29353 SIGSEGV's in _ma_unique_hash, _ma_mak... Closed
includes MDEV-30381 investigate group by window issues Closed

 Description   

Setting this to critical due to use-after-poison in sql/sql_list.h (ref ASAN output in first comment below), as well as the code location (opt_subselect.cc) and the code in crashes on (total_key_length += inner->max_length).

CREATE TABLE t (c INT) ENGINE=InnoDB;
UPDATE t SET c=1 WHERE c=2 ORDER BY (1 IN ((SELECT * FROM (SELECT * FROM t) AS v1 GROUP BY c WINDOW v2 AS (ORDER BY (SELECT * FROM t GROUP BY c WINDOW v3 AS (PARTITION BY c))))));

Leads to:

10.11.2 8283948846740a22f96bbe7bccf250708406d5d9 (Debug)

Core was generated by `/test/MD171122-mariadb-10.11.2-linux-x86_64-dbg/bin/mysqld --no-defaults --core'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  subquery_types_allow_materialization (thd=thd@entry=0x1463c4000d48, 
    in_subs=in_subs@entry=0x1463c4028708)
    at /test/10.11_dbg/sql/opt_subselect.cc:891
891	    total_key_length += inner->max_length;
[Current thread is 1 (Thread 0x1463f8912700 (LWP 3156269))]
(gdb) bt
#0  subquery_types_allow_materialization (thd=thd@entry=0x1463c4000d48, in_subs=in_subs@entry=0x1463c4028708) at /test/10.11_dbg/sql/opt_subselect.cc:891
#1  0x000056489b07a4e6 in is_materialization_applicable (thd=thd@entry=0x1463c4000d48, in_subs=in_subs@entry=0x1463c4028708, child_select=child_select@entry=0x1463c4014068) at /test/10.11_dbg/sql/sql_lex.h:1651
#2  0x000056489b07ab8b in check_and_do_in_subquery_rewrites (join=join@entry=0x1463c4029ef8) at /test/10.11_dbg/sql/opt_subselect.cc:755
#3  0x000056489af4049a in JOIN::prepare (this=0x1463c4029ef8, tables_init=<optimized out>, conds_init=<optimized out>, og_num=<optimized out>, order_init=<optimized out>, skip_order_by=skip_order_by@entry=false, group_init=<optimized out>, having_init=<optimized out>, proc_param_init=<optimized out>, select_lex_arg=<optimized out>, unit_arg=<optimized out>) at /test/10.11_dbg/sql/sql_select.cc:1565
#4  0x000056489b2bd33d in subselect_single_select_engine::prepare (this=0x1463c4028900, thd=0x1463c4000d48) at /test/10.11_dbg/sql/sql_lex.h:1368
#5  0x000056489b2bc80b in Item_subselect::fix_fields (this=this@entry=0x1463c4028708, thd_param=thd_param@entry=0x1463c4000d48, ref=ref@entry=0x1463c4028968) at /test/10.11_dbg/sql/item_subselect.cc:295
#6  0x000056489b2bcc50 in Item_in_subselect::fix_fields (this=0x1463c4028708, thd_arg=0x1463c4000d48, ref=0x1463c4028968) at /test/10.11_dbg/sql/item_subselect.cc:3545
#7  0x000056489af102e7 in Item::fix_fields_if_needed (ref=<optimized out>, thd=0x1463c4000d48, this=0x1463c4028708) at /test/10.11_dbg/sql/item.h:1164
#8  Item::fix_fields_if_needed_for_scalar (ref=<optimized out>, thd=0x1463c4000d48, this=0x1463c4028708) at /test/10.11_dbg/sql/item.h:1156
#9  Item::fix_fields_if_needed_for_order_by (ref=<optimized out>, thd=0x1463c4000d48, this=0x1463c4028708) at /test/10.11_dbg/sql/item.h:1164
#10 find_order_in_list (thd=thd@entry=0x1463c4000d48, ref_pointer_array=<optimized out>, tables=tables@entry=0x1463c4013330, order=order@entry=0x1463c4028958, fields=@0x1463f8910cf0: {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x56489c459f00 <end_of_list>, last = 0x1463f8910cf0, elements = 0}, <No data fields>}, all_fields=@0x1463c4029a28: {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x56489c459f00 <end_of_list>, last = 0x1463c4029a28, elements = 0}, <No data fields>}, is_group_field=false, add_to_all_fields=true, from_window_spec=false) at /test/10.11_dbg/sql/sql_select.cc:25730
#11 0x000056489af3bd31 in setup_order (thd=thd@entry=0x1463c4000d48, ref_pointer_array=<optimized out>, tables=tables@entry=0x1463c4013330, fields=@0x1463f8910cf0: {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x56489c459f00 <end_of_list>, last = 0x1463f8910cf0, elements = 0}, <No data fields>}, all_fields=@0x1463c4029a28: {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x56489c459f00 <end_of_list>, last = 0x1463c4029a28, elements = 0}, <No data fields>}, order=0x1463c4028958, from_window_spec=false) at /test/10.11_dbg/sql/sql_select.cc:25777
#12 0x000056489af3fccd in setup_without_group (reserved=<optimized out>, hidden_group_fields=0x1463c40299d7, win_funcs=<optimized out>, win_specs=<optimized out>, group=<optimized out>, order=<optimized out>, conds=0x1463c4029b10, all_fields=@0x1463c4029a28: {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x56489c459f00 <end_of_list>, last = 0x1463c4029a28, elements = 0}, <No data fields>}, fields=<optimized out>, leaves=<optimized out>, tables=<optimized out>, ref_pointer_array=<optimized out>, thd=<optimized out>) at /test/10.11_dbg/sql/sql_select.cc:888
#13 JOIN::prepare (this=this@entry=0x1463c4029690, tables_init=tables_init@entry=0x1463c4013330, conds_init=conds_init@entry=0x1463c4013dc0, og_num=og_num@entry=1, order_init=order_init@entry=0x1463c4028958, skip_order_by=skip_order_by@entry=false, group_init=<optimized out>, having_init=<optimized out>, proc_param_init=<optimized out>, select_lex_arg=<optimized out>, unit_arg=<optimized out>) at /test/10.11_dbg/sql/sql_select.cc:1465
#14 0x000056489af5650c in mysql_select (thd=thd@entry=0x1463c4000d48, tables=tables@entry=0x1463c4013330, fields=@0x1463f8910cf0: {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x56489c459f00 <end_of_list>, last = 0x1463f8910cf0, elements = 0}, <No data fields>}, conds=conds@entry=0x1463c4013dc0, og_num=1, order=0x1463c4028958, group=0x0, having=0x0, proc_param=0x0, select_options=37383395344512, result=0x1463c4029580, unit=0x1463c4004f88, select_lex=0x1463c40057b8) at /test/10.11_dbg/sql/sql_select.cc:5056
#15 0x000056489afcf16b in mysql_multi_update (thd=thd@entry=0x1463c4000d48, table_list=0x1463c4013330, fields=fields@entry=0x1463c4005a58, values=values@entry=0x1463c4005e88, conds=0x1463c4013dc0, options=0, handle_duplicates=DUP_ERROR, ignore=false, unit=0x1463c4004f88, select_lex=0x1463c40057b8, result=0x1463f8910ed0) at /test/10.11_dbg/sql/sql_update.cc:1980
#16 0x000056489aed18c8 in mysql_execute_command (thd=thd@entry=0x1463c4000d48, is_called_from_prepared_stmt=is_called_from_prepared_stmt@entry=false) at /test/10.11_dbg/sql/sql_parse.cc:4489
#17 0x000056489aebe606 in mysql_parse (thd=thd@entry=0x1463c4000d48, rawbuf=<optimized out>, length=<optimized out>, parser_state=parser_state@entry=0x1463f8911300) at /test/10.11_dbg/sql/sql_parse.cc:7998
#18 0x000056489aecbb41 in dispatch_command (command=command@entry=COM_QUERY, thd=thd@entry=0x1463c4000d48, packet=packet@entry=0x1463c400adf9 "UPDATE t SET c=1 WHERE c=2 ORDER BY (1 IN ((SELECT * FROM (SELECT * FROM t) AS v1 GROUP BY c WINDOW v2 AS (ORDER BY (SELECT * FROM t GROUP BY c WINDOW v3 AS (PARTITION BY c))))))", packet_length=packet_length@entry=178, blocking=blocking@entry=true) at /test/10.11_dbg/sql/sql_class.h:1346
#19 0x000056489aecdf7f in do_command (thd=0x1463c4000d48, blocking=blocking@entry=true) at /test/10.11_dbg/sql/sql_parse.cc:1407
#20 0x000056489b028763 in do_handle_one_connection (connect=<optimized out>, connect@entry=0x56489d9a6fb8, put_in_cache=put_in_cache@entry=true) at /test/10.11_dbg/sql/sql_connect.cc:1416
#21 0x000056489b028c32 in handle_one_connection (arg=0x56489d9a6fb8) at /test/10.11_dbg/sql/sql_connect.cc:1318
#22 0x000014641159c609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#23 0x0000146411188133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Bug confirmed present in:
MariaDB: 10.3.37 (dbg), 10.4.27 (dbg), 10.5.18 (dbg), 10.6.10 (dbg), 10.7.6 (dbg), 10.8.5 (dbg), 10.9.3 (dbg), 10.10.2 (dbg), 10.11.2 (dbg)

Bug (or feature/syntax) confirmed not present in:
MariaDB: 10.3.37 (opt), 10.4.27 (opt), 10.5.18 (opt), 10.6.10 (opt), 10.7.6 (opt), 10.8.5 (opt), 10.9.3 (opt), 10.10.2 (opt), 10.11.2 (opt)
MySQL: 5.5.62 (dbg), 5.5.62 (opt), 5.6.51 (dbg), 5.6.51 (opt), 5.7.38 (dbg), 5.7.38 (opt), 8.0.29 (dbg), 8.0.29 (opt)



 Comments   
Comment by Roel Van de Paar [ 2022-11-21 ]

ASAN Sees use-after-poison

10.11.0 fe1f8f2c6b6f3b8e3383168225f9ae7853028947 (Optimized, UBASAN)

==3243144==ERROR: AddressSanitizer: use-after-poison on address 0x6290000887b0 at pc 0x55f7a34ef836 bp 0x14cfbfdb91e0 sp 0x14cfbfdb91d0
READ of size 8 at 0x6290000887b0 thread T16
    #0 0x55f7a34ef835 in base_list_iterator::next() /test/10.11_opt_san/sql/sql_list.h:431
    #1 0x55f7a34ef835 in List_iterator<Item>::operator++(int) /test/10.11_opt_san/sql/sql_list.h:596
    #2 0x55f7a34ef835 in subquery_types_allow_materialization /test/10.11_opt_san/sql/opt_subselect.cc:888
    #3 0x55f7a34f069a in is_materialization_applicable(THD*, Item_in_subselect*, st_select_lex*) /test/10.11_opt_san/sql/opt_subselect.cc:521
    #4 0x55f7a34f2d25 in check_and_do_in_subquery_rewrites(JOIN*) /test/10.11_opt_san/sql/opt_subselect.cc:755
    #5 0x55f7a2db7f6e in JOIN::prepare(TABLE_LIST*, Item*, unsigned int, st_order*, bool, st_order*, Item*, st_order*, st_select_lex*, st_select_lex_unit*) /test/10.11_opt_san/sql/sql_select.cc:1556
    #6 0x55f7a48acec4 in subselect_single_select_engine::prepare(THD*) /test/10.11_opt_san/sql/item_subselect.cc:3930
    #7 0x55f7a48a5ad5 in Item_subselect::fix_fields(THD*, Item**) /test/10.11_opt_san/sql/item_subselect.cc:295
    #8 0x55f7a48a8d3c in Item_in_subselect::fix_fields(THD*, Item**) /test/10.11_opt_san/sql/item_subselect.cc:3589
    #9 0x55f7a2bacd1e in Item::fix_fields_if_needed(THD*, Item**) /test/10.11_opt_san/sql/item.h:1144
    #10 0x55f7a2bacd1e in Item::fix_fields_if_needed_for_scalar(THD*, Item**) /test/10.11_opt_san/sql/item.h:1148
    #11 0x55f7a2bacd1e in Item::fix_fields_if_needed_for_order_by(THD*, Item**) /test/10.11_opt_san/sql/item.h:1156
    #12 0x55f7a2bacd1e in find_order_in_list /test/10.11_opt_san/sql/sql_select.cc:25677
    #13 0x55f7a2cb5e34 in setup_order(THD*, Bounds_checked_array<Item*>, TABLE_LIST*, List<Item>&, List<Item>&, st_order*, bool) /test/10.11_opt_san/sql/sql_select.cc:25724
    #14 0x55f7a2db5b3d in setup_without_group /test/10.11_opt_san/sql/sql_select.cc:900
    #15 0x55f7a2db5b3d in JOIN::prepare(TABLE_LIST*, Item*, unsigned int, st_order*, bool, st_order*, Item*, st_order*, st_select_lex*, st_select_lex_unit*) /test/10.11_opt_san/sql/sql_select.cc:1456
    #16 0x55f7a2dc3fd6 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*) /test/10.11_opt_san/sql/sql_select.cc:5045
    #17 0x55f7a30ea947 in mysql_multi_update(THD*, TABLE_LIST*, List<Item>*, List<Item>*, Item*, unsigned long long, enum_duplicates, bool, st_select_lex_unit*, st_select_lex*, multi_update**) /test/10.11_opt_san/sql/sql_update.cc:1980
    #18 0x55f7a2a1faa5 in mysql_execute_command(THD*, bool) /test/10.11_opt_san/sql/sql_parse.cc:4487
    #19 0x55f7a29a4500 in mysql_parse(THD*, char*, unsigned int, Parser_state*) /test/10.11_opt_san/sql/sql_parse.cc:8035
    #20 0x55f7a29f90ff in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool) /test/10.11_opt_san/sql/sql_parse.cc:1894
    #21 0x55f7a2a043fd in do_command(THD*, bool) /test/10.11_opt_san/sql/sql_parse.cc:1407
    #22 0x55f7a32ed4cd in do_handle_one_connection(CONNECT*, bool) /test/10.11_opt_san/sql/sql_connect.cc:1418
    #23 0x55f7a32efb3c in handle_one_connection /test/10.11_opt_san/sql/sql_connect.cc:1312
    #24 0x14cfe2fe1608 in start_thread /build/glibc-SzIz7B/glibc-2.31/nptl/pthread_create.c:477
    #25 0x14cfe2256132 in __clone (/lib/x86_64-linux-gnu/libc.so.6+0x11f132)
 
0x6290000887b0 is located 5552 bytes inside of 16400-byte region [0x629000087200,0x62900008b210)
allocated by thread T16 here:
    #0 0x55f7a2178a08 in __interceptor_malloc (/test/UBASAN_MD010922-mariadb-10.11.0-linux-x86_64-opt/bin/mariadbd+0x79c3a08)
    #1 0x55f7a6668824 in my_malloc /test/10.11_opt_san/mysys/my_malloc.c:90
    #2 0x55f7a6644600 in root_alloc /test/10.11_opt_san/mysys/my_alloc.c:66
    #3 0x55f7a6644600 in reset_root_defaults /test/10.11_opt_san/mysys/my_alloc.c:243
    #4 0x55f7a269663c in THD::init_for_queries() /test/10.11_opt_san/sql/sql_class.cc:1385
    #5 0x55f7a32e7ee3 in prepare_new_connection_state(THD*) /test/10.11_opt_san/sql/sql_connect.cc:1240
    #6 0x55f7a32e9717 in thd_prepare_connection(THD*) /test/10.11_opt_san/sql/sql_connect.cc:1333
    #7 0x55f7a32e9717 in thd_prepare_connection(THD*) /test/10.11_opt_san/sql/sql_connect.cc:1322
    #8 0x55f7a32ec509 in do_handle_one_connection(CONNECT*, bool) /test/10.11_opt_san/sql/sql_connect.cc:1408
    #9 0x55f7a32efb3c in handle_one_connection /test/10.11_opt_san/sql/sql_connect.cc:1312
    #10 0x14cfe2fe1608 in start_thread /build/glibc-SzIz7B/glibc-2.31/nptl/pthread_create.c:477
 
Thread T16 created by T0 here:
    #0 0x55f7a20a5a45 in pthread_create (/test/UBASAN_MD010922-mariadb-10.11.0-linux-x86_64-opt/bin/mariadbd+0x78f0a45)
    #1 0x55f7a21c8f83 in create_thread_to_handle_connection(CONNECT*) /test/10.11_opt_san/sql/mysqld.cc:6018
    #2 0x55f7a21da00f in handle_accepted_socket(st_mysql_socket, st_mysql_socket) /test/10.11_opt_san/sql/mysqld.cc:6139
    #3 0x55f7a21db037 in handle_connections_sockets() /test/10.11_opt_san/sql/mysqld.cc:6263
    #4 0x55f7a21ddf94 in mysqld_main(int, char**) /test/10.11_opt_san/sql/mysqld.cc:5913
    #5 0x14cfe215b082 in __libc_start_main ../csu/libc-start.c:308
 
SUMMARY: AddressSanitizer: use-after-poison /test/10.11_opt_san/sql/sql_list.h:431 in base_list_iterator::next()
Shadow bytes around the buggy address:
  0x0c52800090a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c52800090b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c52800090c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 f7 00 00
  0x0c52800090d0: f7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c52800090e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c52800090f0: 00 00 00 00 f7 f7[f7]f7 00 00 00 00 00 00 00 00
  0x0c5280009100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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

Comment by Elena Stepanova [ 2022-11-21 ]

It looks identical to (at least) MDEV-29359.

Comment by Rex Johnston [ 2022-11-24 ]

Still puzzling over this one. Valgrind tells me this.

==22054== Thread 33:
==22054== Invalid read of size 8
==22054==    at 0x9B88AB: base_list_iterator::next_fast() (sql_list.h:443)
==22054==    by 0x9F5BB8: List_iterator_fast<Item>::operator++(int) (sql_list.h:620)
==22054==    by 0xBF8927: JOIN::eval_select_list_used_tables() (sql_select.cc:13203)
==22054==    by 0xBA0588: JOIN::optimize_inner() (sql_select.cc:2113)
==22054==    by 0xB9F9ED: JOIN::optimize() (sql_select.cc:1871)
==22054==    by 0xB063EC: st_select_lex::optimize_unflattened_subqueries(bool) (sql_lex.cc:4905)
==22054==    by 0xD85DF6: JOIN::optimize_constant_subqueries() (opt_subselect.cc:5689)
==22054==    by 0xBA09CF: JOIN::optimize_inner() (sql_select.cc:2194)
==22054==    by 0xB9F9ED: JOIN::optimize() (sql_select.cc:1871)
==22054==    by 0xBAB5AA: 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*) (sql_select.cc:5096)
==22054==    by 0xB9A7D1: handle_select(THD*, LEX*, select_result*, unsigned long long) (sql_select.cc:581)
==22054==    by 0xB44AEE: execute_sqlcom_select(THD*, TABLE_LIST*) (sql_parse.cc:6263)
==22054==  Address 0x3b64e958 is 56 bytes inside a block of size 64 alloc'd
==22054==    at 0x483877F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==22054==    by 0x18B620F: my_malloc (my_malloc.c:90)
==22054==    by 0x18A69D9: alloc_root (my_alloc.c:287)
==22054==    by 0x9B85EA: Sql_alloc::operator new(unsigned long, st_mem_root*) (sql_alloc.h:37)
==22054==    by 0x9B87AF: base_list::push_back(void*, st_mem_root*) (sql_list.h:195)
==22054==    by 0xA05504: List<Item>::push_back(Item*, st_mem_root*) (sql_list.h:505)
==22054==    by 0xB03607: st_select_lex::add_item_to_list(THD*, Item*) (sql_lex.cc:3473)
==22054==    by 0xB251F4: add_item_to_list(THD*, Item*) (sql_class.h:7373)
==22054==    by 0xE47393: MYSQLparse(THD*) (sql_yacc.yy:8958)
==22054==    by 0xB4EC2B: parse_sql(THD*, Parser_state*, Object_creation_ctx*, bool) (sql_parse.cc:10390)
==22054==    by 0xB492B8: mysql_parse(THD*, char*, unsigned int, Parser_state*) (sql_parse.cc:7950)
==22054==    by 0xB3640D: dispatch_command(enum_server_command, THD*, char*, unsigned int, bool) (sql_parse.cc:1894)
==22054== 
==22054== (action on error) vgdb me ... 

Which makes no sense at all. It looks like the memory allocation metadata is corrupted.

Comment by Roel Van de Paar [ 2022-11-24 ]

Johnston The Valgrind stack matches the ASAN stack for the first two frames. Does the ASAN trace above highlight where the issue is located?

Comment by Roel Van de Paar [ 2022-11-24 ]

Whilst the use-after-poison issue was discovered while inside the base_list_iterator class def in sql/sql_list.h (at line 431);

  inline void *next(void)
  {
    prev=el;
    current= *el;
    el= &current->next;
    return current->info;    // << Here, line 431
  }

The original memory allocation for this thread was done by thread T16, which was started itself by the main mysqld_main thread (T0), using this code in reset_root_defaults in mysys/my_alloc.c line 243:

      /* Allocate new prealloc block and add it to the end of free list */
      if ((mem= (USED_MEM *) root_alloc(mem_root, size, &alloced_size,
                                        MYF(MY_WME))))
      {
        mem->size= alloced_size;
        mem->left= alloced_size - ALIGN_SIZE(sizeof(USED_MEM));
        mem->next= *prev;
        *prev= mem_root->pre_alloc= mem;
        TRASH_MEM(mem);
      }

As per the ASAN trace. However, interestingly, we see this at the top of that code:

#if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG))

Showing that using/not using Valgrind will lead to different outcomes here. Additionally, and a little strange, there is this "in-between" #endif preprocessor directive near the end of the function code block (i.e. is it in the right place?):

  }
  else
#endif
    mem_root->pre_alloc= 0;
 
  DBUG_VOID_RETURN;
}

This function in particular (also in combination with having Valgrind turned on/off) may warrant further research.

Comment by Roel Van de Paar [ 2022-11-24 ]

inner->max_length in subquery_types_allow_materialization looks to be corrupted

Program terminated with signal SIGSEGV, Segmentation fault.
#0  subquery_types_allow_materialization (thd=thd@entry=0x14d220000d48, in_subs=in_subs@entry=0x14d220028708)
    at /test/10.11_dbg/sql/opt_subselect.cc:891
891	    total_key_length += inner->max_length;
[Current thread is 1 (Thread 0x14d2690c4700 (LWP 1491502))]
(gdb) p total_key_length
$1 = 0
(gdb) p inner->max_length
Cannot access memory at address 0x8f8f8f8f8f8f8f97

Comment by Rex Johnston [ 2022-11-25 ]

It's poisoned memory by a free() in removing a redundant group by clause in subquery.

(gdb) p select_lex->first_inner_unit()->first_select()->join->fields_list
$27 = (List<Item> &) @0x2edb1310: {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x2edb0fa0, last = 0x2edb0fa0, elements = 1}, <No data fields>}
(gdb) monitor get_vbits  0x2edb0fa0 10
00000000 00000000 0000
 
|
V
 
(gdb) monitor get_vbits  0x2edb0fa0 10
________ ________ ____
Address 0x2EDB0FA0 len 10 has 10 bytes unaddressable

The offending piece of code is in remove_redundant_subquery_clauses()

        /*
          Remove from the JOIN::all_fields list any reference to the elements
          of the eliminated GROUP BY list unless it is 'in_field_list'.
          This is needed in order not to confuse JOIN::make_aggr_tables_info()
          when it constructs different structure for execution phase.
	*/
        List_iterator<Item> li(subq_select_lex->join->all_fields);
	Item *item;
        while ((item= li++))
	{
          if (item == *ord->item)
	    li.remove();
	}

Will fix when I figure out how.

Comment by Roel Van de Paar [ 2022-11-26 ]

Great going

Comment by Rex Johnston [ 2022-12-05 ]

Hi Igor,
I was given this MDEV to look at by Sergei P. Looks like it's your code, I have no idea whether this is the right thing to do. Just guessing.
Cheers, Rex

Comment by Rex Johnston [ 2022-12-06 ]

and this still crashes...

PREPARE FOO from "UPDATE t SET c=1 WHERE c=2 ORDER BY (1 IN ((SELECT * FROM (SELECT * FROM t) AS v1 GROUP BY c WINDOW v2 AS (ORDER BY (SELECT * FROM t GROUP BY c WINDOW v3 AS (PARTITION BY c))))))";
EXECUTE FOO;

Comment by Roel Van de Paar [ 2022-12-20 ]

Found an additional testcase for use-after-poison in base_list_iterator::next to test with.

CREATE TABLE x (x TEXT);
SELECT (x=(SELECT x FROM x WHERE (x,x) NOT IN (SELECT''* x * 1,''FROM x WINDOW x AS (PARTITION BY x ORDER BY x ^ (SELECT 1 FROM x AS x WHERE x GROUP BY 1,x WINDOW x AS (PARTITION BY x ORDER BY (1))) ^ x RANGE BETWEEN 1 FOLLOWING AND 1 FOLLOWING))) * 1^ 1 IN (-1,0,1,0,''/ x=x + CASE x WHEN TRUE THEN 0 ELSE 1 END OR x=x OR x=x)),''/ 1 AS x FROM x WINDOW x AS (PARTITION BY x ORDER BY (SELECT 1 FROM x WHERE x GROUP BY (x=(SELECT x FROM x WHERE (x,x) NOT IN (SELECT 1)) * 1^ 1 IN (-1,0,1,0,''/ x=x + CASE WHEN TRUE THEN 0 ELSE 1 END OR x=x OR x=x)),x WINDOW x AS (PARTITION BY x ORDER BY (1))) RANGE BETWEEN 1 FOLLOWING AND 1 FOLLOWING);

Comment by Roel Van de Paar [ 2023-01-07 ]

Additional testcase with a very different stack, 10.4+ only, and a different ASAN use-after-poison in sql/sql_list.h

CREATE TABLE t (c INT);
SET SESSION optimizer_switch='materialization=OFF';
UPDATE t SET c=1 WHERE c='' ORDER BY 1=(1 IN ((SELECT * FROM (SELECT * FROM t) AS v3 NATURAL JOIN t AS v2 NATURAL JOIN t AS v4 GROUP BY c WINDOW v5 AS (ORDER BY (SELECT * FROM t GROUP BY c WINDOW v6 AS (PARTITION BY c))))));

Leads to:

10.11.2 70be59913c90e93fe5136d6f6df03c4254aa515d (Debug)

Core was generated by `/test/MD070123-mariadb-10.11.2-linux-x86_64-dbg/bin/mysqld --no-defaults --core'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  JOIN::eval_select_list_used_tables (this=0x14b870035148)
    at /test/10.11_dbg/sql/sql_select.cc:13195
[Current thread is 1 (Thread 0x14b897147640 (LWP 2601256))]
(gdb) bt
#0  JOIN::eval_select_list_used_tables (this=0x14b870035148) at /test/10.11_dbg/sql/sql_select.cc:13195
#1  JOIN::optimize_inner (this=this@entry=0x14b870035148) at /test/10.11_dbg/sql/sql_select.cc:2112
#2  0x00005636942443aa in JOIN::optimize (this=this@entry=0x14b870035148) at /test/10.11_dbg/sql/sql_select.cc:1870
#3  0x000056369419066a in st_select_lex::optimize_unflattened_subqueries (this=0x14b8700057c8, const_only=const_only@entry=true) at /test/10.11_dbg/sql/sql_lex.cc:4907
#4  0x000056369436b32b in JOIN::optimize_constant_subqueries (this=this@entry=0x14b870034800) at /test/10.11_dbg/sql/opt_subselect.cc:5689
#5  0x0000563694242f3e in JOIN::optimize_inner (this=this@entry=0x14b870034800) at /test/10.11_dbg/sql/sql_select.cc:2193
#6  0x00005636942443aa in JOIN::optimize (this=this@entry=0x14b870034800) at /test/10.11_dbg/sql/sql_select.cc:1870
#7  0x000056369424449a in mysql_select (thd=thd@entry=0x14b870000d58, tables=tables@entry=0x14b870013390, fields=@0x14b897145d10: {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x5636956a6f00 <end_of_list>, last = 0x14b897145d10, elements = 0}, <No data fields>}, conds=conds@entry=0x14b870013e18, og_num=1, order=0x14b87002adb0, group=0x0, having=0x0, proc_param=0x0, select_options=37383395344512, result=0x14b8700346f0, unit=0x14b870004f98, select_lex=0x14b8700057c8) at /test/10.11_dbg/sql/sql_select.cc:5066
#8  0x00005636942b980b in mysql_multi_update (thd=thd@entry=0x14b870000d58, table_list=0x14b870013390, fields=fields@entry=0x14b870005a68, values=values@entry=0x14b870005ea0, conds=0x14b870013e18, options=0, handle_duplicates=DUP_ERROR, ignore=false, unit=0x14b870004f98, select_lex=0x14b8700057c8, result=0x14b897145f00) at /test/10.11_dbg/sql/sql_update.cc:1980
#9  0x00005636941bcfa8 in mysql_execute_command (thd=thd@entry=0x14b870000d58, is_called_from_prepared_stmt=is_called_from_prepared_stmt@entry=false) at /test/10.11_dbg/sql/sql_parse.cc:4491
#10 0x00005636941c2c7a in mysql_parse (thd=thd@entry=0x14b870000d58, rawbuf=<optimized out>, length=<optimized out>, parser_state=parser_state@entry=0x14b8971462c0) at /test/10.11_dbg/sql/sql_parse.cc:8000
#11 0x00005636941c4e0e in dispatch_command (command=command@entry=COM_QUERY, thd=thd@entry=0x14b870000d58, packet=packet@entry=0x14b87000ae09 "UPDATE t SET c=1 WHERE c='' ORDER BY 1=(1 IN ((SELECT * FROM (SELECT * FROM t) AS v3 NATURAL JOIN t AS v2 NATURAL JOIN t AS v4 GROUP BY c WINDOW v5 AS (ORDER BY (SELECT * FROM t GROUP BY c WINDOW v6 A"..., packet_length=packet_length@entry=223, blocking=blocking@entry=true) at /test/10.11_dbg/sql/sql_class.h:243
#12 0x00005636941c6c67 in do_command (thd=0x14b870000d58, blocking=blocking@entry=true) at /test/10.11_dbg/sql/sql_parse.cc:1407
#13 0x0000563694310d30 in do_handle_one_connection (connect=<optimized out>, connect@entry=0x563697323838, put_in_cache=put_in_cache@entry=true) at /test/10.11_dbg/sql/sql_connect.cc:1416
#14 0x0000563694310f8f in handle_one_connection (arg=0x563697323838) at /test/10.11_dbg/sql/sql_connect.cc:1318
#15 0x000014b8c7a62b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#16 0x000014b8c7af4a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

10.11.2 70be59913c90e93fe5136d6f6df03c4254aa515d (Optimized, UBASAN)

==2670205==ERROR: AddressSanitizer: use-after-poison on address 0x6290000888c0 at pc 0x5622357f717c bp 0x147d5641d9f0 sp 0x147d5641d9e0
READ of size 8 at 0x6290000888c0 thread T13
    #0 0x5622357f717b in base_list_iterator::next_fast() /test/10.11_opt_san/sql/sql_list.h:443
    #1 0x5622357f717b in List_iterator_fast<Item>::operator++(int) /test/10.11_opt_san/sql/sql_list.h:620
    #2 0x5622357f717b in JOIN::eval_select_list_used_tables() /test/10.11_opt_san/sql/sql_select.cc:13193
    #3 0x5622357f717b in JOIN::optimize_inner() /test/10.11_opt_san/sql/sql_select.cc:2112
    #4 0x5622357fb2a0 in JOIN::optimize() /test/10.11_opt_san/sql/sql_select.cc:1870
    #5 0x562235291342 in st_select_lex::optimize_unflattened_subqueries(bool) /test/10.11_opt_san/sql/sql_lex.cc:4907
    #6 0x562235f675e7 in JOIN::optimize_constant_subqueries() /test/10.11_opt_san/sql/opt_subselect.cc:5689
    #7 0x5622357f01a0 in JOIN::optimize_inner() /test/10.11_opt_san/sql/sql_select.cc:2193
    #8 0x5622357fb2a0 in JOIN::optimize() /test/10.11_opt_san/sql/sql_select.cc:1870
    #9 0x5622357fb936 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*) /test/10.11_opt_san/sql/sql_select.cc:5066
    #10 0x562235b16be7 in mysql_multi_update(THD*, TABLE_LIST*, List<Item>*, List<Item>*, Item*, unsigned long long, enum_duplicates, bool, st_select_lex_unit*, st_select_lex*, multi_update**) /test/10.11_opt_san/sql/sql_update.cc:1980
    #11 0x562235404878 in mysql_execute_command(THD*, bool) /test/10.11_opt_san/sql/sql_parse.cc:4491
    #12 0x562235420d82 in mysql_parse(THD*, char*, unsigned int, Parser_state*) /test/10.11_opt_san/sql/sql_parse.cc:8000
    #13 0x56223542e7e5 in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool) /test/10.11_opt_san/sql/sql_parse.cc:1894
    #14 0x562235437f40 in do_command(THD*, bool) /test/10.11_opt_san/sql/sql_parse.cc:1407
    #15 0x562235d1471c in do_handle_one_connection(CONNECT*, bool) /test/10.11_opt_san/sql/sql_connect.cc:1416
    #16 0x562235d16d1c in handle_one_connection /test/10.11_opt_san/sql/sql_connect.cc:1318
    #17 0x147d78315b42 in start_thread nptl/pthread_create.c:442
    #18 0x147d783a79ff  (/lib/x86_64-linux-gnu/libc.so.6+0x1269ff)
 
0x6290000888c0 is located 5824 bytes inside of 16400-byte region [0x629000087200,0x62900008b210)
allocated by thread T13 here:
    #0 0x562234b4c887 in malloc (/test/UBASAN_MD070123-mariadb-10.11.2-linux-x86_64-opt/bin/mariadbd+0x7a20887)
    #1 0x562238f71d14 in my_malloc /test/10.11_opt_san/mysys/my_malloc.c:90
    #2 0x562238f4ce2b in root_alloc /test/10.11_opt_san/mysys/my_alloc.c:66
    #3 0x562238f4ce2b in reset_root_defaults /test/10.11_opt_san/mysys/my_alloc.c:243
    #4 0x56223508d24e in THD::init_for_queries() /test/10.11_opt_san/sql/sql_class.cc:1387
    #5 0x562235d0f24e in prepare_new_connection_state(THD*) /test/10.11_opt_san/sql/sql_connect.cc:1245
    #6 0x562235d10b37 in thd_prepare_connection(THD*) /test/10.11_opt_san/sql/sql_connect.cc:1339
    #7 0x562235d10b37 in thd_prepare_connection(THD*) /test/10.11_opt_san/sql/sql_connect.cc:1328
    #8 0x562235d137b7 in do_handle_one_connection(CONNECT*, bool) /test/10.11_opt_san/sql/sql_connect.cc:1406
    #9 0x562235d16d1c in handle_one_connection /test/10.11_opt_san/sql/sql_connect.cc:1318
    #10 0x147d78315b42 in start_thread nptl/pthread_create.c:442
 
Thread T13 created by T0 here:
    #0 0x562234af06c5 in pthread_create (/test/UBASAN_MD070123-mariadb-10.11.2-linux-x86_64-opt/bin/mariadbd+0x79c46c5)
    #1 0x562234ba4cfe in create_thread_to_handle_connection(CONNECT*) /test/10.11_opt_san/sql/mysqld.cc:6102
    #2 0x562234bb712f in handle_accepted_socket(st_mysql_socket, st_mysql_socket) /test/10.11_opt_san/sql/mysqld.cc:6223
    #3 0x562234bb80b7 in handle_connections_sockets() /test/10.11_opt_san/sql/mysqld.cc:6347
    #4 0x562234bbb11d in mysqld_main(int, char**) /test/10.11_opt_san/sql/mysqld.cc:5997
    #5 0x147d782aad8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
 
SUMMARY: AddressSanitizer: use-after-poison /test/10.11_opt_san/sql/sql_list.h:443 in base_list_iterator::next_fast()
Shadow bytes around the buggy address:
  0x0c52800090c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c52800090d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c52800090e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f7
  0x0c52800090f0: 00 00 f7 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c5280009110: 00 00 00 00 00 00 f7 f7[f7]f7 00 00 00 00 00 00
  0x0c5280009120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c5280009160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
==2670205==ABORTING

Bug confirmed present in:
MariaDB: 10.4.28 (dbg), 10.5.19 (dbg), 10.6.12 (dbg), 10.7.8 (dbg), 10.8.7 (dbg), 10.9.5 (dbg), 10.10.3 (dbg), 10.11.2 (dbg)

Bug (or feature/syntax) confirmed not present in:
MariaDB: 10.3.38 (dbg), 10.3.38 (opt), 10.4.28 (opt), 10.5.19 (opt), 10.6.12 (opt), 10.7.8 (opt), 10.8.7 (opt), 10.9.5 (opt), 10.10.3 (opt), 10.11.2 (opt)
MySQL: 5.5.62 (dbg), 5.5.62 (opt), 5.6.51 (dbg), 5.6.51 (opt), 5.7.40 (dbg), 5.7.40 (opt), 8.0.31 (dbg), 8.0.31 (opt)

Comment by Rex Johnston [ 2023-01-09 ]

Hi Sergei. I returned to this after Roel added new test cases.

Comment by Roel Van de Paar [ 2023-01-10 ]

I keep running into this bug in various ways. New UniqueID with this testcase

CREATE TABLE t (c INT);
SET SESSION optimizer_trace=1;
UPDATE t SET c=1 WHERE c IN (SELECT * FROM t AS c NATURAL JOIN t WINDOW c AS (PARTITION BY 1 BETWEEN (SELECT 1 GROUP BY c WINDOW c AS (PARTITION BY c)) AND 1));

SIGSEGV|st_select_lex::print|opt_trace_print_expanded_query|JOIN::prepare|subselect_single_select_engine::prepare

Please verify this testcase after a fix found, as it may touch on another bug also (given the optimizer_trace setting requirement)

Comment by Sergei Petrunia [ 2023-01-17 ]

Debugging the original testcase...

The code in remove_redundant_subquery_clauses() removes elements from
subq_select_lex->join->all_fields:

        
        /*
          Remove from the JOIN::all_fields list any reference to the elements
          of the eliminated GROUP BY list unless it is 'in_field_list'.
          This is needed in order not to confuse JOIN::make_aggr_tables_info()
          when it constructs different structure for execution phase.
	*/
        List_iterator<Item> li(subq_select_lex->join->all_fields);
	Item *item;
        while ((item= li++))
	{
          if (item == *ord->item)
	    li.remove();
	}

While doing that, it corrupts subq_select_lex->item_list: immediately after the li.remove() call above one, one gets:

(gdb) p subq_select_lex->item_list
$88 = {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x7fff78015598, last = 0x7fff78015598, elements = 1}, <No data fields>}
 
(gdb) p subq_select_lex->item_list->first[0]
$89 = {<Sql_alloc> = {<No data fields>}, next = 0x8f8f8f8f8f8f8f8f, info = 0x8f8f8f8f8f8f8f8f}

I'm not sure how this happens if these two are separate lists?

Comment by Sergei Petrunia [ 2023-01-17 ]

... they are not. item_list is the suffix of all_fields. Note the $PTR here:

(gdb) p subq_select_lex->join->all_fields
$98 = {<base_list> = {<Sql_alloc> = {<No data fields>}, first = 0x7fff6c02b610, last = $PTR, elements = 2}, <No data fields>}
(gdb) p subq_select_lex->item_list
$99 = {<base_list> = {<Sql_alloc> = {<No data fields>}, first = $PTR, last = $PTR, elements = 1}, <No data fields>}
(gdb) p subq_select_lex->join->all_fields.first[0]
$100 = {<Sql_alloc> = {<No data fields>}, next = $PTR, info = 0x7fff6c018988}

List contents:

(gdb) p subq_select_lex->join->all_fields.elem(0)
$102 = (Item_singlerow_subselect *) 0x7fff6c018988
(gdb) p subq_select_lex->join->all_fields.elem(1)
$103 = (Item_field *) 0x7fff6c02aa30

Comment by Sergei Petrunia [ 2023-01-17 ]

Take-away from the optimizer call: the observed "list sharing" is known and it was like that for a very long time.

Comment by Rex Johnston [ 2023-01-18 ]

PR updated after information from sanja details here
https://github.com/MariaDB/server/commit/da51a117e1271b2ac821571a45849ffa04d8be2a#diff-16fd8e507582e428d933bc7ee3984394eab544410785220b536ab964d4e9e084

Comment by Sergei Petrunia [ 2023-01-18 ]

Note: MDEV-29359 is fixed by the fix for this bug. MDEV-28515 (linked to MDEV-29359) is not fixed by fix for this bug.

Comment by Sergei Petrunia [ 2023-01-18 ]

Johnston, the patch technically works, but I see some flaws:

  • why put the test in insert_select.test ? The failure is caused by the code which handles IN subqueries... I don't see how the INSERT part is necessary...
  • There is a more efficient way to check if we've reached the part of the list which we should not touch...
  • Some of the "discoveries" made while investigating this deserve code comments, I think...
Comment by Sergei Petrunia [ 2023-01-18 ]

Addressed all of the above in: https://github.com/MariaDB/server/commit/436abf9253d4f54e8a2c7400c33d65790b9e620c
sanja, could you please review?

Comment by Sergei Petrunia [ 2023-01-18 ]

sanja, one question that is still not clear to me: If the select list entry was NOT added by GROUP BY code, why does the ORDER structure have ord->in_field_list==false ? Is this correct? (It's hard for me to tell, because there's no exact definition of what in_field_list means).

And do we need the check for (!in_field_list) after this patch?

Comment by Igor Babaev [ 2023-01-18 ]

Let's execute the query:

UPDATE t SET c=1 WHERE c=2 ORDER BY (1 IN ((SELECT * FROM (SELECT * FROM t) AS v1 GROUP BY c WINDOW v2 AS (ORDER BY (SELECT * FROM t tt GROUP BY c WINDOW v3 AS (PARTITION BY c))))));

The cause of the problem can seen here:

(gdb) p thd->lex->all_selects_list
$122 = (SELECT_LEX *) 0x7fff90016990
(gdb) p dbug_print_select((SELECT_LEX *) 0x7fff90016990)
$123 = 0x555557581b40 <dbug_item_print_buf> "select `*` from test.t tt group by c"
(gdb) p ((SELECT_LEX *) 0x7fff90016990)->group_list
$124 = {<Sql_alloc> = {<No data fields>}, elements = 1, first = 0x7fff90017eb0, next = 0x7fff90017eb0}
(gdb) p ((SELECT_LEX *) 0x7fff90016990)->next_select_in_list()
$125 = (st_select_lex *) 0x7fff90014d38
(gdb) p dbug_print_select((st_select_lex *) 0x7fff90014d38)
$126 = 0x555557581b40 <dbug_item_print_buf> "select t.c AS c from test.t"
(gdb) p ((st_select_lex *) 0x7fff90014d38)->next_select_in_list()
$127 = (st_select_lex *) 0x7fff90014018
(gdb) p dbug_print_select((st_select_lex *) 0x7fff90014018)
$128 = 0x555557581b40 <dbug_item_print_buf> "select `*` from (select t.c AS c from test.t) v1 group by c"
(gdb) p ((st_select_lex *) 0x7fff90014018)->group_list
$129 = {<Sql_alloc> = {<No data fields>}, elements = 1, first = 0x7fff90017eb0, next = 0x7fff90017eb0}

Here we see that two different selects:

select `*` from (select t.c AS c from test.t) v1 group by c
select `*` from test.t tt group by c

share the same group_list.
We have this when starting to execute mysql_derived_prepare() for the derived table v1 called indirectly from Multiupdate_prelocking_strategy::handle_end()

Comment by Igor Babaev [ 2023-01-19 ]

Ok, this is a parser problem. Here's a fix:

 diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index bdc8b54..548272f 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -976,6 +976,7 @@ class st_select_lex: public st_select_lex_node
   */
   SQL_I_List<ORDER>       group_list;
   Group_list_ptrs        *group_list_ptrs;
+  SQL_I_List<ORDER>       save_group_list;
 
   List<Item>          item_list;  /* list of fields & expressions */
   List<Item>          pre_fix; /* above list before fix_fields */
@@ -1040,6 +1041,7 @@ class st_select_lex: public st_select_lex_node
   const char *type;               /* type of select for EXPLAIN          */
 
   SQL_I_List<ORDER> order_list;   /* ORDER clause */
+  SQL_I_List<ORDER> save_order_list;
   SQL_I_List<ORDER> gorder_list;
   Item *select_limit, *offset_limit;  /* LIMIT clause parameters */
 
@@ -3215,8 +3217,6 @@ struct LEX: public Query_tables_list
   }
 
 
-  SQL_I_List<ORDER> save_group_list;
-  SQL_I_List<ORDER> save_order_list;
   LEX_CSTRING *win_ref;
   Window_frame *win_frame;
   Window_frame_bound *frame_top_bound;
diff --git a/sql/sql_list.h b/sql/sql_list.h
index b112398..40848c2 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -53,7 +53,7 @@ class SQL_I_List :public Sql_alloc
   {
     elements= tmp.elements;
     first= tmp.first;
-    next= tmp.next;
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -53,7 +53,7 @@ class SQL_I_List :public Sql_alloc
   {
     elements= tmp.elements;
     first= tmp.first;
-    next= tmp.next;
+    next= elements ? tmp.next : &first;
     return *this;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 5e4c41f..cfe8c91 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -755,8 +755,6 @@ void LEX::start(THD *thd_arg)
   stmt_var_list.empty();
   proc_list.elements=0;
 
-  save_group_list.empty();
-  save_order_list.empty();
   win_ref= NULL;
   win_frame= NULL;
   frame_top_bound= NULL;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3102aa6..f360b24 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -8662,8 +8662,8 @@ TABLE_LIST *st_select_lex::convert_right_join()
 void st_select_lex::prepare_add_window_spec(THD *thd)
 {
   LEX *lex= thd->lex;
-  lex->save_group_list= group_list;
-  lex->save_order_list= order_list;
+  save_group_list= group_list;
+  save_order_list= order_list;
   lex->win_ref= NULL;
   lex->win_frame= NULL;
   lex->frame_top_bound= NULL;
@@ -8690,8 +8690,8 @@ bool st_select_lex::add_window_def(THD *thd,
                                                       win_part_list_ptr,
                                                       win_order_list_ptr,
                                                       win_frame);
-  group_list= thd->lex->save_group_list;
-  order_list= thd->lex->save_order_list;
+  group_list= save_group_list;
+  order_list= save_order_list;
   if (parsing_place != SELECT_LIST)
   {
     fields_in_window_functions+= win_part_list_ptr->elements +
@@ -8717,8 +8717,8 @@ bool st_select_lex::add_window_spec(THD *thd,
                                                          win_part_list_ptr,
                                                          win_order_list_ptr,
                                                          win_frame);
-  group_list= thd->lex->save_group_list;
-  order_list= thd->lex->save_order_list;
+  group_list= save_group_list;
+  order_list= save_order_list;
   if (parsing_place != SELECT_LIST)
   {
     fields_in_window_functions+= win_part_list_ptr->elements +

This fixes the problem.

Comment by Rex Johnston [ 2023-01-19 ]

Igor's patch passes all mtr tests, one interesting thing. Previously this

SELECT
id IN (SELECT id FROM t1
  WINDOW w AS (ORDER BY 
        (SELECT  1 FROM t1 WHERE EXISTS 
                ( SELECT id FROM t1 GROUP BY id  
                          WINDOW w2 AS (ORDER BY id)
)))) FROM t1

emitted a warning

Column 'id' in group statement is ambiguous

With the patch, it's gone.

Generated at Thu Feb 08 10:13:16 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.