Skip to content

Commit e79aa9c

Browse files
committed
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 (09ea2dc) 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 (cf1c381) 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.
1 parent 7c807e1 commit e79aa9c

File tree

5 files changed

+36
-40
lines changed

5 files changed

+36
-40
lines changed

CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,13 @@ SET(WITH_SAFEMALLOC "AUTO" CACHE STRING "Use safemalloc memory debugger. Will re
342342

343343
IF(WITH_SAFEMALLOC MATCHES "ON")
344344
ADD_DEFINITIONS( -DSAFEMALLOC)
345-
ELSEIF(WITH_SAFEMALLOC MATCHES "AUTO" AND NOT WIN32 AND NOT WITH_VALGRIND)
345+
ELSEIF(WITH_SAFEMALLOC MATCHES "AUTO"
346+
AND NOT WIN32
347+
AND NOT WITH_VALGRIND
348+
AND NOT WITH_ASAN
349+
AND NOT WITH_UBSAN
350+
AND NOT WITH_TSAN
351+
AND NOT WITH_MSAN)
346352
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC")
347353
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC")
348354
ENDIF()

mysql-test/main/json_debug_nonembedded_noasan.result renamed to mysql-test/main/json_debug_nonembedded.result

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#
44
SET @saved_dbug = @@debug_dbug;
55
SET debug_dbug='+d,json_check_min_stack_requirement';
6+
SELECT * from JSON_TABLE('[{"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}]', '$[*]' COLUMNS( a INT PATH '$.a')) as tt;
7+
ERROR HY000: Thread stack overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed. Consider increasing the thread_stack system variable.
68
SET @json1= '{"key1":"val1"}';
79
SET @json2= '{"key1":"val1"}';
810
SELECT JSON_OVERLAPS(@json1, @json2);

mysql-test/main/json_debug_nonembedded_noasan.test renamed to mysql-test/main/json_debug_nonembedded.test

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
-- source include/not_embedded.inc
22
--source include/have_debug.inc
3-
--source include/not_asan.inc
43

54
--echo #
65
--echo # MDEV-28762: recursive call of some json functions without stack control
@@ -9,6 +8,10 @@
98
SET @saved_dbug = @@debug_dbug;
109
SET debug_dbug='+d,json_check_min_stack_requirement';
1110

11+
--replace_regex /overrun: [0-9]* bytes used of a [0-9]* byte stack, and [0-9]* bytes needed/overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed/
12+
--error ER_STACK_OVERRUN_NEED_MORE
13+
SELECT * from JSON_TABLE('[{"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}]', '$[*]' COLUMNS( a INT PATH '$.a')) as tt;
14+
1215
SET @json1= '{"key1":"val1"}';
1316
SET @json2= '{"key1":"val1"}';
1417

sql/item_jsonfunc.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "sql_parse.h" // For check_stack_overrun
2222

2323
#ifndef DBUG_OFF
24-
static int dbug_json_check_min_stack_requirement()
24+
int dbug_json_check_min_stack_requirement()
2525
{
2626
my_error(ER_STACK_OVERRUN_NEED_MORE, MYF(ME_FATAL),
2727
my_thread_stack_size, my_thread_stack_size, STACK_MIN_SIZE);

sql/json_table.cc

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,11 @@
2727
#include "create_tmp_table.h"
2828
#include "sql_parse.h"
2929

30-
#define HA_ERR_JSON_TABLE (HA_ERR_LAST+1)
30+
#ifndef DBUG_OFF
31+
int dbug_json_check_min_stack_requirement();
32+
#endif
3133

32-
/*
33-
Allocating memory and *also* using it (reading and
34-
writing from it) because some build instructions cause
35-
compiler to optimize out stack_used_up. Since alloca()
36-
here depends on stack_used_up, it doesnt get executed
37-
correctly and causes json_debug_nonembedded to fail
38-
( --error ER_STACK_OVERRUN_NEED_MORE does not occur).
39-
*/
40-
#define ALLOCATE_MEM_ON_STACK(A) do \
41-
{ \
42-
uchar *array= (uchar*)alloca(A); \
43-
array[0]= 1; \
44-
array[0]++; \
45-
array[0] ? array[0]++ : array[0]--; \
46-
} while(0)
34+
#define HA_ERR_JSON_TABLE (HA_ERR_LAST+1)
4735

4836
class table_function_handlerton
4937
{
@@ -119,13 +107,9 @@ int get_disallowed_table_deps_for_list(MEM_ROOT *mem_root,
119107
List_iterator<TABLE_LIST> li(*join_list);
120108

121109
DBUG_EXECUTE_IF("json_check_min_stack_requirement",
122-
{
123-
long arbitrary_var;
124-
long stack_used_up= (available_stack_size(current_thd->thread_stack, &arbitrary_var));
125-
ALLOCATE_MEM_ON_STACK(my_thread_stack_size-stack_used_up-STACK_MIN_SIZE);
126-
});
110+
return -dbug_json_check_min_stack_requirement(););
127111
if (check_stack_overrun(current_thd, STACK_MIN_SIZE , NULL))
128-
return 1;
112+
return -1;
129113

130114
while ((table= li++))
131115
{
@@ -1351,31 +1335,32 @@ void Table_function_json_table::fix_after_pullout(TABLE_LIST *sql_table,
13511335
/*
13521336
@brief
13531337
Recursively make all tables in the join_list also depend on deps.
1338+
1339+
@return - boolean - true if error (out of memory).
13541340
*/
13551341

1356-
static void add_extra_deps(List<TABLE_LIST> *join_list, table_map deps)
1342+
static bool add_extra_deps(List<TABLE_LIST> *join_list, table_map deps)
13571343
{
13581344
TABLE_LIST *table;
13591345
List_iterator<TABLE_LIST> li(*join_list);
13601346

13611347
DBUG_EXECUTE_IF("json_check_min_stack_requirement",
1362-
{
1363-
long arbitrary_var;
1364-
long stack_used_up= (available_stack_size(current_thd->thread_stack, &arbitrary_var));
1365-
ALLOCATE_MEM_ON_STACK(my_thread_stack_size-stack_used_up-STACK_MIN_SIZE);
1366-
});
1348+
dbug_json_check_min_stack_requirement(); return true;);
13671349
if (check_stack_overrun(current_thd, STACK_MIN_SIZE , NULL))
1368-
return;
1350+
return true;
1351+
13691352
while ((table= li++))
13701353
{
13711354
table->dep_tables |= deps;
13721355
NESTED_JOIN *nested_join;
13731356
if ((nested_join= table->nested_join))
13741357
{
13751358
// set the deps inside, too
1376-
add_extra_deps(&nested_join->join_list, deps);
1359+
if (add_extra_deps(&nested_join->join_list, deps))
1360+
return true;
13771361
}
13781362
}
1363+
return false;
13791364
}
13801365

13811366

@@ -1444,7 +1429,8 @@ static void add_extra_deps(List<TABLE_LIST> *join_list, table_map deps)
14441429
supply the JOIN's top-level table list.
14451430
@param nest_tables Bitmap of all tables in the join list.
14461431
1447-
@return Bitmap of all outside references that tables in join_list have
1432+
@return Bitmap of all outside references that tables in join_list have,
1433+
or 0 on out of stack error.
14481434
*/
14491435

14501436
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,
14551441
List_iterator<TABLE_LIST> li(*join_list);
14561442

14571443
DBUG_EXECUTE_IF("json_check_min_stack_requirement",
1458-
{
1459-
long arbitrary_var;
1460-
long stack_used_up= (available_stack_size(current_thd->thread_stack, &arbitrary_var));
1461-
ALLOCATE_MEM_ON_STACK(my_thread_stack_size-stack_used_up-STACK_MIN_SIZE);
1462-
});
1444+
if (dbug_json_check_min_stack_requirement()) return 0;);
14631445
if ((res=check_stack_overrun(current_thd, STACK_MIN_SIZE , NULL)))
14641446
return res;
14651447

@@ -1481,7 +1463,10 @@ table_map add_table_function_dependencies(List<TABLE_LIST> *join_list,
14811463
res= res & ~nest_tables & ~PSEUDO_TABLE_BITS;
14821464
// Then, make all "peers" have them:
14831465
if (res)
1484-
add_extra_deps(join_list, res);
1466+
{
1467+
if (add_extra_deps(join_list, res))
1468+
return 0;
1469+
}
14851470

14861471
return res;
14871472
}

0 commit comments

Comments
 (0)