Skip to content

Commit 97cf6dd

Browse files
author
Grzegorz Szwarc
committed
Bug #27691698: UNDEFINED BEHAVIOR WHEN ACCESSING XPLUGIN STATUS VARIABLES
Description: All tests in the x test suite give UBSAN warnings: storage/perfschema/pfs_variable.cc:1386:9: runtime error: call to function void xpl::Server::global_status_variable_server<long long, &xpl::Global_status_variables::m_aborted_clients>(THD*, SHOW_VAR*, char*) through pointer to incorrect function type 'int (*)(THD *, SHOW_VAR *, char *)' Reviewed-by: Lukasz Kotula <[email protected]> Reviewed-by: Tomasz Stepniak <[email protected]> RB:19183
1 parent c67f612 commit 97cf6dd

File tree

3 files changed

+213
-256
lines changed

3 files changed

+213
-256
lines changed

plugin/x/src/xpl_plugin.cc

Lines changed: 213 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,8 @@
5050

5151
namespace {
5252

53-
typedef void (*Xpl_status_variable_get)(THD *, SHOW_VAR *, char *);
54-
55-
char *xpl_func_ptr(Xpl_status_variable_get callback) {
56-
union {
57-
char *ptr;
58-
Xpl_status_variable_get callback;
59-
} ptr_cast;
60-
61-
ptr_cast.callback = callback;
62-
63-
return ptr_cast.ptr;
53+
inline char *xpl_func_ptr(mysql_show_var_func callback) {
54+
return reinterpret_cast<char *>(callback);
6455
}
6556

6657
void exit_hook() { google::protobuf::ShutdownProtobufLibrary(); }
@@ -74,6 +65,180 @@ void check_exit_hook() {
7465
}
7566
}
7667

68+
xpl::Client_ptr get_client_by_thd(xpl::Server::Server_ptr &server, THD *thd) {
69+
struct Client_check_handler_thd {
70+
Client_check_handler_thd(THD *thd) : m_thd(thd) {}
71+
72+
bool operator()(ngs::Client_ptr &client) {
73+
xpl::Client *xpl_client = (xpl::Client *)client.get();
74+
75+
return xpl_client->is_handler_thd(m_thd);
76+
}
77+
78+
THD *m_thd;
79+
} client_check_thd(thd);
80+
81+
std::vector<ngs::Client_ptr> clients;
82+
(*server)->server().get_client_list().get_all_clients(clients);
83+
84+
std::vector<ngs::Client_ptr>::iterator i =
85+
std::find_if(clients.begin(), clients.end(), client_check_thd);
86+
if (clients.end() != i) return ngs::dynamic_pointer_cast<xpl::Client>(*i);
87+
88+
return xpl::Client_ptr();
89+
}
90+
91+
template <void (xpl::Client::*method)(SHOW_VAR *)>
92+
int session_status_variable(THD *thd, SHOW_VAR *var, char *buff) {
93+
var->type = SHOW_UNDEF;
94+
var->value = buff;
95+
96+
auto server(xpl::Server::get_instance());
97+
if (server) {
98+
MUTEX_LOCK(lock, (*server)->server().get_client_exit_mutex());
99+
xpl::Client_ptr client = get_client_by_thd(server, thd);
100+
101+
if (client) ((*client).*method)(var);
102+
}
103+
return 0;
104+
}
105+
106+
template <typename ReturnType,
107+
ReturnType (ngs::Ssl_session_options_interface::*method)() const>
108+
int session_status_variable(THD *thd, SHOW_VAR *var, char *buff) {
109+
var->type = SHOW_UNDEF;
110+
var->value = buff;
111+
112+
auto server(xpl::Server::get_instance());
113+
if (server) {
114+
MUTEX_LOCK(lock, (*server)->server().get_client_exit_mutex());
115+
xpl::Client_ptr client = get_client_by_thd(server, thd);
116+
117+
if (client) {
118+
ReturnType result =
119+
(ngs::Ssl_session_options(&client->connection()).*method)();
120+
mysqld::xpl_show_var(var).assign(result);
121+
}
122+
}
123+
return 0;
124+
}
125+
126+
template <void (xpl::Server::*method)(SHOW_VAR *)>
127+
int global_status_variable(THD *, SHOW_VAR *var, char *buff) {
128+
var->type = SHOW_UNDEF;
129+
var->value = buff;
130+
131+
auto server(xpl::Server::get_instance());
132+
if (server) {
133+
xpl::Server *server_ptr = server->container();
134+
(server_ptr->*method)(var);
135+
}
136+
return 0;
137+
}
138+
139+
template <typename ReturnType, ReturnType (xpl::Server::*method)()>
140+
int global_status_variable_server_with_return(THD *, SHOW_VAR *var,
141+
char *buff) {
142+
var->type = SHOW_UNDEF;
143+
var->value = buff;
144+
145+
auto server(xpl::Server::get_instance());
146+
if (server) {
147+
xpl::Server *server_ptr = server->container();
148+
ReturnType result = (server_ptr->*method)();
149+
150+
mysqld::xpl_show_var(var).assign(result);
151+
}
152+
return 0;
153+
}
154+
155+
template <typename ReturnType, xpl::Global_status_variables::Variable
156+
xpl::Global_status_variables::*variable>
157+
int global_status_variable_server(THD *, SHOW_VAR *var, char *buff) {
158+
var->type = SHOW_UNDEF;
159+
var->value = buff;
160+
161+
ReturnType result =
162+
(xpl::Global_status_variables::instance().*variable).load();
163+
mysqld::xpl_show_var(var).assign(result);
164+
return 0;
165+
}
166+
167+
template <typename ReturnType, ngs::Common_status_variables::Variable
168+
ngs::Common_status_variables::*variable>
169+
int common_status_variable(THD *thd, SHOW_VAR *var, char *buff) {
170+
var->type = SHOW_UNDEF;
171+
var->value = buff;
172+
173+
auto server(xpl::Server::get_instance());
174+
if (server) {
175+
MUTEX_LOCK(lock, (*server)->server().get_client_exit_mutex());
176+
xpl::Client_ptr client = get_client_by_thd(server, thd);
177+
178+
if (client) {
179+
// Status can be queried from different thread than client is bound to.
180+
// User can reset the session by sending SessionReset, to be secure for
181+
// released session pointer, the code needs to hold current session by
182+
// shared_ptr.
183+
auto client_session(client->session_smart_ptr());
184+
185+
if (client_session) {
186+
auto &common_status = client_session->get_status_variables();
187+
ReturnType result = (common_status.*variable).load();
188+
mysqld::xpl_show_var(var).assign(result);
189+
}
190+
return 0;
191+
}
192+
}
193+
194+
ngs::Common_status_variables &common_status =
195+
xpl::Global_status_variables::instance();
196+
ReturnType result = (common_status.*variable).load();
197+
mysqld::xpl_show_var(var).assign(result);
198+
return 0;
199+
}
200+
201+
template <typename ReturnType,
202+
ReturnType (ngs::Ssl_context_options_interface::*method)()>
203+
int global_status_variable(THD *, SHOW_VAR *var, char *buff) {
204+
var->type = SHOW_UNDEF;
205+
var->value = buff;
206+
207+
auto server(xpl::Server::get_instance());
208+
if (!server || !(*server)->server().ssl_context()) return 0;
209+
210+
auto &context = (*server)->server().ssl_context()->options();
211+
auto context_method = method; // workaround on VC compiler internal error
212+
ReturnType result = (context.*context_method)();
213+
214+
mysqld::xpl_show_var(var).assign(result);
215+
return 0;
216+
}
217+
218+
template <typename Copy_type,
219+
void (ngs::Client_interface::*method)(const Copy_type value)>
220+
void thd_variable(THD *thd, SYS_VAR *sys_var, void *tgt, const void *save) {
221+
// Lets copy the data to mysqld storage
222+
// this is going to allow following to return correct value:
223+
// SHOW SESSION VARIABLE LIKE '**var-name**';
224+
*static_cast<Copy_type *>(tgt) = *static_cast<const Copy_type *>(save);
225+
226+
// Lets make our own copy of it
227+
auto server(xpl::Server::get_instance());
228+
if (server) {
229+
MUTEX_LOCK(lock, (*server)->server().get_client_exit_mutex());
230+
231+
xpl::Client_ptr client = get_client_by_thd(server, thd);
232+
if (client) ((*client).*method)(*static_cast<Copy_type *>(tgt));
233+
234+
// We should store the variables values so that they can be set when new
235+
// client is connecting. This is done through a registered
236+
// update_global_timeout_values callback.
237+
xpl::Plugin_system_variables::update_func<Copy_type>(thd, sys_var, tgt,
238+
save);
239+
}
240+
}
241+
77242
} // namespace
78243

79244
/*
@@ -219,36 +384,30 @@ static MYSQL_THDVAR_UINT(
219384
wait_timeout, PLUGIN_VAR_OPCMDARG,
220385
"Number or seconds that X Plugin must wait for activity on noninteractive "
221386
"connection",
222-
NULL,
223-
(&xpl::Server::thd_variable<uint32_t,
224-
&ngs::Client_interface::set_wait_timeout>),
387+
NULL, (&thd_variable<uint32_t, &ngs::Client_interface::set_wait_timeout>),
225388
Global_timeouts::Default::k_wait_timeout, 1, 2147483, 0);
226389

227390
static MYSQL_SYSVAR_UINT(
228391
interactive_timeout, xpl::Plugin_system_variables::m_interactive_timeout,
229392
PLUGIN_VAR_OPCMDARG,
230-
"Default value for \"mysqlx_wait_timeout\", when the connection is \
231-
interactive. The value defines number or seconds that X Plugin must wait for \
232-
activity on interactive connection",
393+
"Default value for \"mysqlx_wait_timeout\", when the connection is "
394+
"interactive. The value defines number or seconds that X Plugin must "
395+
"wait for activity on interactive connection",
233396
NULL, &xpl::Plugin_system_variables::update_func<uint32_t>,
234397
Global_timeouts::Default::k_interactive_timeout, 1, 2147483, 0);
235398

236399
static MYSQL_THDVAR_UINT(
237400
read_timeout, PLUGIN_VAR_OPCMDARG,
238401
"Number or seconds that X Plugin must wait for blocking read operation to "
239402
"complete",
240-
NULL,
241-
(&xpl::Server::thd_variable<uint32_t,
242-
&ngs::Client_interface::set_read_timeout>),
403+
NULL, (&thd_variable<uint32_t, &ngs::Client_interface::set_read_timeout>),
243404
Global_timeouts::Default::k_read_timeout, 1, 2147483, 0);
244405

245406
static MYSQL_THDVAR_UINT(
246407
write_timeout, PLUGIN_VAR_OPCMDARG,
247408
"Number or seconds that X Plugin must wait for blocking write operation to "
248409
"complete",
249-
NULL,
250-
(&xpl::Server::thd_variable<uint32_t,
251-
&ngs::Client_interface::set_write_timeout>),
410+
NULL, (&thd_variable<uint32_t, &ngs::Client_interface::set_write_timeout>),
252411
Global_timeouts::Default::k_write_timeout, 1, 2147483, 0);
253412

254413
static MYSQL_SYSVAR_UINT(
@@ -285,49 +444,47 @@ static struct SYS_VAR *xpl_plugin_system_variables[] = {
285444
MYSQL_SYSVAR(document_id_unique_prefix),
286445
NULL};
287446

288-
#define SESSION_STATUS_VARIABLE_ENTRY_LONGLONG(NAME, METHOD) \
289-
{ \
290-
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
291-
xpl_func_ptr(xpl::Server::common_status_variable<long long, &METHOD>), \
292-
SHOW_FUNC, SHOW_SCOPE_GLOBAL \
293-
}
294-
295-
#define GLOBAL_STATUS_VARIABLE_ENTRY_LONGLONG(NAME, METHOD) \
447+
#define SESSION_STATUS_VARIABLE_ENTRY_LONGLONG(NAME, METHOD) \
296448
{ \
297449
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
298-
xpl_func_ptr( \
299-
xpl::Server::global_status_variable_server<long long, &METHOD>), \
300-
SHOW_FUNC, SHOW_SCOPE_GLOBAL \
450+
xpl_func_ptr(common_status_variable<long long, &METHOD>), SHOW_FUNC, \
451+
SHOW_SCOPE_GLOBAL \
452+
}
453+
454+
#define GLOBAL_STATUS_VARIABLE_ENTRY_LONGLONG(NAME, METHOD) \
455+
{ \
456+
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
457+
xpl_func_ptr(global_status_variable_server<long long, &METHOD>), \
458+
SHOW_FUNC, SHOW_SCOPE_GLOBAL \
301459
}
302460

303-
#define GLOBAL_SSL_STATUS_VARIABLE_ENTRY(NAME, TYPE, METHOD) \
304-
{ \
305-
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
306-
xpl_func_ptr(xpl::Server::global_status_variable<TYPE, &METHOD>), \
307-
SHOW_FUNC, SHOW_SCOPE_GLOBAL \
461+
#define GLOBAL_SSL_STATUS_VARIABLE_ENTRY(NAME, TYPE, METHOD) \
462+
{ \
463+
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
464+
xpl_func_ptr(global_status_variable<TYPE, &METHOD>), SHOW_FUNC, \
465+
SHOW_SCOPE_GLOBAL \
308466
}
309467

310-
#define SESSION_SSL_STATUS_VARIABLE_ENTRY(NAME, TYPE, METHOD) \
311-
{ \
312-
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
313-
xpl_func_ptr(xpl::Server::session_status_variable<TYPE, &METHOD>), \
314-
SHOW_FUNC, SHOW_SCOPE_GLOBAL \
468+
#define SESSION_SSL_STATUS_VARIABLE_ENTRY(NAME, TYPE, METHOD) \
469+
{ \
470+
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
471+
xpl_func_ptr(session_status_variable<TYPE, &METHOD>), SHOW_FUNC, \
472+
SHOW_SCOPE_GLOBAL \
315473
}
316474

317-
#define SESSION_SSL_STATUS_VARIABLE_ENTRY_ARRAY(NAME, METHOD) \
318-
{ \
319-
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
320-
xpl_func_ptr(xpl::Server::session_status_variable<&METHOD>), \
321-
SHOW_FUNC, SHOW_SCOPE_GLOBAL \
475+
#define SESSION_SSL_STATUS_VARIABLE_ENTRY_ARRAY(NAME, METHOD) \
476+
{ \
477+
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
478+
xpl_func_ptr(session_status_variable<&METHOD>), SHOW_FUNC, \
479+
SHOW_SCOPE_GLOBAL \
322480
}
323481

324-
#define GLOBAL_CUSTOM_STATUS_VARIABLE_ENTRY(NAME, TYPE, METHOD) \
325-
{ \
326-
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
327-
xpl_func_ptr( \
328-
xpl::Server::global_status_variable_server_with_return<TYPE, \
329-
&METHOD>), \
330-
SHOW_FUNC, SHOW_SCOPE_GLOBAL \
482+
#define GLOBAL_CUSTOM_STATUS_VARIABLE_ENTRY(NAME, TYPE, METHOD) \
483+
{ \
484+
MYSQLX_STATUS_VARIABLE_PREFIX(NAME), \
485+
xpl_func_ptr( \
486+
global_status_variable_server_with_return<TYPE, &METHOD>), \
487+
SHOW_FUNC, SHOW_SCOPE_GLOBAL \
331488
}
332489

333490
static SHOW_VAR xpl_plugin_status[] = {

plugin/x/src/xpl_server.cc

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -722,31 +722,6 @@ std::string xpl::Server::get_tcp_bind_address() {
722722
return ::STATUS_VALUE_FOR_NOT_CONFIGURED_INTERFACE;
723723
}
724724

725-
struct Client_check_handler_thd {
726-
Client_check_handler_thd(THD *thd) : m_thd(thd) {}
727-
728-
bool operator()(ngs::Client_ptr &client) {
729-
xpl::Client *xpl_client = (xpl::Client *)client.get();
730-
731-
return xpl_client->is_handler_thd(m_thd);
732-
}
733-
734-
THD *m_thd;
735-
};
736-
737-
xpl::Client_ptr xpl::Server::get_client_by_thd(Server_ptr &server, THD *thd) {
738-
std::vector<ngs::Client_ptr> clients;
739-
Client_check_handler_thd client_check_thd(thd);
740-
741-
(*server)->server().get_client_list().get_all_clients(clients);
742-
743-
std::vector<ngs::Client_ptr>::iterator i =
744-
std::find_if(clients.begin(), clients.end(), client_check_thd);
745-
if (clients.end() != i) return ngs::dynamic_pointer_cast<Client>(*i);
746-
747-
return Client_ptr();
748-
}
749-
750725
void xpl::Server::register_udfs() {
751726
udf::Registrator r;
752727
r.registration(udf::get_mysqlx_error_record(), &m_udf_names);

0 commit comments

Comments
 (0)