Commit 7ff0d02d authored by Dmitry Lenev's avatar Dmitry Lenev

Bug #15954872 "MAKE MDL SUBSYSTEM AND TABLE DEFINITION CACHE

ROBUST AGAINST BUGS IN CALLERS".

Both MDL subsystems and Table Definition Cache code assume 
that callers ensure that names of objects passed to them are 
not longer than NAME_LEN bytes. Unfortunately due to bugs in 
callers this assumption might be broken in some cases. As
result we get nasty bugs causing buffer overruns when we
construct MDL key or TDC key from object names.

This patch makes TDC code more robust against such bugs by 
ensuring that we always checking size of result buffer when
constructing TDC keys. This doesn't free its callers from 
ensuring that both db and table names are shorter than 
NAME_LEN bytes. But at least this steps prevents buffer 
overruns in case of bug in caller, replacing them with less 
harmful behavior.

This is 5.1-only version of patch.

This patch introduces new version of create_table_def_key()
helper function which constructs TDC key without risk of
result buffer overrun. Places in code that construct TDC keys 
were changed to use this function.

Also changed rm_temporary_table() and open_new_frm() functions
to avoid use of "unsafe" strmov() and strxmov() functions and 
use safer strnxmov() instead.
parent 8f3f4425
...@@ -1296,6 +1296,31 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok); ...@@ -1296,6 +1296,31 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create); bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list, uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
bool tmp_table); bool tmp_table);
/**
Create a table cache key for non-temporary table.
@param key Buffer for key (must be at least NAME_LEN*2+2 bytes).
@param db Database name.
@param table_name Table name.
@return Length of key.
@sa create_table_def_key(thd, char *, table_list, bool)
*/
inline uint
create_table_def_key(char *key, const char *db, const char *table_name)
{
/*
In theory caller should ensure that both db and table_name are
not longer than NAME_LEN bytes. In practice we play safe to avoid
buffer overruns.
*/
return (uint)(strmake(strmake(key, db, NAME_LEN) + 1, table_name,
NAME_LEN) - key + 1);
}
TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
uint key_length, uint db_flags, int *error); uint key_length, uint db_flags, int *error);
void release_table_share(TABLE_SHARE *share, enum release_type type); void release_table_share(TABLE_SHARE *share, enum release_type type);
...@@ -1593,7 +1618,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen); ...@@ -1593,7 +1618,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
int decide_logging_format(THD *thd, TABLE_LIST *tables); int decide_logging_format(THD *thd, TABLE_LIST *tables);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db, TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
const char *table_name, bool link_in_list); const char *table_name, bool link_in_list);
bool rm_temporary_table(handlerton *base, char *path); bool rm_temporary_table(handlerton *base, const char *path);
void free_io_cache(TABLE *entry); void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry); void intern_close_table(TABLE *entry);
bool close_thread_table(THD *thd, TABLE **table_ptr); bool close_thread_table(THD *thd, TABLE **table_ptr);
......
...@@ -242,8 +242,9 @@ static void check_unused(void) ...@@ -242,8 +242,9 @@ static void check_unused(void)
uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list, uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
bool tmp_table) bool tmp_table)
{ {
uint key_length= (uint) (strmov(strmov(key, table_list->db)+1, uint key_length= create_table_def_key(key, table_list->db,
table_list->table_name)-key)+1; table_list->table_name);
if (tmp_table) if (tmp_table)
{ {
int4store(key + key_length, thd->server_id); int4store(key + key_length, thd->server_id);
...@@ -619,13 +620,10 @@ void release_table_share(TABLE_SHARE *share, enum release_type type) ...@@ -619,13 +620,10 @@ void release_table_share(TABLE_SHARE *share, enum release_type type)
TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name) TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
{ {
char key[NAME_LEN*2+2]; char key[NAME_LEN*2+2];
TABLE_LIST table_list;
uint key_length; uint key_length;
safe_mutex_assert_owner(&LOCK_open); safe_mutex_assert_owner(&LOCK_open);
table_list.db= (char*) db; key_length= create_table_def_key(key, db, table_name);
table_list.table_name= (char*) table_name;
key_length= create_table_def_key((THD*) 0, key, &table_list, 0);
return (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key, key_length); return (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key, key_length);
} }
...@@ -2419,7 +2417,7 @@ bool lock_table_name_if_not_cached(THD *thd, const char *db, ...@@ -2419,7 +2417,7 @@ bool lock_table_name_if_not_cached(THD *thd, const char *db,
uint key_length; uint key_length;
DBUG_ENTER("lock_table_name_if_not_cached"); DBUG_ENTER("lock_table_name_if_not_cached");
key_length= (uint)(strmov(strmov(key, db) + 1, table_name) - key) + 1; key_length= create_table_def_key(key, db, table_name);
VOID(pthread_mutex_lock(&LOCK_open)); VOID(pthread_mutex_lock(&LOCK_open));
if (hash_search(&open_cache, (uchar *)key, key_length)) if (hash_search(&open_cache, (uchar *)key, key_length))
...@@ -3025,7 +3023,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -3025,7 +3023,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name) TABLE *find_locked_table(THD *thd, const char *db,const char *table_name)
{ {
char key[MAX_DBKEY_LENGTH]; char key[MAX_DBKEY_LENGTH];
uint key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; uint key_length= create_table_def_key(key, db, table_name);
for (TABLE *table=thd->open_tables; table ; table=table->next) for (TABLE *table=thd->open_tables; table ; table=table->next)
{ {
...@@ -5737,17 +5735,27 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, ...@@ -5737,17 +5735,27 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
} }
bool rm_temporary_table(handlerton *base, char *path) /**
Delete a temporary table.
@param base Handlerton for table to be deleted.
@param path Path to the table to be deleted (i.e. path
to its .frm without an extension).
@retval false - success.
@retval true - failure.
*/
bool rm_temporary_table(handlerton *base, const char *path)
{ {
bool error=0; bool error=0;
handler *file; handler *file;
char *ext; char frm_path[FN_REFLEN + 1];
DBUG_ENTER("rm_temporary_table"); DBUG_ENTER("rm_temporary_table");
strmov(ext= strend(path), reg_ext); strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
if (my_delete(path,MYF(0))) if (my_delete(frm_path, MYF(0)))
error=1; /* purecov: inspected */ error=1; /* purecov: inspected */
*ext= 0; // remove extension
file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base); file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
if (file && file->ha_delete_table(path)) if (file && file->ha_delete_table(path))
{ {
...@@ -8633,7 +8641,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, ...@@ -8633,7 +8641,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
DBUG_ENTER("remove_table_from_cache"); DBUG_ENTER("remove_table_from_cache");
DBUG_PRINT("enter", ("table: '%s'.'%s' flags: %u", db, table_name, flags)); DBUG_PRINT("enter", ("table: '%s'.'%s' flags: %u", db, table_name, flags));
key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; key_length= create_table_def_key(key, db, table_name);
for (;;) for (;;)
{ {
HASH_SEARCH_STATE state; HASH_SEARCH_STATE state;
...@@ -8831,12 +8839,14 @@ open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, ...@@ -8831,12 +8839,14 @@ open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
{ {
LEX_STRING pathstr; LEX_STRING pathstr;
File_parser *parser; File_parser *parser;
char path[FN_REFLEN]; char path[FN_REFLEN+1];
DBUG_ENTER("open_new_frm"); DBUG_ENTER("open_new_frm");
/* Create path with extension */ /* Create path with extension */
pathstr.length= (uint) (strxmov(path, share->normalized_path.str, reg_ext, pathstr.length= (uint) (strxnmov(path, sizeof(path) - 1,
NullS)- path); share->normalized_path.str,
reg_ext,
NullS) - path);
pathstr.str= path; pathstr.str= path;
if ((parser= sql_parse_prepare(&pathstr, mem_root, 1))) if ((parser= sql_parse_prepare(&pathstr, mem_root, 1)))
...@@ -8977,7 +8987,7 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table ...@@ -8977,7 +8987,7 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table
TABLE *table; TABLE *table;
DBUG_ENTER("mysql_wait_completed_table"); DBUG_ENTER("mysql_wait_completed_table");
key_length=(uint) (strmov(strmov(key,lpt->db)+1,lpt->table_name)-key)+1; key_length= create_table_def_key(key, lpt->db, lpt->table_name);
VOID(pthread_mutex_lock(&LOCK_open)); VOID(pthread_mutex_lock(&LOCK_open));
HASH_SEARCH_STATE state; HASH_SEARCH_STATE state;
for (table= (TABLE*) hash_first(&open_cache,(uchar*) key,key_length, for (table= (TABLE*) hash_first(&open_cache,(uchar*) key,key_length,
......
...@@ -2708,8 +2708,8 @@ void Query_cache::invalidate_table(THD *thd, TABLE_LIST *table_list) ...@@ -2708,8 +2708,8 @@ void Query_cache::invalidate_table(THD *thd, TABLE_LIST *table_list)
char key[MAX_DBKEY_LENGTH]; char key[MAX_DBKEY_LENGTH];
uint key_length; uint key_length;
key_length=(uint) (strmov(strmov(key,table_list->db)+1, key_length= create_table_def_key(key, table_list->db,
table_list->table_name) -key)+ 1; table_list->table_name);
// We don't store temporary tables => no key_length+=4 ... // We don't store temporary tables => no key_length+=4 ...
invalidate_table(thd, (uchar *)key, key_length); invalidate_table(thd, (uchar *)key, key_length);
...@@ -2830,8 +2830,8 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used, ...@@ -2830,8 +2830,8 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
DBUG_PRINT("qcache", ("view: %s db: %s", DBUG_PRINT("qcache", ("view: %s db: %s",
tables_used->view_name.str, tables_used->view_name.str,
tables_used->view_db.str)); tables_used->view_db.str));
key_length= (uint) (strmov(strmov(key, tables_used->view_db.str) + 1, key_length= create_table_def_key(key, tables_used->view_db.str,
tables_used->view_name.str) - key) + 1; tables_used->view_name.str);
/* /*
There are not callback function for for VIEWs There are not callback function for for VIEWs
*/ */
...@@ -4062,8 +4062,9 @@ uint Query_cache::filename_2_table_key (char *key, const char *path, ...@@ -4062,8 +4062,9 @@ uint Query_cache::filename_2_table_key (char *key, const char *path,
*db_length= (filename - dbname) - 1; *db_length= (filename - dbname) - 1;
DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename)); DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename));
DBUG_RETURN((uint) (strmov(strmake(key, dbname, *db_length) + 1, DBUG_RETURN((uint) (strmake(strmake(key, dbname,
filename) -key) + 1); min(*db_length, NAME_LEN)) + 1,
filename, NAME_LEN) - key) + 1);
} }
/**************************************************************************** /****************************************************************************
......
...@@ -1988,9 +1988,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, ...@@ -1988,9 +1988,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
*/ */
#ifndef DBUG_OFF #ifndef DBUG_OFF
uchar key[MAX_DBKEY_LENGTH]; uchar key[MAX_DBKEY_LENGTH];
uint key_length= (uint) (strmov(strmov((char*)&key[0], db)+1, uint key_length= create_table_def_key((char *)key, db, old_table);
old_table)-(char*)&key[0])+1;
if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length)) if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length))
safe_mutex_assert_owner(&LOCK_open); safe_mutex_assert_owner(&LOCK_open);
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment