diff --git a/mysql-test/r/frm_bad_row_type-7333.result b/mysql-test/r/frm_bad_row_type-7333.result new file mode 100644 index 00000000000..198b494d3f8 --- /dev/null +++ b/mysql-test/r/frm_bad_row_type-7333.result @@ -0,0 +1,14 @@ +call mtr.add_suppression("bad_row_type.frm: invalid value 11 for the field row_format"); +select * from bad_row_type; +category_id category_name +show create table bad_row_type; +Table Create Table +bad_row_type CREATE TABLE `bad_row_type` ( + `category_id` int(11) NOT NULL AUTO_INCREMENT, + `category_name` varchar(255) DEFAULT NULL, + PRIMARY KEY (`category_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 +show table status like 'bad_row_type'; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +bad_row_type MyISAM 10 Dynamic 0 0 0 281474976710655 1024 0 1 x x NULL utf8_general_ci NULL +drop table bad_row_type; diff --git a/mysql-test/std_data/bad_row_type.MYD b/mysql-test/std_data/bad_row_type.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/bad_row_type.MYI b/mysql-test/std_data/bad_row_type.MYI new file mode 100644 index 00000000000..731a83ed078 Binary files /dev/null and b/mysql-test/std_data/bad_row_type.MYI differ diff --git a/mysql-test/std_data/bad_row_type.frm b/mysql-test/std_data/bad_row_type.frm new file mode 100644 index 00000000000..d993c98d73e Binary files /dev/null and b/mysql-test/std_data/bad_row_type.frm differ diff --git a/mysql-test/t/frm_bad_row_type-7333.test b/mysql-test/t/frm_bad_row_type-7333.test new file mode 100644 index 00000000000..5100a85cb22 --- /dev/null +++ b/mysql-test/t/frm_bad_row_type-7333.test @@ -0,0 +1,14 @@ +# +# MDEV-7333 "'show table status like 'table_name'" on tokudb table lead to MariaDB crash +# +let $datadir= `select @@datadir`; +call mtr.add_suppression("bad_row_type.frm: invalid value 11 for the field row_format"); +copy_file std_data/bad_row_type.MYD $datadir/test/bad_row_type.MYD; +copy_file std_data/bad_row_type.MYI $datadir/test/bad_row_type.MYI; +copy_file std_data/bad_row_type.frm $datadir/test/bad_row_type.frm; + +select * from bad_row_type; +show create table bad_row_type; +replace_column 12 x 13 x; +show table status like 'bad_row_type'; +drop table bad_row_type; diff --git a/sql/handler.h b/sql/handler.h index 5c902e604e9..8f8b4fd93af 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -373,6 +373,9 @@ enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, /** Unused. Reserved for future versions. */ ROW_TYPE_PAGE }; +/* not part of the enum, so that it shouldn't be in switch(row_type) */ +#define ROW_TYPE_MAX ((uint)ROW_TYPE_PAGE + 1) + enum enum_binlog_func { BFN_RESET_LOGS= 1, BFN_RESET_SLAVE= 2, @@ -1248,7 +1251,7 @@ class partition_info; struct st_partition_iter; #define NOT_A_PARTITION_ID ((uint32)-1) -enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES }; +enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES, HA_CHOICE_MAX }; typedef struct st_ha_create_information { diff --git a/sql/table.cc b/sql/table.cc index 4ce70987334..a4cf9939845 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -890,6 +890,23 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo, return 0; } +/** ensures that the enum value (read from frm) is within limits + + if not - issues a warning and resets the value to 0 + (that is, 0 is assumed to be a default value) +*/ +static uint enum_value_with_check(THD *thd, TABLE_SHARE *share, + const char *name, uint value, uint limit) +{ + if (value < limit) + return value; + + sql_print_warning("%s.frm: invalid value %d for the field %s", + share->normalized_path.str, value, name); + return 0; +} + + /* Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE */ @@ -983,9 +1000,12 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!head[32]) // New frm file in 3.23 { share->avg_row_length= uint4korr(head+34); - share->transactional= (ha_choice) (head[39] & 3); - share->page_checksum= (ha_choice) ((head[39] >> 2) & 3); - share->row_type= (row_type) head[40]; + share->transactional= (ha_choice) + enum_value_with_check(thd, share, "transactional", (head[39] & 3), HA_CHOICE_MAX); + share->page_checksum= (ha_choice) + enum_value_with_check(thd, share, "page_checksum", (head[39] >> 2) & 3, HA_CHOICE_MAX); + share->row_type= (row_type) + enum_value_with_check(thd, share, "row_format", head[40], ROW_TYPE_MAX); share->table_charset= get_charset((((uint) head[41]) << 8) + (uint) head[38],MYF(0)); share->null_field_first= 1;