Skip to content

Commit 3ba276c

Browse files
committed
MDEV-38010: Fix trailing garbage in master.info causing data corruption
Modified init_intvar_from_file and init_floatvar_from_file to detect and reject trailing garbage in master.info numeric fields. Added a test case to verify the fix and ensure replication is correctly restored.
1 parent b0ee381 commit 3ba276c

3 files changed

Lines changed: 120 additions & 8 deletions

File tree

mysql-test/main/mdev_38010.result

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
include/master-slave.inc
2+
[connection master]
3+
connection slave;
4+
include/stop_slave.inc
5+
call mtr.add_suppression("Error reading master configuration");
6+
call mtr.add_suppression("Failed to initialize the master info structure");
7+
call mtr.add_suppression("error connecting to master");
8+
include/rpl_restart_server.inc [server_number=2]
9+
connection slave;
10+
# Verifying that trailing garbage causes parsing to fail.
11+
# Restoring the slave to original replication state
12+
include/rpl_restart_server.inc [server_number=2]
13+
CHANGE MASTER TO MASTER_HOST='MASTER_HOST', MASTER_PORT=MASTER_PORT, MASTER_USER='MASTER_USER';
14+
include/start_slave.inc
15+
connection master;
16+
include/rpl_end.inc

mysql-test/main/mdev_38010.test

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#
2+
# MDEV-38010: Trailing garbage in master.info numeric fields leads to corruption.
3+
# This test verifis that the server strictly validates numeric lines in master.info.
4+
# If trailing non-whitespace characters are found, the line is rejected as corrupted.
5+
#
6+
7+
--source include/master-slave.inc
8+
--source include/have_binlog_format_mixed.inc
9+
10+
--connection slave
11+
12+
# Step1: Capture the current working replication state.
13+
# We need these to restore a clean environment after we've intentionally
14+
# "broken" the master.info file for the test.
15+
--let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1)
16+
--let $master_port= query_get_value(SHOW SLAVE STATUS, Master_Port, 1)
17+
--let $master_user= query_get_value(SHOW SLAVE STATUS, Master_User, 1)
18+
19+
--source include/stop_slave.inc
20+
21+
# Suppress the err we expect to see in the log during this test.
22+
# The server will log these when it hits our new strict validation logic.
23+
call mtr.add_suppression("Error reading master configuration");
24+
call mtr.add_suppression("Failed to initialize the master info structure");
25+
call mtr.add_suppression("error connecting to master");
26+
27+
--let $MYSQLD_DATADIR= `select @@datadir`
28+
29+
# Step2: Simulate a corrupted master.info file.
30+
# We manually rewrite the file with "garbage" appended to numeric fields
31+
# (Port and Connect_Retry) to trigger the new validation check.
32+
--remove_file $MYSQLD_DATADIR/master.info
33+
--write_file $MYSQLD_DATADIR/master.info
34+
17
35+
master-bin.000001
36+
4
37+
127.0.0.1
38+
root
39+
40+
3306abcdefghijklmnopqrstuvwxyz&10
41+
60
42+
0
43+
0
44+
45+
46+
47+
0
48+
49+
60.000abcdefghijklm
50+
1 1
51+
EOF
52+
53+
# Step3: Restart the slave to force it to parse the corrupted file.
54+
--let $rpl_server_number= 2
55+
--source include/rpl_restart_server.inc
56+
57+
--connection slave
58+
59+
--echo # Verifying that trailing garbage causes parsing to fail.
60+
# If the fix works, the server should reject the bad port "3306abcdef..."
61+
# and fall back to the compiled-in default (3306).
62+
--let $port= query_get_value(SHOW SLAVE STATUS, Master_Port, 1)
63+
64+
if ($port != 3306) {
65+
--echo Error: The garbage in master.info was accepted! Port evaluated to $port.
66+
--die "Test failed: Fix is not preventing garbage parsing."
67+
}
68+
69+
# Step4: Cleanup and Restoration.
70+
# We remove the bad master.info and use CHANGE MASTER TO with our saved
71+
# parameters to restore the slave to its original state.
72+
--remove_file $MYSQLD_DATADIR/master.info
73+
74+
--echo # Restoring the slave to original replication state
75+
--let $rpl_server_number= 2
76+
--source include/rpl_restart_server.inc
77+
78+
--replace_result $master_port MASTER_PORT $master_host MASTER_HOST $master_user MASTER_USER
79+
--eval CHANGE MASTER TO MASTER_HOST='$master_host', MASTER_PORT=$master_port, MASTER_USER='$master_user'
80+
--source include/start_slave.inc
81+
82+
--connection master
83+
--source include/rpl_end.inc
84+

sql/slave.cc

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,10 +1585,7 @@ int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
15851585
DBUG_RETURN(1);
15861586
}
15871587

1588-
/*
1589-
when moving these functions to mysys, don't forget to
1590-
remove slave.cc from libmysqld/CMakeLists.txt
1591-
*/
1588+
15921589
int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
15931590
{
15941591
char buf[32];
@@ -1597,7 +1594,15 @@ int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
15971594

15981595
if (my_b_gets(f, buf, sizeof(buf)))
15991596
{
1600-
*var = atoi(buf);
1597+
char *endptr;
1598+
long val= strtol(buf, &endptr, 10);
1599+
1600+
while (my_isspace(&my_charset_bin, *endptr))
1601+
endptr++;
1602+
if (*endptr != '\0')
1603+
DBUG_RETURN(1);
1604+
1605+
*var= (int) val;
16011606
DBUG_RETURN(0);
16021607
}
16031608
else if (default_val)
@@ -1616,10 +1621,17 @@ int init_floatvar_from_file(float* var, IO_CACHE* f, float default_val)
16161621

16171622
if (my_b_gets(f, buf, sizeof(buf)))
16181623
{
1619-
if (sscanf(buf, "%f", var) != 1)
1624+
int bytes_read= 0;
1625+
if (sscanf(buf, "%f%n", var, &bytes_read) != 1)
16201626
DBUG_RETURN(1);
1621-
else
1622-
DBUG_RETURN(0);
1627+
1628+
char *endptr= buf + bytes_read;
1629+
while (my_isspace(&my_charset_bin, *endptr))
1630+
endptr++;
1631+
if (*endptr != '\0')
1632+
DBUG_RETURN(1);
1633+
1634+
DBUG_RETURN(0);
16231635
}
16241636
else if (default_val != 0.0)
16251637
{

0 commit comments

Comments
 (0)