Skip to content

Commit 7e4fef6

Browse files
committed
Bug#29363867: LOST CONNECTION TO MYSQL SERVER DURING QUERY
Note: this patch is for 5.7. This is based on original patch and comments of g.manasa@oracle.com Description: Server exits when a prepared statement is executed after uninstalling a plugin. Analysis: As per the current design, there is no error checking or locking for plugin variables used in EXECUTE statement. When EXECUTE command is run after uninstalling the plugin, server exits with a segmentation fault since the plugin variable is freed. Even for concurrent EXECUTE and UNINSTALL, the server crashes with a segmentation fault. Fix: All plugin variables are rebound in prepared statements before accessing their data. Change-Id: I0db3702f579ba470b53a5b21802c6435f11b23f6
1 parent 2266e30 commit 7e4fef6

File tree

10 files changed

+159
-35
lines changed

10 files changed

+159
-35
lines changed

sql/item_func.cc

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7253,20 +7253,15 @@ Item_func_get_system_var::
72537253
Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg,
72547254
LEX_STRING *component_arg, const char *name_arg,
72557255
size_t name_len_arg)
7256-
:var(var_arg), var_type(var_type_arg), orig_var_type(var_type_arg),
7257-
component(*component_arg), cache_present(0)
7256+
:var(NULL), var_type(var_type_arg), orig_var_type(var_type_arg),
7257+
component(*component_arg), cache_present(0),
7258+
var_tracker(var_arg)
72587259
{
72597260
/* copy() will allocate the name */
72607261
item_name.copy(name_arg, (uint) name_len_arg);
72617262
}
72627263

72637264

7264-
bool Item_func_get_system_var::is_written_to_binlog()
7265-
{
7266-
return var->is_written_to_binlog(var_type);
7267-
}
7268-
7269-
72707265
void Item_func_get_system_var::update_null_value()
72717266
{
72727267
THD *thd= current_thd;
@@ -7283,6 +7278,15 @@ void Item_func_get_system_var::fix_length_and_dec()
72837278
maybe_null= TRUE;
72847279
max_length= 0;
72857280

7281+
THD *const thd= current_thd;
7282+
7283+
DEBUG_SYNC(current_thd, "after_error_checking");
7284+
7285+
assert(var == NULL);
7286+
var= var_tracker.bind_system_variable(thd);
7287+
if (var == NULL)
7288+
return;
7289+
72867290
if (!var->check_scope(var_type))
72877291
{
72887292
if (var_type != OPT_DEFAULT)
@@ -7316,8 +7320,8 @@ void Item_func_get_system_var::fix_length_and_dec()
73167320
case SHOW_CHAR_PTR:
73177321
mysql_mutex_lock(&LOCK_global_system_variables);
73187322
cptr= var->show_type() == SHOW_CHAR ?
7319-
(char*) var->value_ptr(current_thd, var_type, &component) :
7320-
*(char**) var->value_ptr(current_thd, var_type, &component);
7323+
(char*) var->value_ptr(thd, var_type, &component) :
7324+
*(char**) var->value_ptr(thd, var_type, &component);
73217325
if (cptr)
73227326
max_length= system_charset_info->cset->numchars(system_charset_info,
73237327
cptr,
@@ -7330,7 +7334,7 @@ void Item_func_get_system_var::fix_length_and_dec()
73307334
case SHOW_LEX_STRING:
73317335
{
73327336
mysql_mutex_lock(&LOCK_global_system_variables);
7333-
LEX_STRING *ls= ((LEX_STRING*)var->value_ptr(current_thd, var_type, &component));
7337+
LEX_STRING *ls= ((LEX_STRING*)var->value_ptr(thd, var_type, &component));
73347338
max_length= system_charset_info->cset->numchars(system_charset_info,
73357339
ls->str,
73367340
ls->str + ls->length);
@@ -7438,6 +7442,8 @@ longlong Item_func_get_system_var::val_int()
74387442
{
74397443
THD *thd= current_thd;
74407444

7445+
assert(var != NULL);
7446+
74417447
if (cache_present && thd->query_id == used_query_id)
74427448
{
74437449
if (cache_present & GET_SYS_VAR_CACHE_LONG)
@@ -7517,6 +7523,8 @@ String* Item_func_get_system_var::val_str(String* str)
75177523
{
75187524
THD *thd= current_thd;
75197525

7526+
assert(var != NULL);
7527+
75207528
if (cache_present && thd->query_id == used_query_id)
75217529
{
75227530
if (cache_present & GET_SYS_VAR_CACHE_STRING)
@@ -7604,6 +7612,8 @@ double Item_func_get_system_var::val_real()
76047612
{
76057613
THD *thd= current_thd;
76067614

7615+
assert(var != NULL);
7616+
76077617
if (cache_present && thd->query_id == used_query_id)
76087618
{
76097619
if (cache_present & GET_SYS_VAR_CACHE_DOUBLE)
@@ -7697,7 +7707,7 @@ bool Item_func_get_system_var::eq(const Item *item, bool binary_cmp) const
76977707
((Item_func*) item)->functype() != functype())
76987708
return 0;
76997709
Item_func_get_system_var *other=(Item_func_get_system_var*) item;
7700-
return (var == other->var && var_type == other->var_type);
7710+
return (var_tracker == other->var_tracker && var_type == other->var_type);
77017711
}
77027712

77037713

@@ -7707,6 +7717,7 @@ void Item_func_get_system_var::cleanup()
77077717
cache_present= 0;
77087718
var_type= orig_var_type;
77097719
cached_strval.mem_free();
7720+
var= NULL;
77107721
}
77117722

77127723

@@ -8241,6 +8252,9 @@ class Silence_deprecation_warnings : public Internal_error_handler
82418252
@param name Name of base or system variable
82428253
@param component Component.
82438254
8255+
@param unsafe If true and if the variable is written to a
8256+
binlog then mark the statement as unsafe.
8257+
82448258
@note
82458259
If component.str = 0 then the variable name is in 'name'
82468260
@@ -8252,7 +8266,7 @@ class Silence_deprecation_warnings : public Internal_error_handler
82528266

82538267
Item *get_system_var(Parse_context *pc,
82548268
enum_var_type var_type, LEX_STRING name,
8255-
LEX_STRING component)
8269+
LEX_STRING component, bool unsafe)
82568270
{
82578271
THD *thd= pc->thd;
82588272
sys_var *var;
@@ -8288,6 +8302,12 @@ Item *get_system_var(Parse_context *pc,
82888302
Item_func_get_system_var *item= new Item_func_get_system_var(var, var_type,
82898303
component_name,
82908304
NULL, 0);
8305+
if (item == NULL)
8306+
return NULL; // OOM
8307+
8308+
if (unsafe && !var->is_written_to_binlog(var_type))
8309+
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE);
8310+
82918311
#ifndef EMBEDDED_LIBRARY
82928312
if (var_type == OPT_GLOBAL && var->check_scope(OPT_GLOBAL))
82938313
{
@@ -8304,6 +8324,8 @@ Item *get_system_var(Parse_context *pc,
83048324
Silence_deprecation_warnings silencer;
83058325
thd->push_internal_handler(&silencer);
83068326

8327+
if (si)
8328+
(void) si->fix_length_and_dec();
83078329
outStr= si ? si->val_str(&str) : &str;
83088330

83098331
thd->pop_internal_handler();

sql/item_func.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,6 +2301,7 @@ class Item_func_get_system_var :public Item_var_func
23012301
my_bool cached_null_value;
23022302
query_id_t used_query_id;
23032303
uchar cache_present;
2304+
Sys_var_tracker var_tracker;
23042305

23052306
public:
23062307
Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg,
@@ -2321,15 +2322,6 @@ class Item_func_get_system_var :public Item_var_func
23212322
{ return val_decimal_from_real(dec_buf); }
23222323
/* TODO: fix to support views */
23232324
const char *func_name() const { return "get_system_var"; }
2324-
/**
2325-
Indicates whether this system variable is written to the binlog or not.
2326-
2327-
Variables are written to the binlog as part of "status_vars" in
2328-
Query_log_event, as an Intvar_log_event, or a Rand_log_event.
2329-
2330-
@return true if the variable is written to the binlog, false otherwise.
2331-
*/
2332-
bool is_written_to_binlog();
23332325
bool eq(const Item *item, bool binary_cmp) const;
23342326

23352327
void cleanup();
@@ -2885,7 +2877,7 @@ class Item_func_version : public Item_static_string_func
28852877

28862878

28872879
Item *get_system_var(Parse_context *pc, enum_var_type var_type, LEX_STRING name,
2888-
LEX_STRING component);
2880+
LEX_STRING component, bool unsafe);
28892881
extern bool check_reserved_words(LEX_STRING *name);
28902882
extern enum_field_types agg_field_type(Item **items, uint nitems);
28912883
double my_double_round(double value, longlong dec, bool dec_unsigned,

sql/parse_tree_items.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ class PTI_variable_aux_3d : public Parse_tree_item
757757
error(pc, var_pos);
758758
return true;
759759
}
760-
if (!(*res= get_system_var(pc, var_type, var, component)))
760+
if (!(*res= get_system_var(pc, var_type, var, component, true)))
761761
return true;
762762
if (!my_strcasecmp(system_charset_info, var.str, "warning_count") ||
763763
!my_strcasecmp(system_charset_info, var.str, "error_count"))
@@ -770,9 +770,6 @@ class PTI_variable_aux_3d : public Parse_tree_item
770770
*/
771771
lex->keep_diagnostics= DA_KEEP_COUNTS;
772772
}
773-
if (!((Item_func_get_system_var*) *res)->is_written_to_binlog())
774-
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE);
775-
776773
return false;
777774
}
778775
};

sql/set_var.cc

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
/* variable declarations are in sys_vars.cc now !!! */
2424

25+
#include "debug_sync.h"
2526
#include "set_var.h"
2627

2728
#include "hash.h" // HASH
@@ -309,6 +310,41 @@ bool sys_var::set_default(THD *thd, set_var* var)
309310
DBUG_RETURN(ret);
310311
}
311312

313+
Sys_var_tracker::Sys_var_tracker(sys_var *var)
314+
: m_is_dynamic(var->cast_pluginvar() != NULL),
315+
m_name(m_is_dynamic ? current_thd->strmake(var->name) : var->name),
316+
m_var(m_is_dynamic ? NULL : var)
317+
{}
318+
319+
sys_var *Sys_var_tracker::bind_system_variable(THD *thd) {
320+
if (!m_is_dynamic || // (1)
321+
(m_var != NULL && // (2)
322+
thd->state == Query_arena::STMT_INITIALIZED_FOR_SP)) // (3)
323+
{
324+
/*
325+
Return a previous cached value of a system variable:
326+
327+
- if this is a static variable (1) then always return its cached value.
328+
329+
- if SP body evaluation is in the process (3), and if this is not
330+
a resolver phase (2): the resolver phase caches the value and the
331+
executor phase reuses it; this can work since SQL statements
332+
referencing SP calls don't release plugins acquired by those SP
333+
calls until the SPs removed from the server memory.
334+
*/
335+
return m_var;
336+
}
337+
338+
m_var= find_sys_var(thd, m_name.str, m_name.length);
339+
if (m_var == NULL)
340+
{
341+
my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), m_name.str);
342+
return NULL;
343+
}
344+
345+
return m_var;
346+
}
347+
312348
void sys_var::do_deprecated_warning(THD *thd)
313349
{
314350
if (deprecation_substitute != NULL)
@@ -679,6 +715,11 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list)
679715
}
680716

681717
err:
718+
it.rewind();
719+
while ((var= it++))
720+
{
721+
var->cleanup();
722+
}
682723
free_underlaid_joins(thd, thd->lex->select_lex);
683724
DBUG_RETURN(error);
684725
}
@@ -709,7 +750,7 @@ bool keyring_access_test()
709750

710751
set_var::set_var(enum_var_type type_arg, sys_var *var_arg,
711752
const LEX_STRING *base_name_arg, Item *value_arg)
712-
:var(var_arg), type(type_arg), base(*base_name_arg)
753+
:var(NULL), type(type_arg), base(*base_name_arg), var_tracker(var_arg)
713754
{
714755
/*
715756
If the set value is a field, change it to a string to allow things like
@@ -750,6 +791,15 @@ set_var::set_var(enum_var_type type_arg, sys_var *var_arg,
750791
int set_var::check(THD *thd)
751792
{
752793
DBUG_ENTER("set_var::check");
794+
DEBUG_SYNC(current_thd, "after_error_checking");
795+
796+
assert(var == NULL);
797+
var= var_tracker.bind_system_variable(thd);
798+
if (var == NULL)
799+
{
800+
DBUG_RETURN(-1);
801+
}
802+
753803
var->do_deprecated_warning(thd);
754804
if (var->is_readonly())
755805
{
@@ -806,6 +856,12 @@ int set_var::check(THD *thd)
806856
*/
807857
int set_var::light_check(THD *thd)
808858
{
859+
var= var_tracker.bind_system_variable(thd);
860+
if (var == NULL)
861+
{
862+
return 1;
863+
}
864+
809865
if (!var->check_scope(type))
810866
{
811867
int err= type == OPT_GLOBAL ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE;
@@ -835,6 +891,8 @@ int set_var::light_check(THD *thd)
835891
*/
836892
int set_var::update(THD *thd)
837893
{
894+
assert(var != NULL);
895+
838896
return value ? var->update(thd, this) : var->set_default(thd, this);
839897
}
840898

sql/set_var.h

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,16 @@ class sys_var
173173
that support the syntax @@keycache_name.variable_name
174174
*/
175175
bool is_struct() { return option.var_type & GET_ASK_ADDR; }
176+
/**
177+
Indicates whether this system variable is written to the binlog or not.
178+
179+
Variables are written to the binlog as part of "status_vars" in
180+
Query_log_event, as an Intvar_log_event, or a Rand_log_event.
181+
182+
@param type Scope of the system variable.
183+
184+
@return true if the variable is written to the binlog, false otherwise.
185+
*/
176186
bool is_written_to_binlog(enum_var_type type)
177187
{ return type != OPT_GLOBAL && binlog_status == SESSION_VARIABLE_IN_BINLOG; }
178188
virtual bool check_update_type(Item_result type) = 0;
@@ -231,6 +241,26 @@ class sys_var
231241
uchar *global_var_ptr();
232242
};
233243

244+
class Sys_var_tracker
245+
{
246+
public:
247+
explicit Sys_var_tracker(sys_var *var);
248+
249+
sys_var *bind_system_variable(THD *thd);
250+
251+
bool operator==(const Sys_var_tracker &x) const {
252+
return m_var && m_var == x.m_var;
253+
}
254+
255+
bool is_sys_var(sys_var *x) const { return m_var == x; }
256+
257+
private:
258+
const bool m_is_dynamic; ///< true if dynamic variable
259+
const LEX_CSTRING m_name; ///< variable name
260+
261+
sys_var *m_var; ///< variable pointer
262+
};
263+
234264
/****************************************************************************
235265
Classes for parsing of the SET command
236266
****************************************************************************/
@@ -251,6 +281,7 @@ class set_var_base :public Sql_alloc
251281
virtual void print(THD *thd, String *str)=0; /* To self-print */
252282
/// @returns whether this variable is @@@@optimizer_trace.
253283
virtual bool is_var_optimizer_trace() const { return false; }
284+
virtual void cleanup() {}
254285
};
255286

256287

@@ -274,6 +305,10 @@ class set_var :public set_var_base
274305
} save_result;
275306
LEX_STRING base; /**< for structured variables, like keycache_name.variable_name */
276307

308+
private:
309+
Sys_var_tracker var_tracker;
310+
311+
public:
277312
set_var(enum_var_type type_arg, sys_var *var_arg,
278313
const LEX_STRING *base_name_arg, Item *value_arg);
279314

@@ -285,9 +320,10 @@ class set_var :public set_var_base
285320
virtual bool is_var_optimizer_trace() const
286321
{
287322
extern sys_var *Sys_optimizer_trace_ptr;
288-
return var == Sys_optimizer_trace_ptr;
323+
return var_tracker.is_sys_var(Sys_optimizer_trace_ptr);
289324
}
290325
#endif
326+
virtual void cleanup() { var= NULL; }
291327
};
292328

293329

sql/sql_class.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,13 @@ class Query_arena
792792
{ return strdup_root(mem_root,str); }
793793
inline char *strmake(const char *str, size_t size)
794794
{ return strmake_root(mem_root,str,size); }
795+
inline LEX_CSTRING strmake(LEX_CSTRING str)
796+
{
797+
LEX_CSTRING ret;
798+
ret.str= strmake(str.str, str.length);
799+
ret.length= ret.str ? str.length : 0;
800+
return ret;
801+
}
795802
inline void *memdup(const void *str, size_t size)
796803
{ return memdup_root(mem_root,str,size); }
797804

0 commit comments

Comments
 (0)