Details
Description
sp_update_stmt_used_routines() passes thd->stmt_arena to the function sp_add_used_routine() that uses it to allocate an instance of the class Sroutine_hash_entry and places the pointer to the allocated memory into the list prelocking_ctx->sroutines_list
On finishing execution of SP/PS its memory root is marked as read only. In case the same SP/PS is run the second time it crashes on attempt to allocate a memory on the statement's mem_root marked as read_only. The stack trace where assertion hits is below:
Item_func_sp::val_str() [sql/item_func.h:3327]
|
Item_func_sp::execute() [sql/item_func.cc:6470]
|
Item_sp::execute() [sql/item.cc:2761]
|
Item_sp::execute_impl [sql/item.cc:2846]
|
sp_head::execute_function [sql/sp_head.cc:2016] <<=== (*)
|
sp_head::rcontext_create [sql/sp_head.cc:1763]
|
sp_head::rcontext_create [sql/sp_head.cc:1735]
|
sp_rcontext::create [sql/sp_rcontext.cc:110]
|
sql/sp_rcontext.cc:380 [sp_rcontext::init_var_items]
|
Table_ident::resolve_table_rowtype_ref [sql/sp_rcontext.cc:308]
|
open_tables_only_view_structure [sql/sql_base.cc:5456]
|
open_normal_and_derived_tables [sql/sql_base.cc:5406]
|
open_tables [sql/sql_base.h:259]
|
open_tables [sql/sql_base.cc:4397]
|
open_and_process_table [sql/sql_base.cc:4008]
|
DML_prelocking_strategy::handle_view [sql/sql_base.cc:4966]
|
sp_update_stmt_used_routines [sql/sp.cc:2722]
|
sp_add_used_routine [sql/sp.cc:2298]
|
Query_arena::alloc [sql/sql_class.h:1121]
|
alloc_root [mysys/my_alloc.c:228]
|
Please draw your attention that before sp_head::rcontext_create() is called at the frame marked with in the above mentioned stack trace the active arena is switched to the call arena at line switched with (**)
if (!(*func_ctx)) |
{
|
thd->set_n_backup_active_arena(call_arena, &backup_arena); <<<== (**)
|
|
if (!(*func_ctx= rcontext_create(thd, return_value_fld, argp, argcount))) |
{
|
thd->restore_active_arena(call_arena, &backup_arena);
|
err_status= TRUE;
|
goto err_with_cleanup; |
}
|
|
/* |
We have to switch temporarily back to callers arena/memroot.
|
Function arguments belong to the caller and so the may reference
|
memory which they will allocate during calculation long after
|
this function call will be finished (e.g. in Item::cleanup()).
|
*/
|
thd->restore_active_arena(call_arena, &backup_arena);
|
}
|
To fix the issue the following modification is performed:
--- a/sql/sp.cc
|
+++ b/sql/sp.cc
|
@@ -2720,7 +2720,7 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
TABLE_LIST *belong_to_view)
|
{
|
for (Sroutine_hash_entry *rt= src->first; rt; rt= rt->next)
|
- (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
|
+ (void)sp_add_used_routine(prelocking_ctx, thd,
|
&rt->mdl_request.key, rt->m_handler,
|
belong_to_view);
|
} |
That is, it is proposed to make any allocation of a memory performed by the function sp_add_used_routine and its callees on current active memory root instead of using statement's memory root.
But doing this way the server hits another crash that happens on the second execution of the same SP/PS and that caused by referencing to a memory de-allocated on first time execution of SP/PS completes.
That happens inside the function reinit_stmt_before_use()
void reinit_stmt_before_use(THD *thd, LEX *lex) |
{
|
...
|
/* Reset MDL tickets for procedures/functions */ |
for (Sroutine_hash_entry *rt= |
(Sroutine_hash_entry*)thd->lex->sroutines_list.first;
|
rt; rt= rt->next)
|
rt->mdl_request.ticket= NULL; <<=== Access the memory referenced by rt that was released on finishing of first SP/PS invocation
|
...
|
}
|
Use case that forces the test main.sp to fail it the following one:
--echo #
|
--echo # MDEV-23902: MariaDB crash on calling function
|
--echo #
|
|
--delimiter |
|
CREATE FUNCTION f2 () RETURNS VARCHAR(1) |
BEGIN
|
DECLARE rec1 ROW TYPE OF v1; |
SELECT z INTO rec1 FROM v1; |
RETURN 1; |
END| |
--delimiter ;
|
|
CREATE FUNCTION f1 () RETURNS VARCHAR(1) RETURN f2() ; |
CREATE FUNCTION f3 () RETURNS VARCHAR(1) RETURN f_not_exist(); |
CREATE VIEW v1 AS SELECT f3() z; |
|
--error ER_VIEW_INVALID
|
SELECT f1(); |
|
--echo # Check that crash doen't happen in case f3 completes with success.
|
DROP FUNCTION f3; |
CREATE FUNCTION f3 () RETURNS VARCHAR(1) RETURN '!'; |
|
SELECT f1(); |
The following prerequisite patch must be applied to re-produce the issue described above:
--- a/sql/sql_view.cc
|
+++ b/sql/sql_view.cc
|
@@ -1686,9 +1686,10 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
For suid views prepare a security context for checking underlying
|
objects of the view.
|
*/
|
- if (!(table->view_sctx= (Security_context *)
|
- thd->stmt_arena->calloc(sizeof(Security_context))))
|
+ table->view_sctx= (Security_context *)alloc_root(thd->mem_root,sizeof(Security_context));
|
+ if (!table->view_sctx)
|
goto err;
|
+ bzero(table->view_sctx, sizeof(Security_context));
|
security_ctx= table->view_sctx;
|
} |
Attachments
Issue Links
- blocks
-
MDEV-30073 Wrong result on 2nd execution of PS for query with NOT EXISTS
- Stalled
- is part of
-
MDEV-14959 Control over memory allocated for SP/PS
- Closed