From 422f3204efbbb27be9ad95355f69636114e7f907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 30 Aug 2022 12:02:56 +0300 Subject: [PATCH] MDEV-29409 Buffer overflow in my_wc_mb_filename() on RENAME TABLE dict_table_rename_in_cache(), dict_table_get_highest_foreign_id(): Reserve sufficient space for the fkid[] buffer, and ensure that the fkid[] will be NUL-terminated. The fkid[] must accommodate both the database name (which is already encoded in my_charset_filename) and the constraint name (which must be converted to my_charset_filename) so that we can check if it is in the format databasename/tablename_ibfk_1 (all encoded in my_charset_filename). --- .../innodb/r/foreign_key_not_windows.result | 10 +++++++++- .../innodb/t/foreign_key_not_windows.test | 19 ++++++++++++++++++- storage/innobase/dict/dict0dict.cc | 10 ++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/mysql-test/suite/innodb/r/foreign_key_not_windows.result b/mysql-test/suite/innodb/r/foreign_key_not_windows.result index 0dcc3d46bb1..aaff06f8d68 100644 --- a/mysql-test/suite/innodb/r/foreign_key_not_windows.result +++ b/mysql-test/suite/innodb/r/foreign_key_not_windows.result @@ -18,6 +18,14 @@ CREATE TABLE `d255`.`d245` (x INT) ENGINE=InnoDB; DROP TABLE `d255`.`d250`; RENAME TABLE `d250#`.`d245` TO `d250#`.`d250`; RENAME TABLE `d255`.`d250` TO a; -DROP DATABASE `d255`; DROP TABLE a,t; +# +# MDEV-29409 Buffer overflow in my_wc_mb_filename() on RENAME TABLE +# +CREATE TABLE `d255`.t(a INT PRIMARY KEY)ENGINE=InnoDB; +CREATE TABLE `d255`.u(a INT PRIMARY KEY, +CONSTRAINT `d320` FOREIGN KEY (a) REFERENCES `d255`.t (a)) ENGINE=InnoDB; +RENAME TABLE `d255`.u TO u; +DROP TABLE u; +DROP DATABASE `d255`; # End of 10.3 tests diff --git a/mysql-test/suite/innodb/t/foreign_key_not_windows.test b/mysql-test/suite/innodb/t/foreign_key_not_windows.test index 0e1e25d64b3..e5f42a0ddab 100644 --- a/mysql-test/suite/innodb/t/foreign_key_not_windows.test +++ b/mysql-test/suite/innodb/t/foreign_key_not_windows.test @@ -54,7 +54,24 @@ eval RENAME TABLE `$d255`.`$d245` TO `$d255`.`$d250`; --replace_result $d250 d250 $d255 d255 eval RENAME TABLE `$d255`.`$d250` TO a; --replace_result $d255 d255 -eval DROP DATABASE `$d255`; DROP TABLE a,t; +--echo # +--echo # MDEV-29409 Buffer overflow in my_wc_mb_filename() on RENAME TABLE +--echo # + +let $d225=#############################################; +let $d320=################################################################; + +--replace_result $d255 d255 +eval CREATE TABLE `$d255`.t(a INT PRIMARY KEY)ENGINE=InnoDB; +--replace_result $d255 d255 $d320 d320 +eval CREATE TABLE `$d255`.u(a INT PRIMARY KEY, +CONSTRAINT `$d320` FOREIGN KEY (a) REFERENCES `$d255`.t (a)) ENGINE=InnoDB; +--replace_result $d255 d255 +eval RENAME TABLE `$d255`.u TO u; +DROP TABLE u; +--replace_result $d255 d255 +eval DROP DATABASE `$d255`; + --echo # End of 10.3 tests diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index d714e5f815b..eb0f10d55f4 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1595,7 +1595,7 @@ dict_table_rename_in_cache( in UTF-8 charset. The variable fkid here is used to store foreign key constraint name in charset my_charset_filename for comparison further below. */ - char fkid[MAX_TABLE_NAME_LEN+20]; + char fkid[MAX_TABLE_NAME_LEN * 2 + 20]; ibool on_tmp = FALSE; /* The old table name in my_charset_filename is stored @@ -1629,7 +1629,8 @@ dict_table_rename_in_cache( } } - strncpy(fkid, foreign->id, MAX_TABLE_NAME_LEN); + strncpy(fkid, foreign->id, (sizeof fkid) - 1); + fkid[(sizeof fkid) - 1] = '\0'; if (strstr(fkid, TEMP_TABLE_PATH_PREFIX) == NULL) { innobase_convert_to_filename_charset( @@ -3671,10 +3672,11 @@ dict_table_get_highest_foreign_id( for (dict_foreign_set::iterator it = table->foreign_set.begin(); it != table->foreign_set.end(); ++it) { - char fkid[MAX_TABLE_NAME_LEN+20]; + char fkid[MAX_TABLE_NAME_LEN * 2 + 20]; foreign = *it; - strcpy(fkid, foreign->id); + strncpy(fkid, foreign->id, (sizeof fkid) - 1); + fkid[(sizeof fkid) - 1] = '\0'; /* Convert foreign key identifier on dictionary memory cache to filename charset. */ innobase_convert_to_filename_charset(