Skip to content

Commit ad16eb8

Browse files
ssorumgarddahlerlend
authored andcommitted
Bug#36778475 Issues with I_S.processlist due to missing synchronization
The protocol of a non-current THD was accessed fron the code path of I_S processlist. This caused a race condition to occur which resulted in invalid memory being accessed. The fix is to cache information being accessed and to make sure protocol objects of non-current THDs are not referenced from within the I_S processlist implementation. Change-Id: I0ce332854f0bc63879da68702846c0ca52787ca3
1 parent 1991370 commit ad16eb8

File tree

6 files changed

+90
-10
lines changed

6 files changed

+90
-10
lines changed

sql-common/net_serv.cc

+35-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ using std::min;
8181
*/
8282

8383
#ifdef MYSQL_SERVER
84-
8584
/*
8685
The following variables/functions should really not be declared
8786
extern, but as it's hard to include sql_class.h here, we have to
@@ -92,6 +91,13 @@ extern void thd_increment_bytes_received(size_t length);
9291

9392
/* Additional instrumentation hooks for the server */
9493
#include "mysql_com_server.h"
94+
95+
/* Refresh cached values in the THD for the server. */
96+
static void server_store_cached_values(const NET *net) {
97+
// Refresh only if this net is the net of the current_thd
98+
if (current_thd != nullptr && current_thd->get_net() == net)
99+
current_thd->store_cached_properties(THD::cached_properties::RW_STATUS);
100+
}
95101
#endif
96102

97103
static bool net_write_buff(NET *, const uchar *, size_t);
@@ -167,6 +173,7 @@ bool my_net_init(NET *net, Vio *vio) {
167173
net->last_errno = 0;
168174
#ifdef MYSQL_SERVER
169175
net->extension = nullptr;
176+
server_store_cached_values(net);
170177
#else
171178
NET_EXTENSION *ext = net_extension_init();
172179
ext->net_async_context->cur_pos = net->buff + net->where_b;
@@ -1303,6 +1310,9 @@ bool net_write_packet(NET *net, const uchar *packet, size_t length) {
13031310
return true;
13041311

13051312
net->reading_or_writing = 2;
1313+
#ifdef MYSQL_SERVER
1314+
server_store_cached_values(net);
1315+
#endif
13061316

13071317
const bool do_compress = net->compress;
13081318
if (do_compress) {
@@ -1311,6 +1321,9 @@ bool net_write_packet(NET *net, const uchar *packet, size_t length) {
13111321
net->last_errno = ER_OUT_OF_RESOURCES;
13121322
/* In the server, allocation failure raises a error. */
13131323
net->reading_or_writing = 0;
1324+
#ifdef MYSQL_SERVER
1325+
server_store_cached_values(net);
1326+
#endif
13141327
return true;
13151328
}
13161329
}
@@ -1324,6 +1337,9 @@ bool net_write_packet(NET *net, const uchar *packet, size_t length) {
13241337
if (do_compress) my_free(const_cast<uchar *>(packet));
13251338

13261339
net->reading_or_writing = 0;
1340+
#ifdef MYSQL_SERVER
1341+
server_store_cached_values(net);
1342+
#endif
13271343

13281344
/* Socket can't be used any more */
13291345
if (net->error == NET_ERROR_SOCKET_NOT_READABLE) {
@@ -1712,6 +1728,9 @@ static net_async_status net_read_packet_nonblocking(NET *net, ulong *ret) {
17121728
case NET_ASYNC_PACKET_READ_IDLE:
17131729
net_async->async_packet_read_state = NET_ASYNC_PACKET_READ_HEADER;
17141730
net->reading_or_writing = 0;
1731+
#ifdef MYSQL_SERVER
1732+
server_store_cached_values(net);
1733+
#endif
17151734
[[fallthrough]];
17161735
case NET_ASYNC_PACKET_READ_HEADER:
17171736
/*
@@ -1788,6 +1807,9 @@ static net_async_status net_read_packet_nonblocking(NET *net, ulong *ret) {
17881807

17891808
net->read_pos[*ret] = 0;
17901809
net->reading_or_writing = 0;
1810+
#ifdef MYSQL_SERVER
1811+
server_store_cached_values(net);
1812+
#endif
17911813
if (net->compress) {
17921814
mysql_compress_context *mysql_compress_ctx = compress_context(net);
17931815
if (my_uncompress(mysql_compress_ctx, net->buff + net->where_b,
@@ -1808,6 +1830,9 @@ static net_async_status net_read_packet_nonblocking(NET *net, ulong *ret) {
18081830
error:
18091831
*ret = packet_error;
18101832
net->reading_or_writing = 0;
1833+
#ifdef MYSQL_SERVER
1834+
server_store_cached_values(net);
1835+
#endif
18111836
return NET_ASYNC_COMPLETE;
18121837
}
18131838

@@ -2093,6 +2118,9 @@ static size_t net_read_packet(NET *net, size_t *complen) {
20932118
*complen = 0;
20942119

20952120
net->reading_or_writing = 1;
2121+
#ifdef MYSQL_SERVER
2122+
server_store_cached_values(net);
2123+
#endif
20962124

20972125
/*
20982126
We should reset compress_packet_nr even before reading the header because
@@ -2140,12 +2168,18 @@ static size_t net_read_packet(NET *net, size_t *complen) {
21402168
net->error = NET_ERROR_SOCKET_UNUSABLE;
21412169
DBUG_DUMP("net read", net->buff + net->where_b, pkt_len);
21422170
net->reading_or_writing = 0;
2171+
#ifdef MYSQL_SERVER
2172+
server_store_cached_values(net);
2173+
#endif
21432174
return pkt_len;
21442175

21452176
error:
21462177
if (net->error == NET_ERROR_SOCKET_NOT_WRITABLE)
21472178
net->error = NET_ERROR_SOCKET_UNUSABLE;
21482179
net->reading_or_writing = 0;
2180+
#ifdef MYSQL_SERVER
2181+
server_store_cached_values(net);
2182+
#endif
21492183
return packet_error;
21502184
}
21512185

sql/protocol_classic.cc

+1
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,7 @@ uchar Protocol_classic::get_error() { return m_thd->net.error; }
13851385

13861386
void Protocol_classic::wipe_net() {
13871387
memset(&m_thd->net, 0, sizeof(m_thd->net));
1388+
m_thd->store_cached_properties(THD::cached_properties::RW_STATUS);
13881389
}
13891390

13901391
void Protocol_classic::set_max_packet_size(ulong max_packet_size) {

sql/sql_class.cc

+35-6
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ void THD::enter_stage(const PSI_stage_info *new_stage,
608608

609609
m_current_stage_key = new_stage->m_key;
610610
set_proc_info(msg);
611+
store_cached_properties(cached_properties::RW_STATUS);
611612

612613
m_stage_progress_psi =
613614
MYSQL_SET_STAGE(m_current_stage_key, calling_file, calling_line);
@@ -870,7 +871,7 @@ THD::THD(bool enable_plugins)
870871
protocol_text->init(this);
871872
protocol_binary->init(this);
872873
protocol_text->set_client_capabilities(0); // minimalistic client
873-
m_cached_is_connection_alive.store(m_protocol->connection_alive());
874+
store_cached_properties();
874875

875876
/*
876877
Make sure thr_lock_info_init() is called for threads which do not get
@@ -907,6 +908,22 @@ THD::THD(bool enable_plugins)
907908
}
908909
}
909910

911+
void THD::store_cached_properties(cached_properties prop_mask) {
912+
DBUG_EXECUTE_IF("assert_only_current_thd_protocol_access",
913+
{ assert(current_thd == this); });
914+
915+
auto is_selected = [this, prop_mask](cached_properties property) -> bool {
916+
return (this->m_protocol != nullptr &&
917+
static_cast<int>(prop_mask) & static_cast<int>(property));
918+
};
919+
920+
if (is_selected(cached_properties::IS_ALIVE))
921+
m_cached_is_connection_alive.store(m_protocol->connection_alive());
922+
923+
if (is_selected(cached_properties::RW_STATUS))
924+
m_cached_rw_status.store(m_protocol->get_rw_status());
925+
}
926+
910927
void THD::copy_table_access_properties(THD *thd) {
911928
thread_stack = thd->thread_stack;
912929
variables.option_bits = thd->variables.option_bits & OPTION_BIN_LOG;
@@ -1427,7 +1444,7 @@ void THD::release_resources() {
14271444
if (is_classic_protocol() && get_protocol_classic()->get_vio()) {
14281445
vio_delete(get_protocol_classic()->get_vio());
14291446
get_protocol_classic()->end_net();
1430-
m_cached_is_connection_alive.store(m_protocol->connection_alive());
1447+
store_cached_properties();
14311448
}
14321449

14331450
/* modification plan for UPDATE/DELETE should be freed. */
@@ -1732,6 +1749,8 @@ void THD::disconnect(bool server_shutdown) {
17321749
/* Disconnect even if a active vio is not associated. */
17331750
if (is_classic_protocol() && get_protocol_classic()->get_vio() != vio &&
17341751
get_protocol_classic()->connection_alive()) {
1752+
DBUG_EXECUTE_IF("assert_only_current_thd_protocol_access",
1753+
{ assert(current_thd == this); });
17351754
m_protocol->shutdown(server_shutdown);
17361755
}
17371756
}
@@ -2341,6 +2360,8 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
23412360
backup->examined_row_count = m_examined_row_count;
23422361
backup->sent_row_count = m_sent_row_count;
23432362
backup->num_truncated_fields = num_truncated_fields;
2363+
DBUG_EXECUTE_IF("assert_only_current_thd_protocol_access",
2364+
{ assert(current_thd == this); });
23442365
backup->client_capabilities = m_protocol->get_client_capabilities();
23452366
backup->savepoints = get_transaction()->m_savepoints;
23462367
backup->first_successful_insert_id_in_prev_stmt =
@@ -2913,6 +2934,8 @@ bool THD::send_result_metadata(const mem_root_deque<Item *> &list, uint flags) {
29132934
DBUG_TRACE;
29142935
uchar buff[MAX_FIELD_WIDTH];
29152936
String tmp((char *)buff, sizeof(buff), &my_charset_bin);
2937+
DBUG_EXECUTE_IF("assert_only_current_thd_protocol_access",
2938+
{ assert(current_thd == this); });
29162939

29172940
if (m_protocol->start_result_metadata(CountVisibleFields(list), flags,
29182941
variables.character_set_results))
@@ -2953,6 +2976,8 @@ bool THD::send_result_set_row(const mem_root_deque<Item *> &row_items) {
29532976
String str_buffer(buffer, sizeof(buffer), &my_charset_bin);
29542977

29552978
DBUG_TRACE;
2979+
DBUG_EXECUTE_IF("assert_only_current_thd_protocol_access",
2980+
{ assert(current_thd == this); });
29562981

29572982
for (Item *item : VisibleFields(row_items)) {
29582983
if (item->send(m_protocol, &str_buffer) || is_error()) return true;
@@ -2970,6 +2995,8 @@ void THD::send_statement_status() {
29702995
assert(!get_stmt_da()->is_sent());
29712996
bool error = false;
29722997
Diagnostics_area *da = get_stmt_da();
2998+
DBUG_EXECUTE_IF("assert_only_current_thd_protocol_access",
2999+
{ assert(current_thd == this); });
29733000

29743001
/* Can not be true, but do not take chances in production. */
29753002
if (da->is_sent()) return;
@@ -3270,14 +3297,16 @@ bool THD::is_connected(bool use_cached_connection_alive) {
32703297
return get_protocol()->connection_alive();
32713298
}
32723299

3300+
uint THD::get_protocol_rw_status() { return m_cached_rw_status.load(); }
3301+
32733302
Protocol *THD::get_protocol() {
3274-
m_cached_is_connection_alive.store(m_protocol->connection_alive());
3303+
store_cached_properties();
32753304
return m_protocol;
32763305
}
32773306

32783307
Protocol_classic *THD::get_protocol_classic() {
32793308
assert(is_classic_protocol());
3280-
m_cached_is_connection_alive.store(m_protocol->connection_alive());
3309+
store_cached_properties();
32813310
return pointer_cast<Protocol_classic *>(m_protocol);
32823311
}
32833312

@@ -3286,14 +3315,14 @@ void THD::push_protocol(Protocol *protocol) {
32863315
assert(protocol != nullptr);
32873316
m_protocol->push_protocol(protocol);
32883317
m_protocol = protocol;
3289-
m_cached_is_connection_alive.store(m_protocol->connection_alive());
3318+
store_cached_properties();
32903319
}
32913320

32923321
void THD::pop_protocol() {
32933322
assert(m_protocol != nullptr);
32943323
m_protocol = m_protocol->pop_protocol();
32953324
assert(m_protocol != nullptr);
3296-
m_cached_is_connection_alive.store(m_protocol->connection_alive());
3325+
store_cached_properties();
32973326
}
32983327

32993328
void THD::set_time() {

sql/sql_class.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,17 @@ class THD : public MDL_context_owner,
10741074
handlerton *m_eligible_secondary_engine_handlerton;
10751075

10761076
public:
1077+
/* Store a thread safe copy of protocol properties. */
1078+
enum class cached_properties : int {
1079+
NONE = 0, // No properties
1080+
IS_ALIVE = 1, // protocol->is_connection_alive()
1081+
RW_STATUS = 2, // protocol->get_rw_status()
1082+
LAST = 4, // Next unused power of 2.
1083+
ALL = (LAST - 1) // Mask selecting all properties.
1084+
};
1085+
void store_cached_properties(
1086+
cached_properties prop_mask = cached_properties::ALL);
1087+
10771088
/* Used to execute base64 coded binlog events in MySQL server */
10781089
Relay_log_info *rli_fake;
10791090
/* Slave applier execution context */
@@ -1299,12 +1310,13 @@ class THD : public MDL_context_owner,
12991310
mysql_mutex_t LOCK_query_plan;
13001311

13011312
/**
1302-
Keep a cached value saying whether the connection is alive. Update when
1313+
Keep cached values of "connection alive" and "rw status". Update when
13031314
pushing, popping or getting the protocol. Used by
13041315
information_schema.processlist to avoid locking mutexes that might
13051316
affect performance.
13061317
*/
13071318
std::atomic<bool> m_cached_is_connection_alive;
1319+
std::atomic<uint> m_cached_rw_status;
13081320

13091321
public:
13101322
/// Locks the query plan of this THD
@@ -3291,6 +3303,9 @@ class THD : public MDL_context_owner,
32913303
/** Return false if connection to client is broken. */
32923304
bool is_connected(bool use_cached_connection_alive = false) final;
32933305

3306+
/** Return the cached protocol rw status. */
3307+
uint get_protocol_rw_status();
3308+
32943309
/**
32953310
Mark the current error as fatal. Warning: this does not
32963311
set any error, it sets a property of the error, so must be

sql/sql_show.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -2791,8 +2791,8 @@ class thread_info_compare {
27912791

27922792
static const char *thread_state_info(THD *invoking_thd, THD *inspected_thd) {
27932793
DBUG_TRACE;
2794-
if (inspected_thd->get_protocol()->get_rw_status()) {
2795-
if (inspected_thd->get_protocol()->get_rw_status() == 2)
2794+
if (inspected_thd->get_protocol_rw_status()) {
2795+
if (inspected_thd->get_protocol_rw_status() == 2)
27962796
return "Sending to client";
27972797
if (inspected_thd->get_command() == COM_SLEEP) return "";
27982798
return "Receiving from client";

sql/sql_thd_api.cc

+1
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ uint thd_get_net_read_write(THD *thd) {
256256

257257
void thd_set_net_read_write(THD *thd, uint val) {
258258
thd->get_protocol_classic()->get_net()->reading_or_writing = val;
259+
thd->store_cached_properties(THD::cached_properties::RW_STATUS);
259260
}
260261

261262
/**

0 commit comments

Comments
 (0)