Details
-
Bug
-
Status: Closed (View Workflow)
-
Major
-
Resolution: Fixed
-
10.6(EOL)
-
None
-
Not for Release Notes
Description
Grant_tables::open_and_lock() allocates the TABLE_LIST array for the privilege tables with my_malloc() and frees it with my_free() before returning. The heap allocation came in with MDEV-34388 (Alpine stack overflow), which moved the array off the stack.
The privilege tables opened from this array stay open after the function returns. They remain on thd->open_tables until close_mysql_tables() or the end of the statement. Each of these TABLE objects keeps TABLE::pos_in_table_list pointing into the freed array. So for the rest of the GRANT/REVOKE/CREATE USER/FLUSH PRIVILEGES/... statement, every opened privilege table holds a dangling pointer.
The following happens on DROP USER and custom storage engine which reads table->pos_in_table_list in table->file->ha_index_read_idx_map().
...
|
sql/handler.cc:7515(handler::index_read_idx_map(unsigned char*, unsigned int, unsigned char const*, unsigned long, ha_rkey_function))[0x5629d887f28f] |
sql/handler.cc:3986(handler::ha_index_read_idx_map(unsigned char*, unsigned int, unsigned char const*, unsigned long, ha_rkey_function))[0x5629d8869437] |
sql/sql_acl.cc:10532(handle_grant_table(THD*, Grant_table_base const&, enum_acl_tables, bool, LEX_USER*, LEX_USER*))[0x5629d8e9e5b7] |
sql/sql_acl.cc:11184(handle_grant_data(THD*, Grant_tables&, bool, LEX_USER*, LEX_USER*))[0x5629d8e7ea82] |
sql/sql_acl.cc:11437(mysql_drop_user(THD*, List<LEX_USER>&, bool))[0x5629d8e7f2a4] |
sql/sql_parse.cc:0(mysql_execute_command(THD*, bool))[0x5629d9054b32] |
sql/sql_class.h:3284(mysql_parse(THD*, char*, unsigned int, Parser_state*))[0x5629d904a7f2] |
sql/sql_parse.cc:0(dispatch_command(enum_server_command, THD*, char*, unsigned int, bool))[0x5629d9044b09] |
sql/sql_parse.cc:1457(do_command(THD*, bool))[0x5629d904b47c] |
sql/sql_connect.cc:1511(do_handle_one_connection(CONNECT*, bool))[0x5629d947956d] |
sql/sql_connect.cc:1427(handle_one_connection)[0x5629d9478dab] |
perfschema/pfs.cc:2200(pfs_spawn_thread)[0x5629d99e968c] |
crtstuff.c:0(asan_thread_start(void*))[0x5629d86c890b]
|
nptl/pthread_create.c:454(start_thread)[0x7f74996a3dfa] |
x86_64/clone3.S:80(__GI___clone3)[0x7f74997375cc] |
InnoDB and ARIA do not read table->pos_in_table_list thus no issues observed
To check it:
--source include/have_innodb.inc
|
|
|
CREATE USER 'gp_admin'@'localhost' IDENTIFIED BY 'Admin123!';
|
GRANT ALL PRIVILEGES ON *.* TO 'gp_admin'@'localhost' WITH GRANT OPTION;
|
|
|
RENAME TABLE mysql.global_priv TO mysql.global_priv_local;
|
|
|
CREATE TABLE IF NOT EXISTS mysql.global_priv (
|
Host CHAR(255) BINARY NOT NULL,
|
User CHAR(128) BINARY NOT NULL,
|
Priv JSON NOT NULL,
|
PRIMARY KEY (Host, User)
|
) ENGINE=InnoDB CHARACTER SET utf8mb3 COLLATE utf8mb3_bin;
|
|
|
SELECT engine
|
FROM information_schema.tables
|
WHERE table_schema = 'mysql' AND table_name = 'global_priv';
|
|
|
INSERT INTO mysql.global_priv SELECT * FROM mysql.global_priv_local;
|
FLUSH PRIVILEGES;
|
|
|
connect (joiner, localhost, gp_admin, Admin123!);
|
USE mysql;
|
disconnect joiner;
|
|
|
connection default;
|
DROP TABLE mysql.global_priv;
|
RENAME TABLE mysql.global_priv_local TO mysql.global_priv;
|
DROP USER 'gp_admin'@'localhost';
|
Proposed fix:
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
|
index 70984625879..8235d82ea1f 100644
|
--- a/sql/sql_acl.cc
|
+++ b/sql/sql_acl.cc
|
@@ -2739,10 +2739,8 @@ class Grant_tables
|
{
|
DBUG_ENTER("Grant_tables::open_and_lock");
|
|
- TABLE_LIST *first= nullptr, *tables=
|
- static_cast<TABLE_LIST*>(my_malloc(PSI_NOT_INSTRUMENTED,
|
- (USER_TABLE + 1) * sizeof *tables,
|
- MYF(MY_WME)));
|
+ TABLE_LIST *first= nullptr,
|
+ *tables= thd->calloc<TABLE_LIST>(USER_TABLE + 1);
|
int res= -1;
|
uint counter;
|
|
@@ -2802,7 +2800,6 @@ class Grant_tables
|
}
|
|
func_exit:
|
- my_free(tables);
|
DBUG_RETURN(res);
|
}
|
|
|
Attachments
Issue Links
- relates to
-
MDEV-30065 "mariadb-install-db --enforce-storage-engine=InnoDB" fails with InnoDB errors
-
- Stalled
-