Skip to content

Commit 70cf38f

Browse files
author
Justin Jose
committed
BUG#32103192: REPLICA ACCEPTS GTID_LOG_EVENT WITH GNO=INT64_MAX
Description: ------------ The valid range for the GNO part of a GTID (the second, numeric component) is 1 to (1<<63)-2, inclusive. However, a replica accepts Gtid_log_events having GNO=(1<<63)-1, despite being out of range. This may break automation, cause downstream replicas to go out of sync, and probably other bad things. Analysis: --------- In debug mode, replica fails with an assertion. In optimized mode, the consequences are a bit unclear. From testing, apparently auto-skip does not work for this GNO and several transactions with the same GTID can be committed, and their GTIDs will be repeated in gtid_executed. Moreover, GNO_MAX is defined as LLONG_MAX, which may be bigger than (1<<63)-1 on platforms where long long is bigger than 64 bits. Therefore, such platforms will allow big GTIDs, which will fail on downstream replicas. Also, the text representation is assumed to take at most 19 bytes (the length of the decimal representation of (1<<63)-2), so on platforms where we allow bigger GTIDs, we may overflow buffers. Fix: ---- - Add a check to prevent the replica to apply a Gtid_event that has a GTID with an invalid GNO number. - Change all places to use INT64_MAX as an exclusive limit instead of LLONG_MAX. - Rename MAX_GNO to GNO_END for better representation. RB:25925
1 parent af04844 commit 70cf38f

File tree

9 files changed

+55
-14
lines changed

9 files changed

+55
-14
lines changed

rapid/plugin/group_replication/src/certifier.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -475,14 +475,14 @@ void Certifier::compute_group_available_gtid_intervals()
475475
}
476476

477477
// For each used interval find the upper bound and from there
478-
// add the free GTIDs up to the next interval or MAX_GNO.
478+
// add the free GTIDs up to the next interval or GNO_END.
479479
while ((iv= ivit.get()) != NULL)
480480
{
481481
ivit.next();
482482
iv_next= ivit.get();
483483

484484
rpl_gno start= iv->end;
485-
rpl_gno end= MAX_GNO;
485+
rpl_gno end= GNO_END;
486486
if (iv_next != NULL)
487487
end= iv_next->start - 1;
488488

@@ -494,7 +494,7 @@ void Certifier::compute_group_available_gtid_intervals()
494494
// No GTIDs used, so the available interval is the complete set.
495495
if (group_available_gtid_intervals.size() == 0)
496496
{
497-
Gtid_set::Interval interval= {1, MAX_GNO, NULL};
497+
Gtid_set::Interval interval= {1, GNO_END, NULL};
498498
group_available_gtid_intervals.push_back(interval);
499499
}
500500

@@ -1001,7 +1001,7 @@ rpl_gno Certifier::get_group_next_available_gtid(const char *member_uuid)
10011001

10021002
if (member_uuid == NULL || gtid_assignment_block_size <= 1)
10031003
{
1004-
result= get_group_next_available_gtid_candidate(1, MAX_GNO);
1004+
result= get_group_next_available_gtid_candidate(1, GNO_END);
10051005
if (result < 0)
10061006
{
10071007
assert(result == -1);
@@ -1093,7 +1093,7 @@ Certifier::get_group_next_available_gtid_candidate(rpl_gno start,
10931093
{
10941094
assert(candidate >= start);
10951095
const Gtid_set::Interval *iv= ivit.get();
1096-
rpl_gno next_interval_start= iv != NULL ? iv->start : MAX_GNO;
1096+
rpl_gno next_interval_start= iv != NULL ? iv->start : GNO_END;
10971097

10981098
// Correct interval.
10991099
if (candidate < next_interval_start)

rapid/plugin/group_replication/src/plugin.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ ulong compression_threshold_var= DEFAULT_COMPRESSION_THRESHOLD;
193193
/* GTID assignment block size options */
194194
#define DEFAULT_GTID_ASSIGNMENT_BLOCK_SIZE 1000000
195195
#define MIN_GTID_ASSIGNMENT_BLOCK_SIZE 1
196-
#define MAX_GTID_ASSIGNMENT_BLOCK_SIZE MAX_GNO
196+
#define MAX_GTID_ASSIGNMENT_BLOCK_SIZE GNO_END
197197
ulonglong gtid_assignment_block_size_var= DEFAULT_GTID_ASSIGNMENT_BLOCK_SIZE;
198198

199199
/* Flow control options */

sql/log_event.cc

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13405,7 +13405,20 @@ Gtid_log_event::Gtid_log_event(const char *buffer, uint event_len,
1340513405
ANONYMOUS_GROUP : GTID_GROUP;
1340613406
sid.copy_from((uchar *)Uuid_parent_struct.bytes);
1340713407
spec.gtid.sidno= gtid_info_struct.rpl_gtid_sidno;
13408+
//GNO sanity check
13409+
if (spec.type == GTID_GROUP) {
13410+
if (gtid_info_struct.rpl_gtid_gno <= 0 || gtid_info_struct.rpl_gtid_gno >= GNO_END)
13411+
goto err;
13412+
} else { //ANONYMOUS_GTID_LOG_EVENT
13413+
if (gtid_info_struct.rpl_gtid_gno != 0)
13414+
goto err;
13415+
}
1340813416
spec.gtid.gno= gtid_info_struct.rpl_gtid_gno;
13417+
13418+
DBUG_VOID_RETURN;
13419+
13420+
err:
13421+
is_valid_param= false;
1340913422
DBUG_VOID_RETURN;
1341013423
}
1341113424

@@ -13464,10 +13477,15 @@ Gtid_log_event::Gtid_log_event(uint32 server_id_arg, bool using_trans,
1346413477
DBUG_ENTER("Gtid_log_event::Gtid_log_event(uint32, bool, int64, int64, const Gtid_specification)");
1346513478
server_id= server_id_arg;
1346613479
common_header->unmasked_server_id= server_id_arg;
13480+
is_valid_param= true;
1346713481

1346813482
if (spec_arg.type == GTID_GROUP)
1346913483
{
13470-
assert(spec_arg.gtid.sidno > 0 && spec_arg.gtid.gno > 0);
13484+
assert(spec_arg.gtid.sidno > 0);
13485+
assert(spec_arg.gtid.gno > 0);
13486+
assert(spec_arg.gtid.gno < GNO_END);
13487+
if (spec_arg.gtid.gno <= 0 || spec_arg.gtid.gno >= GNO_END)
13488+
is_valid_param= false;
1347113489
spec.set(spec_arg.gtid);
1347213490
global_sid_lock->rdlock();
1347313491
sid= global_sid_map->sidno_to_sid(spec_arg.gtid.sidno);
@@ -13492,7 +13510,6 @@ Gtid_log_event::Gtid_log_event(uint32 server_id_arg, bool using_trans,
1349213510
to_string(buf);
1349313511
DBUG_PRINT("info", ("%s", buf));
1349413512
#endif
13495-
is_valid_param= true;
1349613513
DBUG_VOID_RETURN;
1349713514
}
1349813515
#endif
@@ -13580,6 +13597,11 @@ uint32 Gtid_log_event::write_data_header_to_memory(uchar *buffer)
1358013597
sid.copy_to(ptr_buffer);
1358113598
ptr_buffer+= ENCODED_SID_LENGTH;
1358213599

13600+
#ifndef NDEBUG
13601+
if (DBUG_EVALUATE_IF("send_invalid_gno_to_replica", true, false))
13602+
int8store(ptr_buffer, GNO_END);
13603+
else
13604+
#endif
1358313605
int8store(ptr_buffer, spec.gtid.gno);
1358413606
ptr_buffer+= ENCODED_GNO_LENGTH;
1358513607

sql/rpl_gtid.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
#include "table.h"
3737
#endif
3838

39+
#ifndef INT64_MAX
40+
#define INT64_MAX 0x7fffffffffffffffLL
41+
#endif
42+
3943
#include <list>
4044
#include "atomic_class.h"
4145

@@ -401,7 +405,7 @@ inline const char *get_gtid_consistency_mode_string()
401405

402406

403407
/// The maximum value of GNO
404-
const rpl_gno MAX_GNO= LLONG_MAX;
408+
const rpl_gno GNO_END= INT64_MAX;
405409
/// The length of MAX_GNO when printed in decimal.
406410
const int MAX_GNO_TEXT_LENGTH= 19;
407411
/// The maximal possible length of thread_id when printed in decimal.
@@ -981,6 +985,7 @@ struct Gtid
981985
{
982986
assert(sidno_arg > 0);
983987
assert(gno_arg > 0);
988+
assert(gno_arg < GNO_END);
984989
sidno= sidno_arg;
985990
gno= gno_arg;
986991
}
@@ -1140,6 +1145,9 @@ class Gtid_set
11401145
void _add_gtid(rpl_sidno sidno, rpl_gno gno)
11411146
{
11421147
DBUG_ENTER("Gtid_set::_add_gtid(sidno, gno)");
1148+
assert(sidno > 0);
1149+
assert(gno > 0);
1150+
assert(gno < GNO_END);
11431151
Interval_iterator ivit(this, sidno);
11441152
Free_intervals_lock lock(this);
11451153
add_gno_interval(&ivit, gno, gno + 1, &lock);

sql/rpl_gtid_execution.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ bool set_gtid_next(THD *thd, const Gtid_specification &spec)
9393
assert(spec.type == GTID_GROUP);
9494
assert(spec.gtid.sidno >= 1);
9595
assert(spec.gtid.gno >= 1);
96+
assert(spec.gtid.gno < GNO_END);
9697
while (true)
9798
{
9899
// loop invariant: we should always hold global_sid_lock.rdlock
@@ -128,6 +129,7 @@ bool set_gtid_next(THD *thd, const Gtid_specification &spec)
128129
thd->variables.gtid_next= spec;
129130
assert(thd->owned_gtid.sidno >= 1);
130131
assert(thd->owned_gtid.gno >= 1);
132+
assert(thd->owned_gtid.gno < GNO_END);
131133
break;
132134
}
133135
// GTID owned by someone (other thread)

sql/rpl_gtid_owned.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ enum_return_status Owned_gtids::add_gtid_owner(const Gtid &gtid,
8787
{
8888
DBUG_ENTER("Owned_gtids::add_gtid_owner(Gtid, my_thread_id)");
8989
assert(gtid.sidno <= get_max_sidno());
90+
assert(gtid.gno > 0);
91+
assert(gtid.gno < GNO_END);
9092
Node *n= (Node *)my_malloc(key_memory_Sid_map_Node,
9193
sizeof(Node), MYF(MY_WME));
9294
if (n == NULL)

sql/rpl_gtid_set.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ rpl_gno parse_gno(const char **s)
449449
{
450450
char *endp;
451451
rpl_gno ret= my_strtoll(*s, &endp, 0);
452-
if (ret < 0 || ret == LLONG_MAX)
452+
if (ret < 0 || ret >= GNO_END)
453453
return -1;
454454
*s= endp;
455455
return ret;
@@ -790,11 +790,12 @@ void Gtid_set::remove_gtid_set(const Gtid_set *other)
790790
bool Gtid_set::contains_gtid(rpl_sidno sidno, rpl_gno gno) const
791791
{
792792
DBUG_ENTER("Gtid_set::contains_gtid");
793-
assert(sidno >= 1 && gno >= 1);
794793
if (sid_lock != NULL)
795794
sid_lock->assert_some_lock();
796795
if (sidno > get_max_sidno())
797796
DBUG_RETURN(false);
797+
assert(sidno >= 1);
798+
assert(gno >= 1);
798799
Const_interval_iterator ivit(this, sidno);
799800
const Interval *iv;
800801
while ((iv= ivit.get()) != NULL)
@@ -970,7 +971,8 @@ void Gtid_set::get_gtid_intervals(list<Gtid_interval> *gtid_intervals) const
970971
*/
971972
static size_t get_string_length(rpl_gno gno)
972973
{
973-
assert(gno >= 1 && gno < MAX_GNO);
974+
assert(gno >= 1);
975+
assert(gno < GNO_END);
974976
rpl_gno tmp_gno= gno;
975977
size_t len= 0;
976978
do

sql/rpl_gtid_state.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ rpl_gno Gtid_state::get_automatic_gno(rpl_sidno sidno) const
471471
while (true)
472472
{
473473
const Gtid_set::Interval *iv= ivit.get();
474-
rpl_gno next_interval_start= iv != NULL ? iv->start : MAX_GNO;
474+
rpl_gno next_interval_start= iv != NULL ? iv->start : GNO_END;
475475
while (next_candidate.gno < next_interval_start &&
476476
DBUG_EVALUATE_IF("simulate_gno_exhausted", false, true))
477477
{
@@ -486,7 +486,7 @@ rpl_gno Gtid_state::get_automatic_gno(rpl_sidno sidno) const
486486
my_error(ER_GNO_EXHAUSTED, MYF(0));
487487
DBUG_RETURN(-1);
488488
}
489-
if (next_candidate.gno <= iv->end)
489+
if (next_candidate.gno < iv->end)
490490
next_candidate.gno= iv->end;
491491
ivit.next();
492492
}

sql/rpl_slave.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8589,6 +8589,11 @@ bool queue_event(Master_info* mi,const char* buf, ulong event_len)
85898589
checksum_alg != binary_log::BINLOG_CHECKSUM_ALG_OFF ?
85908590
event_len - BINLOG_CHECKSUM_LEN : event_len,
85918591
mi->get_mi_description_event());
8592+
if (!gtid_ev.is_valid())
8593+
{
8594+
global_sid_lock->unlock();
8595+
goto err;
8596+
}
85928597
gtid.sidno= gtid_ev.get_sidno(false);
85938598
global_sid_lock->unlock();
85948599
if (gtid.sidno < 0)

0 commit comments

Comments
 (0)