diff --git a/mysql-test/main/mdev_38010.result b/mysql-test/main/mdev_38010.result new file mode 100644 index 0000000000000..8f3644cfa5ff3 --- /dev/null +++ b/mysql-test/main/mdev_38010.result @@ -0,0 +1,17 @@ +include/master-slave.inc +[connection master] +connection slave; +include/stop_slave.inc +call mtr.add_suppression("Error reading master configuration"); +call mtr.add_suppression("Failed to initialize the master info structure"); +call mtr.add_suppression("error connecting to master"); +include/rpl_restart_server.inc [server_number=2] +connection slave; +# Verifying that trailing garbage causes parsing to fail. +# Rejection successful. +# Restoring the slave to original replication state +include/rpl_restart_server.inc [server_number=2] +CHANGE MASTER TO MASTER_HOST='MASTER_HOST', MASTER_PORT=MASTER_PORT, MASTER_USER='MASTER_USER'; +include/start_slave.inc +connection master; +include/rpl_end.inc diff --git a/mysql-test/main/mdev_38010.test b/mysql-test/main/mdev_38010.test new file mode 100644 index 0000000000000..d99905928e7d6 --- /dev/null +++ b/mysql-test/main/mdev_38010.test @@ -0,0 +1,89 @@ +# +# MDEV-38010: Trailing garbage in master.info numeric fields leads to corruption. +# This test verifis that the server strictly validates numeric lines in master.info. +# If trailing non-whitespace characters are found, the line is rejected as corrupted. +# + +--source include/master-slave.inc +--source include/have_binlog_format_mixed.inc + +--connection slave + +# Step1: Capture the current working replication state. +# We need these to restore a clean environment after we've intentionally +# "broken" the master.info file for the test. +--let $master_host= query_get_value(SHOW SLAVE STATUS, Master_Host, 1) +--let $master_port= query_get_value(SHOW SLAVE STATUS, Master_Port, 1) +--let $master_user= query_get_value(SHOW SLAVE STATUS, Master_User, 1) + +--source include/stop_slave.inc + +# Suppress the err we expect to see in the log during this test. +# The server will log these when it hits our new strict validation logic. +call mtr.add_suppression("Error reading master configuration"); +call mtr.add_suppression("Failed to initialize the master info structure"); +call mtr.add_suppression("error connecting to master"); + +--let $MYSQLD_DATADIR= `select @@datadir` + +# Step2: Simulate a corrupted master.info file. +# We manually rewrite the file with "garbage" appended to numeric fields +# (Port and Connect_Retry) to trigger the new validation check. +--remove_file $MYSQLD_DATADIR/master.info +--write_file $MYSQLD_DATADIR/master.info +17 +master-bin.000001 +4 +127.0.0.1 +root + +3306abcdefghijklmnopqrstuvwxyz&10 +60 +0 +0 + + + +0 + +60.000abcdefghijklm +1 1 +EOF + +# Step3: Restart the slave to force it to parse the corrupted file. +--let $rpl_server_number= 2 +--source include/rpl_restart_server.inc + +--connection slave + +--echo # Verifying that trailing garbage causes parsing to fail. +# If the fix works, the server should reject the bad port "3306abcdef..." +# This may lead to Master_Port being unset or the slave failing to show up. +--let $port= query_get_value(SHOW SLAVE STATUS, Master_Port, 1) + +if ($port != 3306) { + if ($port != No such row) { + if ($port != "") { + --echo Error: The garbage in master.info was accepted! Port evaluated to $port. + --die "Test failed: Fix is not preventing garbage parsing." + } + } +} +--echo # Rejection successful. + +# Step4: Cleanup and Restoration. +# We remove the bad master.info and use CHANGE MASTER TO with our saved +# parameters to restore the slave to its original state. +--remove_file $MYSQLD_DATADIR/master.info + +--echo # Restoring the slave to original replication state +--let $rpl_server_number= 2 +--source include/rpl_restart_server.inc + +--replace_result $master_port MASTER_PORT $master_host MASTER_HOST $master_user MASTER_USER +--eval CHANGE MASTER TO MASTER_HOST='$master_host', MASTER_PORT=$master_port, MASTER_USER='$master_user' +--source include/start_slave.inc + +--connection master +--source include/rpl_end.inc + diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index bb77c46d57f57..85d07d2240bd5 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -49,6 +49,8 @@ Master_info::Master_info(LEX_CSTRING *connection_name_arg, { char *tmp; host[0] = 0; user[0] = 0; password[0] = 0; + master_log_pos = 0; + master_log_name[0] = 0; ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0; ssl_cipher[0]= 0; ssl_key[0]= 0; ssl_crl[0]= 0; ssl_crlpath[0]= 0; diff --git a/sql/slave.cc b/sql/slave.cc index ce18874167013..a887d1e11d594 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1699,26 +1699,36 @@ int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, DBUG_RETURN(1); } -/* - when moving these functions to mysys, don't forget to - remove slave.cc from libmysqld/CMakeLists.txt -*/ + + int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) { char buf[32]; DBUG_ENTER("init_intvar_from_file"); + *var= default_val; if (my_b_gets(f, buf, sizeof(buf))) { - *var = atoi(buf); + char *endptr= buf + strlen(buf); + int error= 0; + longlong val= my_strtoll10(buf, &endptr, &error); + + if (error && error != MY_ERRNO_EDOM) + DBUG_RETURN(1); + + while (my_isspace(&my_charset_latin1, *endptr)) + endptr++; + if (*endptr != '\0') + DBUG_RETURN(1); + + *var= (int) val; DBUG_RETURN(0); } - else if (default_val) - { - *var = default_val; + + if (default_val) DBUG_RETURN(0); - } + DBUG_RETURN(1); } @@ -1726,20 +1736,30 @@ int init_ulonglongvar_from_file(ulonglong* var, IO_CACHE* f, ulonglong default_val) { char buf[MY_INT64_NUM_DECIMAL_DIGITS]; - int error; + int error= 0; DBUG_ENTER("init_ulonglongvar_from_file"); + *var= default_val; if (my_b_gets(f, buf, sizeof(buf))) { - *var = (ulonglong) my_strtoll10(buf, (char**) 0, &error); + char *endptr= buf + strlen(buf); + *var= (ulonglong) my_strtoll10(buf, &endptr, &error); + + if (error && error != MY_ERRNO_EDOM) + DBUG_RETURN(1); + + while (my_isspace(&my_charset_latin1, *endptr)) + endptr++; + if (*endptr != '\0') + DBUG_RETURN(1); + DBUG_RETURN(0); } - else if (default_val) - { - *var = default_val; + + if (default_val) DBUG_RETURN(0); - } + DBUG_RETURN(1); } @@ -1748,19 +1768,28 @@ int init_floatvar_from_file(float* var, IO_CACHE* f, float default_val) char buf[16]; DBUG_ENTER("init_floatvar_from_file"); + *var= default_val; if (my_b_gets(f, buf, sizeof(buf))) { - if (sscanf(buf, "%f", var) != 1) + char *endptr= buf + strlen(buf); + int error= 0; + double val= my_strtod(buf, &endptr, &error); + if (error && error != MY_ERRNO_EDOM) DBUG_RETURN(1); - else - DBUG_RETURN(0); - } - else if (default_val != 0.0) - { - *var = default_val; + + while (my_isspace(&my_charset_latin1, *endptr)) + endptr++; + if (*endptr != '\0') + DBUG_RETURN(1); + + *var= (float) val; DBUG_RETURN(0); } + + if (default_val != 0.0) + DBUG_RETURN(0); + DBUG_RETURN(1); }