Bug #120706 Crash in stored procedure: changing group_concat_max_len between calls reprepares EXISTS subquery and crashes in Item_su
Submitted: 16 Jun 21:12
Reporter: Chelluru Vidyadhar Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: Stored Routines Severity:S2 (Serious)
Version:8.4.10 OS:Any
Assigned to: CPU Architecture:Any

[16 Jun 21:12] Chelluru Vidyadhar
Description:
The server crashes with SIGSEGV in Item_sum::add_used_tables_for_aggr_func() when a stored procedure executes a cached statement that contains an aggregate inside an EXISTS subquery, after a session variable affecting that aggregate's prepare-time metadata (e.g. group_concat_max_len) is changed between calls.

The variable change forces a reprepare of the cached SP statement: 
* validate_lex_and_execute_core() calls free_lex() (releasing the old item arena, including the aggregate Item) and then re-parses the expression. 
* The new query block is parsed but never resolved — m_first_execution is set to true, m_saved_base_items is empty, leaf_tables is nullptr. 
* Despite that, sp_lex_instr::execute_expression() still invokes Query_block::restore_cmd_properties(), which recurses into the unresolved EXISTS subquery and calls update_used_tables() over a field list that still references the freed aggregate Item — a use-after-free, manifesting as a SIGSEGV when Item_sum::add_used_tables_for_aggr_func() dereferences base_query_block.

// Crash signature in mysql 8.4.10

2026-06-16T21:07:02Z UTC - mysqld got signal 11 ;
Signal SIGSEGV (Address not mapped to object) at address 0x320
Most likely, you have hit a bug, but this error can also be caused by malfunctioning hardware.
BuildID[sha1]=5dec4dc319fbee88f4663559ae92333a7e41273e
Thread pointer: 0x7f2da00128b0
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong...
stack_bottom = 7f2e003f1be0 thread_stack 0x100000
 #0 0x7f2e1843ebef <unknown> at sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0
 #1 0xd34b8e Query_block::all_tables_map() const at mysql-8.4.10/sql/sql_lex.h:1260
 #2 0xd34b8e Item_sum::add_used_tables_for_aggr_func() at mysql-8.4.10/sql/item_sum.cc:853
 #3 0xd34b8e Item_sum::update_used_tables() at mysql-8.4.10/sql/item_sum.cc:800
 #4 0xe6af52 Query_block::update_used_tables() at mysql-8.4.10/sql/sql_resolver.cc:922
 #5 0xe6aa88 Query_expression::restore_cmd_properties() at mysql-8.4.10/sql/sql_lex.cc:4032
 #6 0xe6ad5d Query_block::restore_cmd_properties() at mysql-8.4.10/sql/sql_lex.cc:4975
 #7 0xe6aa88 Query_expression::restore_cmd_properties() at mysql-8.4.10/sql/sql_lex.cc:4032
 #8 0xe67956 LEX::restore_cmd_properties() at mysql-8.4.10/sql/sql_lex.h:4509
 #9 0xe67956 sp_lex_instr::execute_expression(THD*, unsigned int*) at mysql-8.4.10/sql/sp_instr.cc:369
 #10 0xe6231c sp_lex_instr::reset_lex_and_exec_core(THD*, unsigned int*, bool) at mysql-8.4.10/sql/sp_instr.cc:463
 #11 0xe61c48 sp_lex_instr::validate_lex_and_execute_core(THD*, unsigned int*, bool) at mysql-8.4.10/sql/sp_instr.cc:767
 #12 0xe60200 sp_lex_instr::execute(THD*, unsigned int*) at mysql-8.4.10/sql/sp_instr.h:334
 #13 0xe60200 sp_head::execute(THD*, bool) at mysql-8.4.10/sql/sp_head.cc:2244
 #14 0x12e0128 sp_head::execute_procedure(THD*, mem_root_deque<Item*>*) at mysql-8.4.10/sql/sp_head.cc:3117
 #15 0x12dfb9e Sql_cmd_call::execute_inner(THD*) at mysql-8.4.10/sql/sql_call.cc:233
 #16 0xcd8a1d Sql_cmd_dml::execute(THD*) at mysql-8.4.10/sql/sql_select.cc:782
 #17 0xb7d920 mysql_execute_command(THD*, bool) at mysql-8.4.10/sql/sql_parse.cc:4739
 #18 0xd6ddb4 dispatch_sql_command(THD*, Parser_state*) at mysql-8.4.10/sql/sql_parse.cc:5406
 #19 0xd69f67 dispatch_command(THD*, COM_DATA const*, enum_server_command) at mysql-8.4.10/sql/sql_parse.cc:2136
 #20 0xd65939 do_command(THD*) at mysql-8.4.10/sql/sql_parse.cc:1465
 #21 0xd54450 handle_connection at mysql-8.4.10/sql/conn_handler/connection_handler_per_thread.cc:304
 #22 0x104bba1 pfs_spawn_thread at mysql-8.4.10/storage/perfschema/pfs.cc:3067
 #23 0x7f2e1848a4a9 start_thread at /usr/src/debug/glibc-2.34-196.amzn2023.0.1.x86_64/nptl/pthread_create.c:443
 #24 0x7f2e1850f50f clone3 at sysdeps/unix/sysv/linux/x86_64/clone3.S:81
 #25 0xffffffffffffffff <unknown>

Trying to get some variables.
Some pointers may be invalid and cause the dump to abort.
Query (7f2da0148960): CALL bug_test.test_proc2
Connection ID (thread ID): 10
Status: NOT_KILLED

The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains
information that should help you find out what is causing the crash.

How to repeat:
CREATE DATABASE IF NOT EXISTS bug_test;
USE bug_test;

DROP TABLE IF EXISTS test_table;
CREATE TABLE test_table (
    id       INT UNSIGNED NOT NULL AUTO_INCREMENT,
    value    VARCHAR(20) DEFAULT NULL,
    modified INT UNSIGNED NOT NULL,
    status   INT UNSIGNED NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY (value)
) ENGINE=InnoDB
  CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

DROP PROCEDURE IF EXISTS bug_test.test_proc2;

DELIMITER $$
CREATE PROCEDURE bug_test.test_proc2()
BEGIN
    IF NOT EXISTS (
        SELECT GROUP_CONCAT(value) FROM test_table
    ) THEN
        SELECT 1;
    ELSE
        SELECT 2;
    END IF;
END$$
DELIMITER ;

SET SESSION group_concat_max_len = 1500;
CALL bug_test.test_proc2;     -- 1st call: prepares & caches the IF expression

SET SESSION group_concat_max_len = 16384;
CALL bug_test.test_proc2;     -- 2nd call: forces reprepare -> SIGSEGV

Result: client receives `ERROR 2013 (HY000): Lost connection to MySQL server during query` on the 2nd call; server restarts.

Suggested fix:
In `sql/sp_instr.cc`, function `sp_lex_instr::execute_expression()`, the guard around `restore_cmd_properties()` should also test `m_first_execution`. After a forced reparse the SP runtime sets `m_first_execution = true` to signal "treat as first execution"; the existing guard ignores that and runs restore on a never-resolved LEX, which dereferences a freed Item from the prior arena.

```cpp
- if (m_arena.get_state() != Query_arena::STMT_INITIALIZED_FOR_SP) {
+ if (!m_first_execution &&
+     m_arena.get_state() != Query_arena::STMT_INITIALIZED_FOR_SP) {
      m_lex->restore_cmd_properties();
      bind_fields(m_arena.item_list());
  }
```

Rationale: `m_first_execution == true` -> `exec_core()` has not run on this LEX -> the LEX has not been resolved/optimized -> nothing was ever saved by `save_cmd_properties()`. In that state, restore must be skipped and `exec_core()` must (re)resolve the statement from scratch which is exactly what already happens on a genuine first execution (the safe path).