MDEV-23902: MariaDB crash on calling function
On creation of a VIEW that depends on a stored routine an instance of the class Item_func_sp is allocated on a memory root of SP statement. It happens since mysql_make_view() calls the method THD::activate_stmt_arena_if_needed() before parsing definition of the view. On the other hand, when sp_head's rcontext is created an instance of the class Field referenced by the data member Item_func_sp::result_field is allocated on the Item_func_sp's Query_arena (call arena) that set up inside the method Item_sp::execute_impl just before calling the method sp_head::execute_function() On return from the method sp_head::execute_function() all items allocated on the Item_func_sp's Query_arena are released and its memory root is freed (see implementation of the method Item_sp::execute_impl). As a consequence, the pointer Item_func_sp::result_field references to the deallocated memory. Later, when the method sp_head::execute cleans up items allocated for just executed SP instruction the method Item_func_sp::cleanup is invoked and tries to delete an object referenced by data member Item_func_sp::result_field that points to already deallocated memory, that results in a server abnormal termination. To fix the issue the current active arena shouldn't be switched to a statement arena inside the function mysql_make_view() that invoked indirectly by the method sp_head::rcontext_create. It is implemented by introducing the new Query_arena's state STMT_SP_QUERY_ARGUMENTS that is set when explicit Query_arena is created for placing SP arguments and other caller's side items used during SP execution. Then the method THD::activate_stmt_arena_if_needed() checks Query_arena's state and returns immediately without switching to statement's arena.
This commit is contained in:
parent
8bbe3a3cd2
commit
68353dc92a
@ -8935,5 +8935,30 @@ CREATE TABLE t1 (a INT);
|
|||||||
CREATE PROCEDURE p1() SELECT 1 FROM t1 PROCEDURE ANALYSE( 10, (SELECT a FROM t1));
|
CREATE PROCEDURE p1() SELECT 1 FROM t1 PROCEDURE ANALYSE( 10, (SELECT a FROM t1));
|
||||||
ERROR 42000: PROCEDURE does not support subqueries or stored functions
|
ERROR 42000: PROCEDURE does not support subqueries or stored functions
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# MDEV-23902: MariaDB crash on calling function
|
||||||
|
#
|
||||||
|
CREATE FUNCTION f2 () RETURNS VARCHAR(1)
|
||||||
|
BEGIN
|
||||||
|
DECLARE rec1 ROW TYPE OF v1;
|
||||||
|
SELECT z INTO rec1 FROM v1;
|
||||||
|
RETURN 1;
|
||||||
|
END|
|
||||||
|
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;
|
||||||
|
SELECT f1();
|
||||||
|
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
|
||||||
|
# Check that crash doen't happen in case f3 completes with success.
|
||||||
|
DROP FUNCTION f3;
|
||||||
|
CREATE FUNCTION f3 () RETURNS VARCHAR(1) RETURN '!';
|
||||||
|
SELECT f1();
|
||||||
|
f1()
|
||||||
|
1
|
||||||
|
# Clean up
|
||||||
|
DROP FUNCTION f1;
|
||||||
|
DROP FUNCTION f2;
|
||||||
|
DROP FUNCTION f3;
|
||||||
|
DROP VIEW v1;
|
||||||
# End of 10.4 tests
|
# End of 10.4 tests
|
||||||
#
|
#
|
||||||
|
@ -10535,5 +10535,37 @@ CREATE PROCEDURE p1() SELECT 1 FROM t1 PROCEDURE ANALYSE( 10, (SELECT a FROM t1)
|
|||||||
|
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--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();
|
||||||
|
|
||||||
|
--echo # Clean up
|
||||||
|
DROP FUNCTION f1;
|
||||||
|
DROP FUNCTION f2;
|
||||||
|
DROP FUNCTION f3;
|
||||||
|
DROP VIEW v1;
|
||||||
|
|
||||||
--echo # End of 10.4 tests
|
--echo # End of 10.4 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
@ -2840,7 +2840,7 @@ Item_sp::execute_impl(THD *thd, Item **args, uint arg_count)
|
|||||||
{
|
{
|
||||||
init_sql_alloc(&sp_mem_root, "Item_sp", MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
|
init_sql_alloc(&sp_mem_root, "Item_sp", MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
|
||||||
*sp_query_arena= Query_arena(&sp_mem_root,
|
*sp_query_arena= Query_arena(&sp_mem_root,
|
||||||
Query_arena::STMT_INITIALIZED_FOR_SP);
|
Query_arena::STMT_SP_QUERY_ARGUMENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool err_status= m_sp->execute_function(thd, args, arg_count,
|
bool err_status= m_sp->execute_function(thd, args, arg_count,
|
||||||
|
@ -1070,11 +1070,21 @@ public:
|
|||||||
Prepared statement: STMT_INITIALIZED -> STMT_PREPARED -> STMT_EXECUTED.
|
Prepared statement: STMT_INITIALIZED -> STMT_PREPARED -> STMT_EXECUTED.
|
||||||
Stored procedure: STMT_INITIALIZED_FOR_SP -> STMT_EXECUTED.
|
Stored procedure: STMT_INITIALIZED_FOR_SP -> STMT_EXECUTED.
|
||||||
Other statements: STMT_CONVENTIONAL_EXECUTION never changes.
|
Other statements: STMT_CONVENTIONAL_EXECUTION never changes.
|
||||||
|
|
||||||
|
Special case for stored procedure arguments: STMT_SP_QUERY_ARGUMENTS
|
||||||
|
This state never changes and used for objects
|
||||||
|
whose lifetime is whole duration of function call
|
||||||
|
(sp_rcontext, it's tables and items. etc). Such objects
|
||||||
|
should be deallocated after every execution of a stored
|
||||||
|
routine. Caller's arena/memroot can't be used for
|
||||||
|
placing such objects since memory allocated on caller's
|
||||||
|
arena not freed until termination of user's session.
|
||||||
*/
|
*/
|
||||||
enum enum_state
|
enum enum_state
|
||||||
{
|
{
|
||||||
STMT_INITIALIZED= 0, STMT_INITIALIZED_FOR_SP= 1, STMT_PREPARED= 2,
|
STMT_INITIALIZED= 0, STMT_INITIALIZED_FOR_SP= 1, STMT_PREPARED= 2,
|
||||||
STMT_CONVENTIONAL_EXECUTION= 3, STMT_EXECUTED= 4, STMT_ERROR= -1
|
STMT_CONVENTIONAL_EXECUTION= 3, STMT_EXECUTED= 4,
|
||||||
|
STMT_SP_QUERY_ARGUMENTS= 5, STMT_ERROR= -1
|
||||||
};
|
};
|
||||||
|
|
||||||
enum_state state;
|
enum_state state;
|
||||||
@ -4046,6 +4056,17 @@ public:
|
|||||||
|
|
||||||
inline Query_arena *activate_stmt_arena_if_needed(Query_arena *backup)
|
inline Query_arena *activate_stmt_arena_if_needed(Query_arena *backup)
|
||||||
{
|
{
|
||||||
|
if (state == Query_arena::STMT_SP_QUERY_ARGUMENTS)
|
||||||
|
/*
|
||||||
|
Caller uses the arena with state STMT_SP_QUERY_ARGUMENTS for stored
|
||||||
|
routine's parameters. Lifetime of these objects spans a lifetime of
|
||||||
|
stored routine call and freed every time the stored routine execution
|
||||||
|
has been completed. That is the reason why switching to statement's
|
||||||
|
arena is not performed for arguments, else we would observe increasing
|
||||||
|
of memory usage while a stored routine be called over and over again.
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Use the persistent arena if we are in a prepared statement or a stored
|
Use the persistent arena if we are in a prepared statement or a stored
|
||||||
procedure statement and we have not already changed to use this arena.
|
procedure statement and we have not already changed to use this arena.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user