Description:
The function replace_user_table() does not properly check expected return code when reading from the mysql.user table. Thus execution will continue as if the user didn't exists even in case it probably did. Normally resulting in that the code tries to update the table.
diff --git a/sql/auth/sql_user_table.cc b/sql/auth/sql_user_table.cc
index 1339080..4031686 100644
--- a/sql/auth/sql_user_table.cc
+++ b/sql/auth/sql_user_table.cc
@@ -449,6 +449,7 @@ int replace_user_table(THD *thd, TABLE *table, LEX_USER *combo,
HA_WHOLE_KEY,
HA_READ_KEY_EXACT))
{
+ Need to check for expected "record does not exist" error here!
/*
The user record wasn't found; if the intention was to revoke privileges
(indicated by what == 'N') then execution must fail now.
How to repeat:
Provoke error reading from mysql.user so that the table->file->ha_index_read_idx_map() call fails with a different error than "record does not exist".
Normally it is hard to provoke as long as the mysql.user table is using MyISAM but with other engine(s) it might be possible to start an update transaction on the given user and then switch over to another connection and perform the SET PASSWORD command.
With engine being NDB it would (using mysqltest language) look like:
connection server1;
CREATE USER 'billy1'@'127.0.0.1' IDENTIFIED by 'mypass';
BEGIN;
UPDATE mysql.user SET authentication_string = '' WHERE User = 'billy1';
connection server2;
--error <some sensible error message>
SET PASSWORD FOR 'billy1'@'127.0.0.1' = 'newpass';
SHOW WARNINGS;
<should show some warnings describing that an error occured while reading the user form the table>
connection server1;
ROLLBACK;
connection server2;
DROP USER 'billy1'@'127.0.0.1';
Suggested fix:
Properly check expected return code after trying to read from mysql.user table.