revision-id: eb3e9cdbe86b5a68c3ec8a9f26165259e4ca7eec (mariadb-10.3.6-125-geb3e9cd) parent(s): bd09c5ca866e273b6cebbd9a15c51c82bfa3ac9b committer: Alexey Botchkov timestamp: 2018-05-08 14:19:55 +0400 message: MDEV-15813 ASAN use-after-poison in hp_hashnr upon HANDLER READ on a versioned HEAP table. Returns an error if we try to use a key in a query it declared unable to handle. --- mysql-test/suite/heap/heap_hash.result | 19 +++++++++++++++++++ mysql-test/suite/heap/heap_hash.test | 18 ++++++++++++++++++ sql/share/errmsg-utf8.txt | 2 ++ sql/sql_handler.cc | 29 +++++++++++++++++++++++++---- sql/sql_handler.h | 3 ++- sql/sql_prepare.cc | 1 + 6 files changed, 67 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/heap/heap_hash.result b/mysql-test/suite/heap/heap_hash.result index 55d4358..cba39c1 100644 --- a/mysql-test/suite/heap/heap_hash.result +++ b/mysql-test/suite/heap/heap_hash.result @@ -467,3 +467,22 @@ c1 bar2 DROP TABLE t1; End of 5.5 tests +# +# MDEV-15813 ASAN use-after-poison in hp_hashnr upon +# HANDLER READ on a versioned HEAP table +# +CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, CONSTRAINT PRIMARY KEY (a, b), UNIQUE ba(b, a) USING HASH) ENGINE=HEAP; +INSERT INTO t1 VALUES (1, 10), (2, 20), (3,30), (4,40); +HANDLER t1 OPEN AS m; +HANDLER m READ `PRIMARY`= (3,30); +a b +3 30 +HANDLER m READ `PRIMARY`> (3,30); +ERROR HY000: HASH index `PRIMARY` does not support this operation +HANDLER m READ `ba`= (30,3); +a b +3 30 +HANDLER m READ `ba`= (30); +ERROR HY000: HASH index `ba` does not support this operation +HANDLER m CLOSE; +DROP TABLE t1; diff --git a/mysql-test/suite/heap/heap_hash.test b/mysql-test/suite/heap/heap_hash.test index 3fe95e1..5a08c6d 100644 --- a/mysql-test/suite/heap/heap_hash.test +++ b/mysql-test/suite/heap/heap_hash.test @@ -347,3 +347,21 @@ SELECT * FROM t1 WHERE c1='bar2'; DROP TABLE t1; --echo End of 5.5 tests + +--echo # +--echo # MDEV-15813 ASAN use-after-poison in hp_hashnr upon +--echo # HANDLER READ on a versioned HEAP table +--echo # + +CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, CONSTRAINT PRIMARY KEY (a, b), UNIQUE ba(b, a) USING HASH) ENGINE=HEAP; +INSERT INTO t1 VALUES (1, 10), (2, 20), (3,30), (4,40); +HANDLER t1 OPEN AS m; +HANDLER m READ `PRIMARY`= (3,30); +--error ER_KEY_DOESNT_SUPPORT +HANDLER m READ `PRIMARY`> (3,30); +HANDLER m READ `ba`= (30,3); +--error ER_KEY_DOESNT_SUPPORT +HANDLER m READ `ba`= (30); +HANDLER m CLOSE; +DROP TABLE t1; + diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index c55ac4f..7446a60 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7915,3 +7915,5 @@ ER_UPDATED_COLUMN_ONLY_ONCE eng "The column %`s.%`s cannot be changed more than once in a single UPDATE statement" ER_EMPTY_ROW_IN_TVC eng "Row with no elements is not allowed in table value constructor in this context" +ER_KEY_DOESNT_SUPPORT + eng "%s index %`s does not support this operation" diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 09883d8..12e4b5a 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -618,7 +618,7 @@ static SQL_HANDLER *mysql_ha_find_handler(THD *thd, const LEX_CSTRING *name) static bool mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, enum enum_ha_read_modes mode, const char *keyname, - List<Item> *key_expr, + List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode, Item *cond, bool in_prepare) { THD *thd= handler->thd; @@ -661,12 +661,30 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, key_part_map keypart_map; uint key_len; + if (ha_rkey_mode != HA_READ_KEY_EXACT && + (table->file->index_flags(handler->keyno, 0, TRUE) & + (HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE)) == 0) + { + my_error(ER_KEY_DOESNT_SUPPORT, MYF(0), + table->file->index_type(handler->keyno), keyinfo->name); + return 1; + } + if (key_expr->elements > keyinfo->user_defined_key_parts) { my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->user_defined_key_parts); return 1; } + else if (key_expr->elements < keyinfo->user_defined_key_parts && + (table->file->index_flags(handler->keyno, 0, TRUE) & + HA_ONLY_WHOLE_INDEX)) + { + my_error(ER_KEY_DOESNT_SUPPORT, MYF(0), + table->file->index_type(handler->keyno), keyinfo->name); + return 1; + } + for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++) { my_bitmap_map *old_map; @@ -841,7 +859,8 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, goto err0; // mysql_lock_tables() printed error message already } - if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 0)) + if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, + ha_rkey_mode, cond, 0)) goto err; mode= handler->mode; keyno= handler->keyno; @@ -1002,14 +1021,16 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables, enum enum_ha_read_modes mode, const char *keyname, - List<Item> *key_expr, Item *cond) + List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode, + Item *cond) { SQL_HANDLER *handler; DBUG_ENTER("mysql_ha_read_prepare"); if (!(handler= mysql_ha_find_handler(thd, &tables->alias))) DBUG_RETURN(0); tables->table= handler->table; // This is used by fix_fields - if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 1)) + if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, + ha_rkey_mode, cond, 1)) DBUG_RETURN(0); DBUG_RETURN(handler); } diff --git a/sql/sql_handler.h b/sql/sql_handler.h index ffefec9..4c16f7e 100644 --- a/sql/sql_handler.h +++ b/sql/sql_handler.h @@ -80,5 +80,6 @@ void mysql_ha_rm_temporary_tables(THD *thd); SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables, enum enum_ha_read_modes mode, const char *keyname, - List<Item> *key_expr, Item *cond); + List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode, + Item *cond); #endif diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index f763613..d6d30f7 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2204,6 +2204,7 @@ static int mysql_test_handler_read(Prepared_statement *stmt, if (!(ha_table= mysql_ha_read_prepare(thd, tables, lex->ha_read_mode, lex->ident.str, lex->insert_list, + lex->ha_rkey_mode, lex->select_lex.where))) DBUG_RETURN(1);