From e4257b9f9bdd1468d4f8715653680480a31a5875 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 6 May 2026 18:40:03 +1000 Subject: [PATCH] MDEV-30518: JSON functions cannot be killed on big-endian The json_engine killed_ptr was of type uchar however the enum in the server is dependant on the architecture. On big-endian architectures like IBM Z, retreiving a uchar* retreived the unmodifed part of the THD->kill enum location and was always 0. As C++11 allows enums to inherit a type, used uint32_t as the base class of killed_state type in the server and used uint32_t in the json library. reload_acl_and_cache required an expression change to avoid the compile error: sql/sql_reload.cc:472:24: error: enumerated and non-enumerated type in conditional expression [-Werror=enum-conversion] 472 | return result || (thd ? thd->killed : 0); Added the kill_ptr assignment the following functions can be killed: * Item_func_json_depth::val_int * Item_func_json_type::val_str * Item_func_json_length::val_int Item_func_json_array_append::val_str, check_killed() only applied to json_error: label and not return_null. Item_func_json_overlaps::val_bool ensure check_killed() is called. Item_func_json_format::val_str(), corresponding to the SQL, json_compact, json_detailed, json_loose - add debug instrumentation for the func_json_notembedded test. MDEV-26726 (fcd345de485f) added a json_pause_execution sync point however the test case used debug_max_statement_time, which doesn't exist. Fix the sync point name in the test. --- include/json_lib.h | 3 +- mysql-test/main/func_json_notembedded.result | 2 +- mysql-test/main/func_json_notembedded.test | 2 +- sql/item_jsonfunc.cc | 42 ++++++++++++-------- sql/sql_class.h | 2 +- sql/sql_reload.cc | 2 +- strings/json_lib.c | 4 +- 7 files changed, 34 insertions(+), 23 deletions(-) diff --git a/include/json_lib.h b/include/json_lib.h index 7de7f2827518f..521a336c06cac 100644 --- a/include/json_lib.h +++ b/include/json_lib.h @@ -2,6 +2,7 @@ #define JSON_LIB_INCLUDED #include +#include #ifdef __cplusplus extern "C" { @@ -228,7 +229,7 @@ typedef struct st_json_engine_t int stack[JSON_DEPTH_LIMIT]; /* Keeps the stack of nested JSON structures. */ int stack_p; /* The 'stack' pointer. */ - volatile uchar *killed_ptr; + volatile uint32_t *killed_ptr; } json_engine_t; diff --git a/mysql-test/main/func_json_notembedded.result b/mysql-test/main/func_json_notembedded.result index ba4d38dd623f3..19267ef1e1686 100644 --- a/mysql-test/main/func_json_notembedded.result +++ b/mysql-test/main/func_json_notembedded.result @@ -11,7 +11,7 @@ length(@obj) length(@arr) 5000009 5000009 set max_statement_time=0.0001; SET @old_debug= @@debug_dbug; -SET debug_dbug='+d,debug_max_statement_time exceeded'; +SET debug_dbug='+d,json_pause_execution'; select json_array_append(@arr, '$[0]', 1); ERROR 70100: Query execution was interrupted (max_statement_time exceeded) select json_array_insert(@arr, '$[0]', 1); diff --git a/mysql-test/main/func_json_notembedded.test b/mysql-test/main/func_json_notembedded.test index 1e05571e16f83..f147190e53122 100644 --- a/mysql-test/main/func_json_notembedded.test +++ b/mysql-test/main/func_json_notembedded.test @@ -18,7 +18,7 @@ select length(@obj), length(@arr); set max_statement_time=0.0001; disable_abort_on_error; SET @old_debug= @@debug_dbug; -SET debug_dbug='+d,debug_max_statement_time exceeded'; +SET debug_dbug='+d,json_pause_execution'; select json_array_append(@arr, '$[0]', 1); select json_array_insert(@arr, '$[0]', 1); select json_insert(@obj, '$.meta', 1); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 7ee9933a0efde..bf10ee4dfa005 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -2119,7 +2119,7 @@ String *Item_func_json_array_append::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) (uchar*)&thd->killed; c_path->cur_step= c_path->p.steps; @@ -2200,7 +2200,7 @@ String *Item_func_json_array_append::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) &thd->killed; if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -2208,9 +2208,9 @@ String *Item_func_json_array_append::val_str(String *str) js_error: report_json_error(js, &je, 0); + thd->check_killed(); // to get the error message right return_null: - thd->check_killed(); // to get the error message right null_value= 1; return 0; } @@ -2263,7 +2263,7 @@ String *Item_func_json_array_insert::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) &thd->killed; c_path->cur_step= c_path->p.steps; @@ -2399,7 +2399,7 @@ String *Item_func_json_array_insert::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) (uchar*)&thd->killed; if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -2690,11 +2690,11 @@ String *Item_func_json_merge::val_str(String *str) json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); - je1.killed_ptr= (uchar*)&thd->killed; + je1.killed_ptr= (volatile uint32_t*) (uchar*)&thd->killed; json_scan_start(&je2, js2->charset(),(const uchar *) js2->ptr(), (const uchar *) js2->ptr() + js2->length()); - je2.killed_ptr= (uchar*)&thd->killed; + je2.killed_ptr= (volatile uint32_t*) (uchar*)&thd->killed; if (do_merge(str, &je1, &je2)) goto error_return; @@ -2716,7 +2716,8 @@ String *Item_func_json_merge::val_str(String *str) json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); - je1.killed_ptr= (uchar*)&thd->killed; + je1.killed_ptr= (volatile uint32_t*) (uchar*)&thd->killed; + if (json_nice(&je1, str, Item_func_json_format::LOOSE)) goto error_return; @@ -2823,6 +2824,7 @@ static int do_merge_patch(String *str, json_engine_t *je1, json_engine_t *je2, if (str->append('{')) return 3; + while (json_scan_next(je1) == 0 && je1->state != JST_OBJ_END) { @@ -3002,7 +3004,7 @@ String *Item_func_json_merge_patch::val_str(String *str) json_scan_start(&je2, js2->charset(),(const uchar *) js2->ptr(), (const uchar *) js2->ptr() + js2->length()); - je2.killed_ptr= (uchar*)&thd->killed; + je2.killed_ptr= (volatile uint32_t*) &thd->killed; if (merge_to_null) { @@ -3022,7 +3024,7 @@ String *Item_func_json_merge_patch::val_str(String *str) json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); - je1.killed_ptr= (uchar*)&thd->killed; + je1.killed_ptr= (volatile uint32_t*) &thd->killed; if (do_merge_patch(str, &je1, &je2, &empty_result)) goto error_return; @@ -3051,7 +3053,7 @@ String *Item_func_json_merge_patch::val_str(String *str) json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); - je1.killed_ptr= (uchar*)&thd->killed; + je1.killed_ptr= (volatile uint32_t*) &thd->killed; if (json_nice(&je1, str, Item_func_json_format::LOOSE)) goto error_return; @@ -3093,6 +3095,7 @@ longlong Item_func_json_length::val_int() json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (volatile uint32_t*) ¤t_thd->killed; if (arg_count > 1) { @@ -3158,6 +3161,7 @@ longlong Item_func_json_length::val_int() err_return: report_json_error(js, &je, 0); + current_thd->check_killed(); // to get the error message right null_return: null_value= 1; return 0; @@ -3177,6 +3181,7 @@ longlong Item_func_json_depth::val_int() json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (volatile uint32_t*) ¤t_thd->killed; do { @@ -3211,6 +3216,7 @@ longlong Item_func_json_depth::val_int() return depth; report_json_error(js, &je, 0); + current_thd->check_killed(); // to get the error message right null_value= 1; return 0; } @@ -3237,6 +3243,7 @@ String *Item_func_json_type::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (volatile uint32_t*) ¤t_thd->killed; if (json_read_value(&je)) goto error; @@ -3275,6 +3282,7 @@ String *Item_func_json_type::val_str(String *str) error: report_json_error(js, &je, 0); + current_thd->check_killed(); null_value= 1; return 0; } @@ -3355,7 +3363,7 @@ String *Item_func_json_insert::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) &thd->killed; if (c_path->p.last_step < c_path->p.steps) goto v_found; @@ -3545,7 +3553,7 @@ String *Item_func_json_insert::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) &thd->killed; if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -3626,7 +3634,7 @@ String *Item_func_json_remove::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) &thd->killed; c_path->cur_step= c_path->p.steps; @@ -3747,7 +3755,7 @@ String *Item_func_json_remove::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) &thd->killed; if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -4187,6 +4195,7 @@ String *Item_func_json_format::val_str(String *str) int tab_size= 4; THD *thd= current_thd; + JSON_DO_PAUSE_EXECUTION(thd, 0.0002); if ((null_value= args[0]->null_value)) return 0; @@ -4209,7 +4218,7 @@ String *Item_func_json_format::val_str(String *str) json_scan_start(&je, js->charset(), (const uchar *) js->ptr(), (const uchar *) js->ptr()+js->length()); - je.killed_ptr= (uchar*)&thd->killed; + je.killed_ptr= (volatile uint32_t*) &thd->killed; if (json_nice(&je, str, fmt, tab_size)) { @@ -4983,6 +4992,7 @@ bool Item_func_json_overlaps::val_bool() report_json_error(js, &je, 0); if (ve.s.error) report_json_error(val, &ve, 1); + current_thd->check_killed(); // to get the error message right return 0; } diff --git a/sql/sql_class.h b/sql/sql_class.h index aa78312c3b6a5..011995f99431b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -613,7 +613,7 @@ extern const LEX_CSTRING Diag_condition_item_names[]; These states are bit coded with HARD. For each state there must be a pair , and _HARD. */ -enum killed_state +enum killed_state : uint32_t { NOT_KILLED= 0, KILL_HARD_BIT= 1, /* Bit for HARD KILL */ diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index 12f12b14c95aa..74aaa4beba301 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -469,7 +469,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, /* If the query was killed then this function must fail. */ - return result || (thd ? thd->killed : 0); + return result || (thd ? thd->killed > NOT_KILLED : 0); } diff --git a/strings/json_lib.c b/strings/json_lib.c index bce0e264f32ab..16a7491e6d74a 100644 --- a/strings/json_lib.c +++ b/strings/json_lib.c @@ -807,13 +807,13 @@ static json_state_handler json_actions[NR_JSON_STATES][NR_C_CLASSES]= int json_scan_start(json_engine_t *je, CHARSET_INFO *i_cs, const uchar *str, const uchar *end) { - static const uchar no_time_to_die= 0; + static const uint32_t no_time_to_die= 0; json_string_setup(&je->s, i_cs, str, end); je->stack[0]= JST_DONE; je->stack_p= 0; je->state= JST_VALUE; - je->killed_ptr = (uchar*)&no_time_to_die; + je->killed_ptr= (uint32_t *) &no_time_to_die; return 0; }