1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-37052: JSON_TABLE stack overflow handling errors

main.json_debug_nonembedded_noasan fails because of stack
overrun on Debug + MSAN testing.

Since MDEV-33209 (09ea2dc788)
the the stack overflow errors are just injected instead of
frailer mechanisms to consume stack. These mechanims where
not carried forward to the JSON_TABLE functions where
the pattern was the same.

Related MDEV-34099 (cf1c381bb8) makes check_stack_overrun never fail
under Address Sanitizer (only).

The previous ALLOCATE_MEM_ON_STACK did in MemorySanitizer consume
memory, but check_stack_overrun did fail because its 16000 byte
safety margin was exceeded. The allocation of the 448 byte error
ER_STACK_OVERRUN_NEED_MORE is well within these bounds, however
under the safemalloc implementation, "backtrace" library call is called,
which does further allocation for every stack frame. This exceeds the stack.

Fixes:

JSON_TABLE functions that trigger on out of memory debug instrumentation
replaced with the mechanism from MDEV-33209.

The get_disallowed_table_deps_for_list in a non-Debug build returned
incorrectly 1, instead of -1 indicating the out of memory condition.

In json_table add_extra_deps never passed the out of memory error
condition to the caller and would continue to run in a loop, potentially
recursively under these near out of stack conditions.

The Memory, Undefined Behaviour, Address and Thread sanitizers provide
sufficient instrumentation and a backtrace so the safemalloc
functionality provides insufficent value with these. As such is
disabled under WITH_SAFEMALLOC=AUTO.

With all of thse corrected the main.json_debug_nonembedded_noasan no
longer needs its ASAN exclusion.

The JSON_TABLE tests in this test case was dropped in a merge from 10.6
so these tests are re-added.
This commit is contained in:
Daniel Black
2025-06-23 12:13:53 +10:00
parent 7c807e1075
commit e79aa9ca38
5 changed files with 36 additions and 40 deletions

View File

@@ -27,23 +27,11 @@
#include "create_tmp_table.h"
#include "sql_parse.h"
#define HA_ERR_JSON_TABLE (HA_ERR_LAST+1)
#ifndef DBUG_OFF
int dbug_json_check_min_stack_requirement();
#endif
/*
Allocating memory and *also* using it (reading and
writing from it) because some build instructions cause
compiler to optimize out stack_used_up. Since alloca()
here depends on stack_used_up, it doesnt get executed
correctly and causes json_debug_nonembedded to fail
( --error ER_STACK_OVERRUN_NEED_MORE does not occur).
*/
#define ALLOCATE_MEM_ON_STACK(A) do \
{ \
uchar *array= (uchar*)alloca(A); \
array[0]= 1; \
array[0]++; \
array[0] ? array[0]++ : array[0]--; \
} while(0)
#define HA_ERR_JSON_TABLE (HA_ERR_LAST+1)
class table_function_handlerton
{
@@ -119,13 +107,9 @@ int get_disallowed_table_deps_for_list(MEM_ROOT *mem_root,
List_iterator<TABLE_LIST> li(*join_list);
DBUG_EXECUTE_IF("json_check_min_stack_requirement",
{
long arbitrary_var;
long stack_used_up= (available_stack_size(current_thd->thread_stack, &arbitrary_var));
ALLOCATE_MEM_ON_STACK(my_thread_stack_size-stack_used_up-STACK_MIN_SIZE);
});
return -dbug_json_check_min_stack_requirement(););
if (check_stack_overrun(current_thd, STACK_MIN_SIZE , NULL))
return 1;
return -1;
while ((table= li++))
{
@@ -1351,21 +1335,20 @@ void Table_function_json_table::fix_after_pullout(TABLE_LIST *sql_table,
/*
@brief
Recursively make all tables in the join_list also depend on deps.
@return - boolean - true if error (out of memory).
*/
static void add_extra_deps(List<TABLE_LIST> *join_list, table_map deps)
static bool add_extra_deps(List<TABLE_LIST> *join_list, table_map deps)
{
TABLE_LIST *table;
List_iterator<TABLE_LIST> li(*join_list);
DBUG_EXECUTE_IF("json_check_min_stack_requirement",
{
long arbitrary_var;
long stack_used_up= (available_stack_size(current_thd->thread_stack, &arbitrary_var));
ALLOCATE_MEM_ON_STACK(my_thread_stack_size-stack_used_up-STACK_MIN_SIZE);
});
dbug_json_check_min_stack_requirement(); return true;);
if (check_stack_overrun(current_thd, STACK_MIN_SIZE , NULL))
return;
return true;
while ((table= li++))
{
table->dep_tables |= deps;
@@ -1373,9 +1356,11 @@ static void add_extra_deps(List<TABLE_LIST> *join_list, table_map deps)
if ((nested_join= table->nested_join))
{
// set the deps inside, too
add_extra_deps(&nested_join->join_list, deps);
if (add_extra_deps(&nested_join->join_list, deps))
return true;
}
}
return false;
}
@@ -1444,7 +1429,8 @@ static void add_extra_deps(List<TABLE_LIST> *join_list, table_map deps)
supply the JOIN's top-level table list.
@param nest_tables Bitmap of all tables in the join list.
@return Bitmap of all outside references that tables in join_list have
@return Bitmap of all outside references that tables in join_list have,
or 0 on out of stack error.
*/
table_map add_table_function_dependencies(List<TABLE_LIST> *join_list,
@@ -1455,11 +1441,7 @@ table_map add_table_function_dependencies(List<TABLE_LIST> *join_list,
List_iterator<TABLE_LIST> li(*join_list);
DBUG_EXECUTE_IF("json_check_min_stack_requirement",
{
long arbitrary_var;
long stack_used_up= (available_stack_size(current_thd->thread_stack, &arbitrary_var));
ALLOCATE_MEM_ON_STACK(my_thread_stack_size-stack_used_up-STACK_MIN_SIZE);
});
if (dbug_json_check_min_stack_requirement()) return 0;);
if ((res=check_stack_overrun(current_thd, STACK_MIN_SIZE , NULL)))
return res;
@@ -1481,7 +1463,10 @@ table_map add_table_function_dependencies(List<TABLE_LIST> *join_list,
res= res & ~nest_tables & ~PSEUDO_TABLE_BITS;
// Then, make all "peers" have them:
if (res)
add_extra_deps(join_list, res);
{
if (add_extra_deps(join_list, res))
return 0;
}
return res;
}