Skip to content

Commit 2d1e019

Browse files
Thirunarayanangrooverdan
authored andcommitted
MDEV-36871 mariadb-backup incremental segfault querying mariadb_backup_history
Problem: ========= (1) Mariabackup tries to read the history data from mysql.mariadb_backup_history and fails with segfault. Reason is that mariabackup does force innodb_log_checkpoint_now from commit 652f33e(MDEV-30000). Mariabackup sends the "innodb_log_checkpoint_now=1" query to server and reads the result set for the query later in the code because the query may trigger the page thread to flush the pages. But before reading the query result for innodb_log_checkpoint_now=1, mariabackup does execute the select query for the history table (mysql.mariadb_backup_history) and wrongly reads the query result of innodb_log_checkpoint_now. This leads to assertion in mariabackup. (2) The recording of incremental backups has the format as "tar" when mbstream was used. The xb_stream_fmt_t only had XB_STREAM_FMT_NONE and XB_STREAM_FMT_XBSTREAM and hence in the mysql.mariadb_backup_history table the format was recorded as "tar" for the "mbstream" due to the offset in the xb_stream_name array within mariadb-backup. (3) Also under Windows the full path of mariabackup was recorded in the the history. (4) select_incremental_lsn_from_history(): Name of the backup and UUID of the history record variable could lead to buffer overflow while copying the variable value from global variable. Solution: ========= (1) Move the reading of history data from mysql.mariadb_backup_history after reading the result of innodb_log_checkpoint_now=1 query (2) We've removed the "tar" element from the xb_stream_name. As the "xbstream" was never used, the format name is changed to mbstream. As the table needs alteration the "mbstream" appended instead of the unused xbstream in the table. "tar" is left in the enum as the previous recordings are still possible. (3) The Windows path separator is used to store just the executable name as the tool in the mariadb_backup_history table. (4) select_incremental_lsn_from_history(): Check and validate the length of incremental history name and incremental history uuid before copying into temporary buffer Thanks to Daniel black for contributing the code for solution (2) and (3)
1 parent 0931617 commit 2d1e019

File tree

5 files changed

+98
-17
lines changed

5 files changed

+98
-17
lines changed

extra/mariabackup/backup_mysql.cc

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -561,12 +561,36 @@ select_incremental_lsn_from_history(lsn_t *incremental_lsn)
561561
{
562562
MYSQL_RES *mysql_result;
563563
char query[1000];
564-
char buf[100];
564+
char buf[NAME_LEN*2+3];
565+
566+
size_t opt_incremental_history_name_len= 0;
567+
size_t opt_incremental_history_uuid_len= 0;
568+
569+
if (opt_incremental_history_name)
570+
opt_incremental_history_name_len=
571+
strlen(opt_incremental_history_name);
572+
573+
if (opt_incremental_history_uuid)
574+
opt_incremental_history_uuid_len=
575+
strlen(opt_incremental_history_uuid);
576+
577+
if (opt_incremental_history_name_len*2 > sizeof(buf))
578+
die("Incremental history table name '%s' is too long.",
579+
opt_incremental_history_name);
580+
581+
if (opt_incremental_history_uuid_len*2 > sizeof(buf))
582+
die("Incremental history uuid '%s' is too long.",
583+
opt_incremental_history_uuid);
584+
585+
if (opt_incremental_history_name && opt_incremental_history_name[0]
586+
&& opt_incremental_history_uuid && opt_incremental_history_uuid[0])
587+
die("It is allowed to use either --incremental-history-name "
588+
"or --incremental-history-uuid, but not both.");
565589

566590
if (opt_incremental_history_name) {
567591
mysql_real_escape_string(mysql_connection, buf,
568592
opt_incremental_history_name,
569-
(unsigned long)strlen(opt_incremental_history_name));
593+
(unsigned long) opt_incremental_history_name_len);
570594
snprintf(query, sizeof(query),
571595
"SELECT innodb_to_lsn "
572596
"FROM " XB_HISTORY_TABLE " "
@@ -575,11 +599,10 @@ select_incremental_lsn_from_history(lsn_t *incremental_lsn)
575599
"ORDER BY innodb_to_lsn DESC LIMIT 1",
576600
buf);
577601
}
578-
579-
if (opt_incremental_history_uuid) {
602+
else if (opt_incremental_history_uuid) {
580603
mysql_real_escape_string(mysql_connection, buf,
581604
opt_incremental_history_uuid,
582-
(unsigned long)strlen(opt_incremental_history_uuid));
605+
(unsigned long) opt_incremental_history_uuid_len);
583606
snprintf(query, sizeof(query),
584607
"SELECT innodb_to_lsn "
585608
"FROM " XB_HISTORY_TABLE " "
@@ -589,6 +612,8 @@ select_incremental_lsn_from_history(lsn_t *incremental_lsn)
589612
buf);
590613
}
591614

615+
/* xb_mysql_query(..,.., true) will die on error, so
616+
mysql_result can't be nullptr */
592617
mysql_result = xb_mysql_query(mysql_connection, query, true);
593618

594619
ut_ad(mysql_num_fields(mysql_result) == 1);
@@ -1689,7 +1714,7 @@ write_xtrabackup_info(ds_ctxt *datasink,
16891714
char buf_end_time[100];
16901715
tm tm;
16911716
std::ostringstream oss;
1692-
const char *xb_stream_name[] = {"file", "tar", "xbstream"};
1717+
const char *xb_stream_name[] = {"file", "mbstream"};
16931718

16941719
uuid = read_mysql_one_value(connection, "SELECT UUID()");
16951720
server_version = read_mysql_one_value(connection, "SELECT VERSION()");
@@ -1772,6 +1797,10 @@ write_xtrabackup_info(ds_ctxt *datasink,
17721797
goto cleanup;
17731798
}
17741799

1800+
xb_mysql_query(connection,
1801+
"ALTER TABLE IF EXISTS " XB_HISTORY_TABLE
1802+
" MODIFY format ENUM('file', 'tar', 'mbstream') DEFAULT NULL", false);
1803+
17751804
xb_mysql_query(connection,
17761805
"CREATE TABLE IF NOT EXISTS " XB_HISTORY_TABLE "("
17771806
"uuid VARCHAR(40) NOT NULL PRIMARY KEY,"
@@ -1789,7 +1818,7 @@ write_xtrabackup_info(ds_ctxt *datasink,
17891818
"innodb_to_lsn BIGINT UNSIGNED DEFAULT NULL,"
17901819
"partial ENUM('Y', 'N') DEFAULT NULL,"
17911820
"incremental ENUM('Y', 'N') DEFAULT NULL,"
1792-
"format ENUM('file', 'tar', 'xbstream') DEFAULT NULL,"
1821+
"format ENUM('file', 'tar', 'mbstream') DEFAULT NULL,"
17931822
"compressed ENUM('Y', 'N') DEFAULT NULL"
17941823
") CHARACTER SET utf8 ENGINE=innodb", false);
17951824

@@ -1940,7 +1969,7 @@ void
19401969
capture_tool_command(int argc, char **argv)
19411970
{
19421971
/* capture tool name tool args */
1943-
tool_name = strrchr(argv[0], '/');
1972+
tool_name = strrchr(argv[0], IF_WIN('\\', '/'));
19441973
tool_name = tool_name ? tool_name + 1 : argv[0];
19451974

19461975
make_argv(tool_args, sizeof(tool_args), argc, argv);

extra/mariabackup/xtrabackup.cc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5520,10 +5520,6 @@ static bool xtrabackup_backup_func()
55205520

55215521
backup_datasinks.init();
55225522

5523-
if (!select_history()) {
5524-
goto fail;
5525-
}
5526-
55275523
/* open the log file */
55285524
memset(&stat_info, 0, sizeof(MY_STAT));
55295525
dst_log_file = ds_open(backup_datasinks.m_redo, LOG_FILE_NAME, &stat_info);
@@ -5538,6 +5534,11 @@ static bool xtrabackup_backup_func()
55385534
if (innodb_log_checkpoint_now != false) {
55395535
mysql_read_query_result(mysql_connection);
55405536
}
5537+
5538+
if (!select_history()) {
5539+
goto fail;
5540+
}
5541+
55415542
/* label it */
55425543
recv_sys.file_checkpoint = log_sys.next_checkpoint_lsn;
55435544
log_hdr_init();

mysql-test/suite/mariabackup/xb_history.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mariadb_backup_history CREATE TABLE `mariadb_backup_history` (
1717
`innodb_to_lsn` bigint(20) unsigned DEFAULT NULL,
1818
`partial` enum('Y','N') DEFAULT NULL,
1919
`incremental` enum('Y','N') DEFAULT NULL,
20-
`format` enum('file','tar','xbstream') DEFAULT NULL,
20+
`format` enum('file','tar','mbstream') DEFAULT NULL,
2121
`compressed` enum('Y','N') DEFAULT NULL,
2222
PRIMARY KEY (`uuid`)
2323
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci
Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,37 @@
11
CREATE TABLE t(i INT) ENGINE INNODB;
22
INSERT INTO t VALUES(1);
3-
# xtrabackup backup to stream
3+
# xtrabackup full backup to stream
4+
INSERT INTO t VALUES(2), (3), (4);
5+
# xtrabackup incremental backup to stream
6+
# checking recording of history
7+
SELECT tool_name, name, partial, incremental, format, compressed
8+
FROM mysql.mariadb_backup_history
9+
ORDER BY innodb_from_lsn;
10+
tool_name mariabackup
11+
name fullback
12+
partial Y
13+
incremental N
14+
format mbstream
15+
compressed N
16+
tool_name mariabackup
17+
name incr_1
18+
partial N
19+
incremental Y
20+
format mbstream
21+
compressed N
422
# xbstream extract
523
# xtrabackup prepare
24+
# xbstream extract for incremental backup
25+
# xtrabackup incremental prepare
626
# shutdown server
727
# remove datadir
828
# xtrabackup move back
929
# restart
1030
SELECT * FROM t;
1131
i
1232
1
33+
2
34+
3
35+
4
1336
DROP TABLE t;
37+
DROP TABLE mysql.mariadb_backup_history;

mysql-test/suite/mariabackup/xbstream.test

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,48 @@ CREATE TABLE t(i INT) ENGINE INNODB;
44
INSERT INTO t VALUES(1);
55

66
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
7+
let $incr_dir=$MYSQLTEST_VARDIR/tmp/backup_incr;
78
mkdir $targetdir;
9+
mkdir $incr_dir;
10+
811
let $streamfile=$MYSQLTEST_VARDIR/tmp/backup.xb;
12+
let $stream_incr_file=$MYSQLTEST_VARDIR/tmp/backup_incr.xb;
13+
14+
echo # xtrabackup full backup to stream;
15+
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --parallel=10 --databases-exclude=foobar --history=fullback --stream=xbstream > $streamfile 2>$targetdir/backup_stream.log;
16+
17+
INSERT INTO t VALUES(2), (3), (4);
18+
19+
echo # xtrabackup incremental backup to stream;
20+
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --incremental-history-name=fullback --history=incr_1 --stream=xbstream > $stream_incr_file 2>$targetdir/backup_incr.log;
21+
22+
echo # checking recording of history;
23+
--replace_result mariabackup.exe mariabackup
24+
--vertical_results
25+
SELECT tool_name, name, partial, incremental, format, compressed
26+
FROM mysql.mariadb_backup_history
27+
ORDER BY innodb_from_lsn;
28+
29+
--horizontal_results
930

10-
echo # xtrabackup backup to stream;
11-
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --parallel=10 --databases-exclude=foobar --stream=xbstream > $streamfile 2>$targetdir/backup_stream.log;
1231
echo # xbstream extract;
1332
--disable_result_log
1433
exec $XBSTREAM -x -C $targetdir < $streamfile;
1534

1635
echo # xtrabackup prepare;
1736
exec $XTRABACKUP --prepare --target-dir=$targetdir;
1837

38+
echo # xbstream extract for incremental backup;
39+
exec $XBSTREAM -x -C $incr_dir < $stream_incr_file;
40+
41+
echo # xtrabackup incremental prepare;
42+
exec $XTRABACKUP --prepare --target-dir=$targetdir --incremental-dir=$incr_dir;
43+
1944
-- source include/restart_and_restore.inc
2045
--enable_result_log
2146
SELECT * FROM t;
2247
DROP TABLE t;
48+
DROP TABLE mysql.mariadb_backup_history;
2349
rmdir $targetdir;
24-
50+
remove_file $streamfile;
51+
remove_file $stream_incr_file;

0 commit comments

Comments
 (0)