diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index 7ac10de1f7c23893c8dfc753050ddc70a7e7eb8a..ca375ef50575bdfd4efdbe0cdec9768324cd5825 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -312,3 +312,12 @@ flush privileges; drop database mysqltest_1; set password = password("changed"); ERROR 42000: Access denied for user ''@'localhost' to database 'mysql' +lock table mysql.user write; + flush privileges; + grant all on *.* to 'mysqltest_1'@'localhost'; +unlock tables; +lock table mysql.user write; + set password for 'mysqltest_1'@'localhost' = password(''); + revoke all on *.* from 'mysqltest_1'@'localhost'; +unlock tables; +drop user 'mysqltest_1'@'localhost'; diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test index ee9cd1924c670633e28beab9928a6551ef758a10..2fa82ce5dce2ce2b55401900ae18b9f38e3aedcb 100644 --- a/mysql-test/t/grant2.test +++ b/mysql-test/t/grant2.test @@ -351,4 +351,47 @@ set password = password("changed"); disconnect n5; connection default; + +# Bug #12423 "Deadlock when doing FLUSH PRIVILEGES and GRANT in +# multi-threaded environment". We should be able to execute FLUSH +# PRIVILEGES and SET PASSWORD simultaneously with other account +# management commands (such as GRANT and REVOKE) without causing +# deadlocks. To achieve this we should ensure that all account +# management commands take table and internal locks in the same order. +connect (con2root,localhost,root,,); +connect (con3root,localhost,root,,); +# Check that we can execute FLUSH PRIVILEGES and GRANT simultaneously +# This will check that locks are taken in proper order during both +# user/db-level and table/column-level privileges reloading. +connection default; +lock table mysql.user write; +connection con2root; +send flush privileges; +connection con3root; +send grant all on *.* to 'mysqltest_1'@'localhost'; +connection default; +unlock tables; +connection con2root; +reap; +connection con3root; +reap; +# Check for simultaneous SET PASSWORD and REVOKE. +connection default; +lock table mysql.user write; +connection con2root; +send set password for 'mysqltest_1'@'localhost' = password(''); +connection con3root; +send revoke all on *.* from 'mysqltest_1'@'localhost'; +connection default; +unlock tables; +connection con2root; +reap; +connection con3root; +reap; +connection default; +# Clean-up +drop user 'mysqltest_1'@'localhost'; +disconnect con2root; +disconnect con3root; + # End of 4.1 tests diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f58dd72f6b555e3f6cc739a5330d06f9ba3e6a4b..a3cc3d2916b5411248d4a03ad0a9dd72aea32ff3 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3254,7 +3254,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); */ error_handler_hook= my_message_sql; start_signal_handler(); // Creates pidfile - if (acl_init((THD *)0, opt_noacl) || + if (acl_init(opt_noacl) || my_tz_init((THD *)0, default_tz_name, opt_bootstrap)) { abort_loop=1; @@ -3271,7 +3271,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); exit(1); } if (!opt_noacl) - (void) grant_init((THD *)0); + (void) grant_init(); #ifdef HAVE_DLOPEN if (!opt_noacl) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4629752c6a71fe7f3c21cce8cff539e358fa16dd..4127129576bc16f183e925448029e33920418366 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -63,18 +63,21 @@ static bool allow_all_hosts=1; static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash; static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; -static uint grant_version=0; /* Version of priv tables. incremented by acl_init */ +static uint grant_version=0; /* Version of priv tables. incremented by acl_load */ static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0); static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); static ACL_USER *find_acl_user(const char *host, const char *user, my_bool exact); -static bool update_user_table(THD *thd, const char *host, const char *user, +static bool update_user_table(THD *thd, TABLE *table, + const char *host, const char *user, const char *new_password, uint new_password_len); static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool compare_hostname(const acl_host_and_ip *host,const char *hostname, const char *ip); +static my_bool acl_load(THD *thd, TABLE_LIST *tables); +static my_bool grant_load(TABLE_LIST *tables); /* Convert scrambled password to binary form, according to scramble type, @@ -119,68 +122,85 @@ static void restrict_update_of_old_passwords_var(THD *thd, /* - Read grant privileges from the privilege tables in the 'mysql' database. + Initialize structures responsible for user/db-level privilege checking and + load privilege information for them from tables in the 'mysql' database. SYNOPSIS acl_init() - thd Thread handler - dont_read_acl_tables Set to 1 if run with --skip-grant + dont_read_acl_tables TRUE if we want to skip loading data from + privilege tables and disable privilege checking. + + NOTES + This function is mostly responsible for preparatory steps, main work + on initialization and grants loading is done in acl_reload(). RETURN VALUES 0 ok 1 Could not initialize grant's */ - -my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) +my_bool acl_init(bool dont_read_acl_tables) { THD *thd; - TABLE_LIST tables[3]; - TABLE *table; - READ_RECORD read_record_info; - my_bool return_val=1; - bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; - char tmp_name[NAME_LEN+1]; - + my_bool return_val; DBUG_ENTER("acl_init"); - if (!acl_cache) - acl_cache=new hash_filo(ACL_CACHE_SIZE,0,0, - (hash_get_key) acl_entry_get_key, - (hash_free_key) free, system_charset_info); + acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0, + (hash_get_key) acl_entry_get_key, + (hash_free_key) free, system_charset_info); if (dont_read_acl_tables) { DBUG_RETURN(0); /* purecov: tested */ } - grant_version++; /* Privileges updated */ - mysql_proc_table_exists= 1; // Assume mysql.proc exists - /* To be able to run this from boot, we allocate a temporary THD */ if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: inspected */ thd->store_globals(); + /* + It is safe to call acl_reload() since acl_* arrays and hashes which + will be freed there are global static objects and thus are initialized + by zeros at startup. + */ + return_val= acl_reload(thd); + delete thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + DBUG_RETURN(return_val); +} + + +/* + Initialize structures responsible for user/db-level privilege checking + and load information about grants from open privilege tables. + + SYNOPSIS + acl_load() + thd Current thread + tables List containing open "mysql.host", "mysql.user" and + "mysql.db" tables. + + RETURN VALUES + FALSE Success + TRUE Error +*/ + +static my_bool acl_load(THD *thd, TABLE_LIST *tables) +{ + TABLE *table; + READ_RECORD read_record_info; + my_bool return_val= 1; + bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; + char tmp_name[NAME_LEN+1]; + DBUG_ENTER("acl_load"); + + grant_version++; /* Privileges updated */ + mysql_proc_table_exists= 1; // Assume mysql.proc exists acl_cache->clear(1); // Clear locked hostname cache - thd->db= my_strdup("mysql",MYF(0)); - thd->db_length=5; // Safety - bzero((char*) &tables,sizeof(tables)); - tables[0].alias=tables[0].table_name=(char*) "host"; - tables[1].alias=tables[1].table_name=(char*) "user"; - tables[2].alias=tables[2].table_name=(char*) "db"; - tables[0].next_local= tables[0].next_global= tables+1; - tables[1].next_local= tables[1].next_global= tables+2; - tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; - tables[0].db=tables[1].db=tables[2].db=thd->db; - if (simple_open_n_lock_tables(thd, tables)) - { - sql_print_error("Fatal error: Can't open and lock privilege tables: %s", - thd->net.last_error); - goto end; - } init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0); init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0); VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50)); @@ -454,19 +474,9 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) init_check_host(); initialized=1; - thd->version--; // Force close to free memory return_val=0; end: - close_thread_tables(thd); - delete thd; - if (org_thd) - org_thd->store_globals(); /* purecov: inspected */ - else - { - /* Remember that we don't have a THD */ - my_pthread_setspecific_ptr(THR_THD, 0); - } DBUG_RETURN(return_val); } @@ -490,27 +500,60 @@ void acl_free(bool end) /* - Forget current privileges and read new privileges from the privilege tables + Forget current user/db-level privileges and read new privileges + from the privilege tables. SYNOPSIS acl_reload() - thd Thread handle. Note that this may be NULL if we refresh - because we got a signal + thd Current thread + + NOTE + All tables of calling thread which were open and locked by LOCK TABLES + statement will be unlocked and closed. + This function is also used for initialization of structures responsible + for user/db-level privilege checking. + + RETURN VALUE + FALSE Success + TRUE Failure */ -void acl_reload(THD *thd) +my_bool acl_reload(THD *thd) { + TABLE_LIST tables[3]; DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs; MEM_ROOT old_mem; bool old_initialized; + my_bool return_val= 1; DBUG_ENTER("acl_reload"); - if (thd && thd->locked_tables) + if (thd->locked_tables) { // Can't have locked tables here thd->lock=thd->locked_tables; thd->locked_tables=0; close_thread_tables(thd); } + + /* + To avoid deadlocks we should obtain table locks before + obtaining acl_cache->lock mutex. + */ + bzero((char*) tables, sizeof(tables)); + tables[0].alias= tables[0].table_name= (char*) "host"; + tables[1].alias= tables[1].table_name= (char*) "user"; + tables[2].alias= tables[2].table_name= (char*) "db"; + tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + tables[0].next_local= tables[0].next_global= tables+1; + tables[1].next_local= tables[1].next_global= tables+2; + tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; + + if (simple_open_n_lock_tables(thd, tables)) + { + sql_print_error("Fatal error: Can't open and lock privilege tables: %s", + thd->net.last_error); + goto end; + } + if ((old_initialized=initialized)) VOID(pthread_mutex_lock(&acl_cache->lock)); @@ -521,7 +564,7 @@ void acl_reload(THD *thd) delete_dynamic(&acl_wild_hosts); hash_free(&acl_check_hosts); - if (acl_init(thd, 0)) + if ((return_val= acl_load(thd, tables))) { // Error. Revert to old list DBUG_PRINT("error",("Reverting to old privileges")); acl_free(); /* purecov: inspected */ @@ -540,7 +583,9 @@ void acl_reload(THD *thd) } if (old_initialized) VOID(pthread_mutex_unlock(&acl_cache->lock)); - DBUG_VOID_RETURN; +end: + close_thread_tables(thd); + DBUG_RETURN(return_val); } @@ -1330,7 +1375,13 @@ bool check_change_password(THD *thd, const char *host, const char *user, bool change_password(THD *thd, const char *host, const char *user, char *new_password) { + TABLE_LIST tables; + TABLE *table; + /* Buffer should be extended when password length is extended. */ + char buff[512]; + ulong query_length; uint new_password_len= strlen(new_password); + bool result= 1; DBUG_ENTER("change_password"); DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'", host,user,new_password)); @@ -1339,40 +1390,69 @@ bool change_password(THD *thd, const char *host, const char *user, if (check_change_password(thd, host, user, new_password, new_password_len)) DBUG_RETURN(1); + bzero((char*) &tables, sizeof(tables)); + tables.alias= tables.table_name= (char*) "user"; + tables.db= (char*) "mysql"; + +#ifdef HAVE_REPLICATION + /* + GRANT and REVOKE are applied the slave in/exclusion rules as they are + some kind of updates to the mysql.% tables. + */ + if (thd->slave_thread && table_rules_on) + { + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. It's ok to leave 'updating' set after tables_ok. + */ + tables.updating= 1; + /* Thanks to bzero, tables.next==0 */ + if (!tables_ok(thd, &tables)) + DBUG_RETURN(0); + } +#endif + + if (!(table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(1); + VOID(pthread_mutex_lock(&acl_cache->lock)); ACL_USER *acl_user; if (!(acl_user= find_acl_user(host, user, TRUE))) { VOID(pthread_mutex_unlock(&acl_cache->lock)); my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); - DBUG_RETURN(1); + goto end; } /* update loaded acl entry: */ set_user_salt(acl_user, new_password, new_password_len); - if (update_user_table(thd, + if (update_user_table(thd, table, acl_user->host.hostname ? acl_user->host.hostname : "", acl_user->user ? acl_user->user : "", new_password, new_password_len)) { VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */ - DBUG_RETURN(1); /* purecov: deadcode */ + goto end; } acl_cache->clear(1); // Clear locked hostname cache VOID(pthread_mutex_unlock(&acl_cache->lock)); - - char buff[512]; /* Extend with extended password length*/ - ulong query_length= - my_sprintf(buff, - (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"", - acl_user->user ? acl_user->user : "", - acl_user->host.hostname ? acl_user->host.hostname : "", - new_password)); - thd->clear_error(); - Query_log_event qinfo(thd, buff, query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - DBUG_RETURN(0); + result= 0; + if (mysql_bin_log.is_open()) + { + query_length= + my_sprintf(buff, + (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"", + acl_user->user ? acl_user->user : "", + acl_user->host.hostname ? acl_user->host.hostname : "", + new_password)); + thd->clear_error(); + Query_log_event qinfo(thd, buff, query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } +end: + close_thread_tables(thd); + DBUG_RETURN(result); } @@ -1486,44 +1566,29 @@ bool hostname_requires_resolving(const char *hostname) return FALSE; } + /* - Update grants in the user and database privilege tables + Update record for user in mysql.user privilege table with new password. + + SYNOPSIS + update_user_table() + thd Thread handle + table Pointer to TABLE object for open mysql.user table + host/user Hostname/username pair identifying user for which + new password should be set + new_password New password + new_password_len Length of new password */ -static bool update_user_table(THD *thd, const char *host, const char *user, +static bool update_user_table(THD *thd, TABLE *table, + const char *host, const char *user, const char *new_password, uint new_password_len) { - TABLE_LIST tables; - TABLE *table; - bool error=1; char user_key[MAX_KEY_LENGTH]; + int error; DBUG_ENTER("update_user_table"); DBUG_PRINT("enter",("user: %s host: %s",user,host)); - bzero((char*) &tables,sizeof(tables)); - tables.alias=tables.table_name=(char*) "user"; - tables.db=(char*) "mysql"; - -#ifdef HAVE_REPLICATION - /* - GRANT and REVOKE are applied the slave in/exclusion rules as they are - some kind of updates to the mysql.% tables. - */ - if (thd->slave_thread && table_rules_on) - { - /* - The tables must be marked "updating" so that tables_ok() takes them into - account in tests. It's ok to leave 'updating' set after tables_ok. - */ - tables.updating= 1; - /* Thanks to bzero, tables.next==0 */ - if (!tables_ok(thd, &tables)) - DBUG_RETURN(0); - } -#endif - - if (!(table=open_ltable(thd,&tables,TL_WRITE))) - DBUG_RETURN(1); /* purecov: deadcode */ table->field[0]->store(host,(uint) strlen(host), system_charset_info); table->field[1]->store(user,(uint) strlen(user), system_charset_info); key_copy((byte *) user_key, table->record[0], table->key_info, @@ -1543,13 +1608,9 @@ static bool update_user_table(THD *thd, const char *host, const char *user, if ((error=table->file->update_row(table->record[1],table->record[0]))) { table->file->print_error(error,MYF(0)); /* purecov: deadcode */ - goto end; /* purecov: deadcode */ + DBUG_RETURN(1); } - error=0; // Record updated - -end: - close_thread_tables(thd); - DBUG_RETURN(error); + DBUG_RETURN(0); } @@ -3125,17 +3186,59 @@ void grant_free(void) } -/* Init grant array if possible */ +/* + Initialize structures responsible for table/column-level privilege checking + and load information for them from tables in the 'mysql' database. + + SYNOPSIS + grant_init() -my_bool grant_init(THD *org_thd) + RETURN VALUES + 0 ok + 1 Could not initialize grant's +*/ + +my_bool grant_init() { THD *thd; - TABLE_LIST tables[3]; + my_bool return_val; + DBUG_ENTER("grant_init"); + + if (!(thd= new THD)) + DBUG_RETURN(1); /* purecov: deadcode */ + thd->store_globals(); + return_val= grant_reload(thd); + delete thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + DBUG_RETURN(return_val); +} + + +/* + Initialize structures responsible for table/column-level privilege + checking and load information about grants from open privilege tables. + + SYNOPSIS + grant_load() + thd Current thread + tables List containing open "mysql.tables_priv" and + "mysql.columns_priv" tables. + + RETURN VALUES + FALSE - success + TRUE - error +*/ + +static my_bool grant_load(TABLE_LIST *tables) +{ MEM_ROOT *memex_ptr; my_bool return_val= 1; TABLE *t_table, *c_table, *p_table; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; - DBUG_ENTER("grant_init"); + MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, + THR_MALLOC); + DBUG_ENTER("grant_load"); grant_option = FALSE; (void) hash_init(&column_priv_hash,system_charset_info, @@ -3149,34 +3252,12 @@ my_bool grant_init(THD *org_thd) 0,0); init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0); - /* Don't do anything if running with --skip-grant */ - if (!initialized) - DBUG_RETURN(0); /* purecov: tested */ - - if (!(thd=new THD)) - DBUG_RETURN(1); /* purecov: deadcode */ - thd->store_globals(); - thd->db= my_strdup("mysql",MYF(0)); - thd->db_length=5; // Safety - bzero((char*) &tables, sizeof(tables)); - tables[0].alias=tables[0].table_name= (char*) "tables_priv"; - tables[1].alias=tables[1].table_name= (char*) "columns_priv"; - tables[2].alias=tables[2].table_name= (char*) "procs_priv"; - tables[0].next_local= tables[0].next_global= tables+1; - tables[1].next_local= tables[1].next_global= tables+2; - tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; - tables[0].db=tables[1].db=tables[2].db=thd->db; - - if (simple_open_n_lock_tables(thd, tables)) - goto end; - t_table = tables[0].table; c_table = tables[1].table; p_table= tables[2].table; t_table->file->ha_index_init(0); p_table->file->ha_index_init(0); if (!t_table->file->index_first(t_table->record[0])) { - /* Will be restored by org_thd->store_globals() */ memex_ptr= &memex; my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); do @@ -3214,7 +3295,6 @@ my_bool grant_init(THD *org_thd) } if (!p_table->file->index_first(p_table->record[0])) { - /* Will be restored by org_thd->store_globals() */ memex_ptr= &memex; my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); do @@ -3274,40 +3354,58 @@ my_bool grant_init(THD *org_thd) end_unlock: t_table->file->ha_index_end(); p_table->file->ha_index_end(); - thd->version--; // Force close to free memory - -end: - close_thread_tables(thd); - delete thd; - if (org_thd) - org_thd->store_globals(); - else - { - /* Remember that we don't have a THD */ - my_pthread_setspecific_ptr(THR_THD, 0); - } + my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr); DBUG_RETURN(return_val); } /* - Reload grant array (table and column privileges) if possible + Reload information about table and column level privileges if possible. SYNOPSIS grant_reload() - thd Thread handler (can be NULL) + thd Current thread NOTES - Locked tables are checked by acl_init and doesn't have to be checked here + Locked tables are checked by acl_reload() and doesn't have to be checked + in this call. + This function is also used for initialization of structures responsible + for table/column-level privilege checking. + + RETURN VALUE + FALSE Success + TRUE Error */ -void grant_reload(THD *thd) +my_bool grant_reload(THD *thd) { + TABLE_LIST tables[3]; HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash; bool old_grant_option; MEM_ROOT old_mem; + my_bool return_val= 1; DBUG_ENTER("grant_reload"); + /* Don't do anything if running with --skip-grant-tables */ + if (!initialized) + DBUG_RETURN(0); + + bzero((char*) tables, sizeof(tables)); + tables[0].alias= tables[0].table_name= (char*) "tables_priv"; + tables[1].alias= tables[1].table_name= (char*) "columns_priv"; + tables[2].alias= tables[2].table_name= (char*) "procs_priv"; + tables[0].db= tables[1].db= tables[2].db= (char *) "mysql"; + tables[0].next_local= tables[0].next_global= tables+1; + tables[1].next_local= tables[1].next_global= tables+2; + tables[0].lock_type= tables[1].lock_type= tables[2].lock_type= TL_READ; + + /* + To avoid deadlocks we should obtain table locks before + obtaining LOCK_grant rwlock. + */ + if (simple_open_n_lock_tables(thd, tables)) + goto end; + rw_wrlock(&LOCK_grant); grant_version++; old_column_priv_hash= column_priv_hash; @@ -3316,7 +3414,7 @@ void grant_reload(THD *thd) old_grant_option= grant_option; old_mem= memex; - if (grant_init(thd)) + if ((return_val= grant_load(tables))) { // Error. Revert to old hash DBUG_PRINT("error",("Reverting to old privileges")); grant_free(); /* purecov: deadcode */ @@ -3334,7 +3432,9 @@ void grant_reload(THD *thd) free_root(&old_mem,MYF(0)); } rw_unlock(&LOCK_grant); - DBUG_VOID_RETURN; +end: + close_thread_tables(thd); + DBUG_RETURN(return_val); } diff --git a/sql/sql_acl.h b/sql/sql_acl.h index eba000a627a3b7061bd63fe0acf1b763884f595d..6e6f33e68c2e2d2bbb523c99c36a4ed152d7a443 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -174,8 +174,8 @@ class ACL_DB :public ACL_ACCESS /* prototypes */ bool hostname_requires_resolving(const char *hostname); -my_bool acl_init(THD *thd, bool dont_read_acl_tables); -void acl_reload(THD *thd); +my_bool acl_init(bool dont_read_acl_tables); +my_bool acl_reload(THD *thd); void acl_free(bool end=0); ulong acl_get(const char *host, const char *ip, const char *user, const char *db, my_bool db_is_pattern); @@ -196,9 +196,9 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc, List <LEX_USER> &user_list, ulong rights, bool revoke, bool no_error); ACL_USER *check_acl_user(LEX_USER *user_name, uint *acl_acl_userdx); -my_bool grant_init(THD *thd); +my_bool grant_init(); void grant_free(void); -void grant_reload(THD *thd); +my_bool grant_reload(THD *thd); bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, uint show_command, uint number, bool dont_print_error); bool check_grant_column (THD *thd, GRANT_INFO *grant, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 923a00decae498f8d82d3172757f902bbdd62fb9..8b054035e294a927fe5f5b06a45cde7aab006820 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6552,8 +6552,25 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, #ifndef NO_EMBEDDED_ACCESS_CHECKS if (options & REFRESH_GRANT) { - acl_reload(thd); - grant_reload(thd); + THD *tmp_thd= 0; + /* + If reload_acl_and_cache() is called from SIGHUP handler we have to + allocate temporary THD for execution of acl_reload()/grant_reload(). + */ + if (!thd && (thd= (tmp_thd= new THD))) + thd->store_globals(); + if (thd) + { + (void)acl_reload(thd); + (void)grant_reload(thd); + } + if (tmp_thd) + { + delete tmp_thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + thd= 0; + } reset_mqh((LEX_USER *)NULL, TRUE); } #endif