Skip to content

Commit 295bad1

Browse files
committed
Bug#27471510 PERFORMANCE_SCHEMA STATUS AND VARIABLE BY THREAD ARE NOT SAFE
Patch for 8.0. Before this fix, executing: SELECT * FROM performance_schema.status_by_thread SELECT * FROM performance_schema.variables_by_thread under heavy load was unsafe. Root cause 1 ============ SELECT * from performance_schema.status_by_thread inspects the status variables of running sessions. For sessions using SSL, SSL status variables are inspected. For example, the status variable "Ssl_cipher_list" is evaluated by executing function show_ssl_get_cipher_list() This function is implemented as follows: show_ssl_get_cipher_list() { if (thd->get_protocol()->get_ssl()) // (a) { ... SSL_get_cipher(thd->get_protocol()->get_ssl())); // (b) ... } } The problem is that evaluating thd->get_protocol()->get_ssl() to access the underlying SSL structure is unsafe, and subject to race conditions. The value returned in (a) can change by the time (b) is evaluated, for example when using prepared statements, because the thd->get_protocol() pointer will change during execution of PREPARE and EXECUTE. Fix 1 ===== thd->get_protocol()->get_ssl() is not a proper way to access SSL data for the session. Instead, THD::m_SSL now keeps the SSL data attached to the THD session. THD::m_SSL is set after the SSL connection is established, is reset upon disconnect, and is immutable during the session execution. Inspecting this attribute is safe when LOCK_thd_data is held, which make table performance_schema.status_by_thread safe. Root cause 2 ============ SELECT * from performance_schema.variables_by_thread inspects the variables of running sessions. In particular, variable "session_track_system_variables" is inspected. This variable value is stored in THD::variables.track_sysvars_ptr On the session connection, THD::variables.track_sysvars_ptr is duplicated and points to allocated memory, in this call: thd->session_sysvar_res_mgr.init(&thd->variables.track_sysvars_ptr, thd->charset()); This is because Session_sysvar_resource_manager::init() modifies the THD::variables.track_sysvars_ptr pointer itself. On session disconnect, Session_sysvar_resource_manager::deinit() free the allocated memory, which leaves the THD::variables.track_sysvars_ptr pointer invalid, referencing freed memory. As soon as cleanup_variables() unlocks LOCK_thd_data, a race condition is possible, when using the now invalid THD::variables.track_sysvars_ptr pointer. Fix 2 ===== In cleanup_variables(), clear the offending pointer before freeing the underlying memory with the call to session_sysvar_res_mgr.deinit() This makes table performance_schema.variables_by_thread safe.
1 parent 945478a commit 295bad1

15 files changed

+97
-76
lines changed

include/violite.h

+6
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,10 @@ struct Vio {
420420
Vio &operator=(Vio &&vio);
421421
};
422422

423+
#ifdef HAVE_OPENSSL
424+
#define SSL_handle SSL *
425+
#else
426+
#define SSL_handle void *
427+
#endif
428+
423429
#endif /* vio_violite_h_ */

sql/auth/sql_authentication.cc

+3-2
Original file line numberDiff line numberDiff line change
@@ -1737,7 +1737,7 @@ static bool read_client_connect_attrs(char **ptr, size_t *max_bytes_available,
17371737
static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user) {
17381738
#if defined(HAVE_OPENSSL)
17391739
Vio *vio = thd->get_protocol_classic()->get_vio();
1740-
SSL *ssl = thd->get_protocol()->get_ssl();
1740+
SSL *ssl = (SSL *)vio->ssl_arg;
17411741
X509 *cert;
17421742
#endif /* HAVE_OPENSSL */
17431743

@@ -2807,7 +2807,8 @@ static void server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio,
28072807
mpvio->auth_info.host_or_ip_length = sctx_host_or_ip.length;
28082808

28092809
#if defined(HAVE_OPENSSL)
2810-
if (thd->get_protocol()->get_ssl())
2810+
Vio *vio = thd->get_protocol_classic()->get_vio();
2811+
if (vio->ssl_arg)
28112812
mpvio->vio_is_encrypted = 1;
28122813
else
28132814
#endif /* HAVE_OPENSSL */

sql/mysqld.cc

+21-20
Original file line numberDiff line numberDiff line change
@@ -7640,77 +7640,78 @@ static int show_ssl_ctx_get_session_cache_mode(THD *, SHOW_VAR *var, char *) {
76407640
inside an Event.
76417641
*/
76427642
static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *) {
7643+
SSL_handle ssl = thd->get_ssl();
76437644
var->type = SHOW_CHAR;
7644-
if (thd->get_protocol()->get_ssl())
7645-
var->value =
7646-
const_cast<char *>(SSL_get_version(thd->get_protocol()->get_ssl()));
7645+
if (ssl)
7646+
var->value = const_cast<char *>(SSL_get_version(ssl));
76477647
else
76487648
var->value = (char *)"";
76497649
return 0;
76507650
}
76517651

76527652
static int show_ssl_session_reused(THD *thd, SHOW_VAR *var, char *buff) {
7653+
SSL_handle ssl = thd->get_ssl();
76537654
var->type = SHOW_LONG;
76547655
var->value = buff;
7655-
if (thd->get_protocol()->get_ssl())
7656-
*((long *)buff) = (long)SSL_session_reused(thd->get_protocol()->get_ssl());
7656+
if (ssl)
7657+
*((long *)buff) = (long)SSL_session_reused(ssl);
76577658
else
76587659
*((long *)buff) = 0;
76597660
return 0;
76607661
}
76617662

76627663
static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff) {
7664+
SSL_handle ssl = thd->get_ssl();
76637665
var->type = SHOW_LONG;
76647666
var->value = buff;
7665-
if (thd->get_protocol()->get_ssl())
7666-
*((long *)buff) =
7667-
(long)SSL_get_default_timeout(thd->get_protocol()->get_ssl());
7667+
if (ssl)
7668+
*((long *)buff) = (long)SSL_get_default_timeout(ssl);
76687669
else
76697670
*((long *)buff) = 0;
76707671
return 0;
76717672
}
76727673

76737674
static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff) {
7675+
SSL_handle ssl = thd->get_ssl();
76747676
var->type = SHOW_LONG;
76757677
var->value = buff;
7676-
if (thd->get_protocol()->get_ssl())
7677-
*((long *)buff) = (long)SSL_get_verify_mode(thd->get_protocol()->get_ssl());
7678+
if (ssl)
7679+
*((long *)buff) = (long)SSL_get_verify_mode(ssl);
76787680
else
76797681
*((long *)buff) = 0;
76807682
return 0;
76817683
}
76827684

76837685
static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff) {
7686+
SSL_handle ssl = thd->get_ssl();
76847687
var->type = SHOW_LONG;
76857688
var->value = buff;
7686-
if (thd->get_protocol()->get_ssl())
7687-
*((long *)buff) =
7688-
(long)SSL_get_verify_depth(thd->get_protocol()->get_ssl());
7689+
if (ssl)
7690+
*((long *)buff) = (long)SSL_get_verify_depth(ssl);
76897691
else
76907692
*((long *)buff) = 0;
76917693
return 0;
76927694
}
76937695

76947696
static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, char *) {
7697+
SSL_handle ssl = thd->get_ssl();
76957698
var->type = SHOW_CHAR;
7696-
if (thd->get_protocol()->get_ssl())
7697-
var->value =
7698-
const_cast<char *>(SSL_get_cipher(thd->get_protocol()->get_ssl()));
7699+
if (ssl)
7700+
var->value = const_cast<char *>(SSL_get_cipher(ssl));
76997701
else
77007702
var->value = (char *)"";
77017703
return 0;
77027704
}
77037705

77047706
static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff) {
7707+
SSL_handle ssl = thd->get_ssl();
77057708
var->type = SHOW_CHAR;
77067709
var->value = buff;
7707-
if (thd->get_protocol()->get_ssl()) {
7710+
if (ssl) {
77087711
int i;
77097712
const char *p;
77107713
char *end = buff + SHOW_VAR_FUNC_BUFF_SIZE;
7711-
for (i = 0; (p = SSL_get_cipher_list(thd->get_protocol()->get_ssl(), i)) &&
7712-
buff < end;
7713-
i++) {
7714+
for (i = 0; (p = SSL_get_cipher_list(ssl, i)) && buff < end; i++) {
77147715
buff = my_stpnmov(buff, p, end - buff - 1);
77157716
*buff++ = ':';
77167717
}

sql/protocol.h

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef PROTOCOL_INCLUDED
22
#define PROTOCOL_INCLUDED
33

4-
/* Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
4+
/* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
55
66
This program is free software; you can redistribute it and/or modify
77
it under the terms of the GNU General Public License, version 2.0,
@@ -232,15 +232,6 @@ class Protocol {
232232
true then the server is shutting down.
233233
*/
234234
virtual int shutdown(bool server_shutdown = false) = 0;
235-
236-
/**
237-
Returns pointer to the SSL object/struct
238-
239-
@return
240-
@retval SSL* The SSL struct/object
241-
@retval NULL If HAVE_OPENSSL is not defined
242-
*/
243-
virtual SSL_handle get_ssl() = 0;
244235
/**
245236
Returns the read/writing status
246237

sql/protocol_callback.cc

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License, version 2.0,
@@ -266,12 +266,6 @@ int Protocol_callback::shutdown(bool server_shutdown) {
266266
*/
267267
bool Protocol_callback::connection_alive() { return true; }
268268

269-
/**
270-
Returns SSL info. In case of Protocol_callback returns NULL, meaning
271-
no SSL connection.
272-
*/
273-
SSL_handle Protocol_callback::get_ssl() { return NULL; }
274-
275269
/**
276270
Should return protocol's reading/writing status. Returns 0 (idle) as it this
277271
is the best guess that can be made as there is no callback for

sql/protocol_callback.h

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License, version 2.0,
@@ -302,11 +302,6 @@ class Protocol_callback : public Protocol {
302302
*/
303303
virtual bool connection_alive();
304304

305-
/**
306-
Returns SSL info
307-
*/
308-
virtual SSL_handle get_ssl();
309-
310305
/**
311306
Should return protocol's reading/writing status. Returns 0 (idle) as it
312307
this is the best guess that can be made as there is no callback for

sql/protocol_classic.cc

-8
Original file line numberDiff line numberDiff line change
@@ -2971,14 +2971,6 @@ bool Protocol_classic::store_string_aux(const char *from, size_t length,
29712971
return net_store_data((uchar *)from, length);
29722972
}
29732973

2974-
SSL_handle Protocol_classic::get_ssl() {
2975-
return
2976-
#ifdef HAVE_OPENSSL
2977-
m_thd->net.vio ? (SSL *)m_thd->net.vio->ssl_arg :
2978-
#endif
2979-
NULL;
2980-
}
2981-
29822974
int Protocol_classic::shutdown(bool) {
29832975
return m_thd->net.vio ? vio_shutdown(m_thd->net.vio) : 0;
29842976
}

sql/protocol_classic.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef PROTOCOL_CLASSIC_INCLUDED
22
#define PROTOCOL_CLASSIC_INCLUDED
33

4-
/* Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
4+
/* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
55
66
This program is free software; you can redistribute it and/or modify
77
it under the terms of the GNU General Public License, version 2.0,
@@ -163,8 +163,6 @@ class Protocol_classic : public Protocol {
163163
uchar get_error();
164164
/* Set max allowed packet size */
165165
void set_max_packet_size(ulong max_packet_size);
166-
/* Return SSL descriptor, if any */
167-
SSL_handle get_ssl();
168166
/* Deinitialize VIO */
169167
virtual int shutdown(bool server_shutdown = false);
170168
/* Wipe NET with zeros */

sql/rpl_slave.cc

+4-14
Original file line numberDiff line numberDiff line change
@@ -8791,15 +8791,10 @@ bool start_slave(THD *thd, LEX_SLAVE_CONNECTION *connection_param,
87918791
mi->channel_wrlock();
87928792

87938793
if (connection_param->user || connection_param->password) {
8794-
#if defined(HAVE_OPENSSL)
8795-
if (!thd->get_protocol()->get_ssl())
8794+
if (!thd->get_ssl()) {
87968795
push_warning(thd, Sql_condition::SL_NOTE, ER_INSECURE_PLAIN_TEXT,
87978796
ER_THD(thd, ER_INSECURE_PLAIN_TEXT));
8798-
#endif
8799-
#if !defined(HAVE_OPENSSL)
8800-
push_warning(thd, Sql_condition::SL_NOTE, ER_INSECURE_PLAIN_TEXT,
8801-
ER_THD(thd, ER_INSECURE_PLAIN_TEXT));
8802-
#endif
8797+
}
88038798
}
88048799

88058800
lock_slave_threads(mi); // this allows us to cleanly read slave_running
@@ -9393,15 +9388,10 @@ static int change_receive_options(THD *thd, LEX_MASTER_INFO *lex_mi,
93939388
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong)mi->get_master_log_pos()));
93949389

93959390
if (lex_mi->user || lex_mi->password) {
9396-
#if defined(HAVE_OPENSSL)
9397-
if (!thd->get_protocol()->get_ssl())
9391+
if (!thd->get_ssl()) {
93989392
push_warning(thd, Sql_condition::SL_NOTE, ER_INSECURE_PLAIN_TEXT,
93999393
ER_THD(thd, ER_INSECURE_PLAIN_TEXT));
9400-
#endif
9401-
#if !defined(HAVE_OPENSSL)
9402-
push_warning(thd, Sql_condition::SL_NOTE, ER_INSECURE_PLAIN_TEXT,
9403-
ER_THD(thd, ER_INSECURE_PLAIN_TEXT));
9404-
#endif
9394+
}
94059395
push_warning(thd, Sql_condition::SL_NOTE, ER_INSECURE_CHANGE_MASTER,
94069396
ER_THD(thd, ER_INSECURE_CHANGE_MASTER));
94079397
}

sql/sql_class.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ int THD::send_explain_fields(Query_result *result) {
15891589
}
15901590

15911591
enum_vio_type THD::get_vio_type() {
1592-
DBUG_ENTER("shutdown_active_vio");
1592+
DBUG_ENTER("THD::get_vio_type");
15931593
DBUG_RETURN(get_protocol()->connection_type());
15941594
}
15951595

@@ -1599,6 +1599,7 @@ void THD::shutdown_active_vio() {
15991599
if (active_vio) {
16001600
vio_shutdown(active_vio);
16011601
active_vio = 0;
1602+
m_SSL = NULL;
16021603
}
16031604
DBUG_VOID_RETURN;
16041605
}

sql/sql_class.h

+38-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686
#include "mysqld_error.h"
8787
#include "prealloced_array.h"
8888
#include "sql/auth/sql_security_ctx.h" // Security_context
89-
#include "sql/discrete_interval.h" // Discrete_interval
89+
#include "sql/current_thd.h"
90+
#include "sql/discrete_interval.h" // Discrete_interval
9091
#include "sql/mdl.h"
9192
#include "sql/opt_costmodel.h"
9293
#include "sql/opt_trace_context.h" // Opt_trace_context
@@ -1140,6 +1141,20 @@ class THD : public MDL_context_owner,
11401141

11411142
Protocol *get_protocol() { return m_protocol; }
11421143

1144+
SSL_handle get_ssl() const {
1145+
#ifndef DBUG_OFF
1146+
if (current_thd != this) {
1147+
/*
1148+
When inspecting this thread from monitoring,
1149+
the monitoring thread MUST lock LOCK_thd_data,
1150+
to be allowed to safely inspect SSL status variables.
1151+
*/
1152+
mysql_mutex_assert_owner(&LOCK_thd_data);
1153+
}
1154+
#endif
1155+
return m_SSL;
1156+
}
1157+
11431158
/**
11441159
Asserts that the protocol is of type text or binary and then
11451160
returns the m_protocol casted to Protocol_classic. This method
@@ -1154,6 +1169,17 @@ class THD : public MDL_context_owner,
11541169

11551170
private:
11561171
Protocol *m_protocol; // Current protocol
1172+
/**
1173+
SSL data attached to this connection.
1174+
This is an opaque pointer,
1175+
When building with SSL, this pointer is non NULL
1176+
only if the connection is using SSL.
1177+
When building without SSL, this pointer is always NULL.
1178+
The SSL data can be inspected to read per thread
1179+
status variables,
1180+
and this can be inspected while the thread is running.
1181+
*/
1182+
SSL_handle m_SSL = {nullptr};
11571183

11581184
public:
11591185
/**
@@ -2477,9 +2503,20 @@ class THD : public MDL_context_owner,
24772503
mysql_mutex_unlock(&LOCK_thd_data);
24782504
}
24792505

2506+
inline void set_ssl(Vio *vio MY_ATTRIBUTE((unused))) {
2507+
#ifdef HAVE_OPENSSL
2508+
mysql_mutex_lock(&LOCK_thd_data);
2509+
m_SSL = (SSL *)vio->ssl_arg;
2510+
mysql_mutex_unlock(&LOCK_thd_data);
2511+
#else
2512+
m_SSL = NULL;
2513+
#endif
2514+
}
2515+
24802516
inline void clear_active_vio() {
24812517
mysql_mutex_lock(&LOCK_thd_data);
24822518
active_vio = 0;
2519+
m_SSL = NULL;
24832520
mysql_mutex_unlock(&LOCK_thd_data);
24842521
}
24852522

sql/sql_connect.cc

+8
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,14 @@ static int check_connection(THD *thd) {
632632
reset_host_connect_errors(thd->m_main_security_ctx.ip().str);
633633
}
634634

635+
/*
636+
Now that acl_authenticate() is executed,
637+
the SSL info is available.
638+
Advertise it to THD, so SSL status variables
639+
can be inspected.
640+
*/
641+
thd->set_ssl(net->vio);
642+
635643
return auth_rc;
636644
}
637645

sql/sql_plugin.cc

+2
Original file line numberDiff line numberDiff line change
@@ -2815,6 +2815,8 @@ static void cleanup_variables(THD *thd, struct System_variables *vars) {
28152815
mysql_mutex_lock(&thd->LOCK_thd_data);
28162816

28172817
plugin_var_memalloc_free(&thd->variables);
2818+
/* Remove references to session_sysvar_res_mgr memory before freeing it. */
2819+
thd->variables.track_sysvars_ptr = NULL;
28182820
thd->session_sysvar_res_mgr.deinit();
28192821
}
28202822
DBUG_ASSERT(vars->table_plugin == NULL);

sql/sql_prepare.cc

-3
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ class Protocol_local : public Protocol {
209209
virtual void end_partial_result_set();
210210
virtual int shutdown(bool server_shutdown = false);
211211
virtual bool connection_alive();
212-
virtual SSL_handle get_ssl();
213212
virtual void start_row();
214213
virtual bool end_row();
215214
virtual void abort_row(){};
@@ -3546,8 +3545,6 @@ void Protocol_local::end_partial_result_set() {}
35463545

35473546
int Protocol_local::shutdown(bool) { return 0; }
35483547

3549-
SSL_handle Protocol_local::get_ssl() { return NULL; }
3550-
35513548
/**
35523549
Called between two result set rows.
35533550

0 commit comments

Comments
 (0)