Skip to content
  • Dmitry Shulga's avatar
    cf3d43d5
    BUG#21463167 - GTIDS: LOCK_OPEN DEADLOCK WITH REVOKE INCIDENT + SHOW TABLE STATUS CONCURRENTLY · cf3d43d5
    Dmitry Shulga authored
    For mysql server started with GTID_MODE=ON concurrent running of the statements
    SHOW TABLE STATUS and REVOKE ALL PRIVILEGES could lead to deadlock in case
    there is a view in database and REVOKE ALL PRIVILEGES partially failed for
    some of requested users, that is some users are handled successfully and
    some of them are failed.
    
    The reason for deadlock is that SHOW TABLE STATUS acquires the lock LOCK_grant
    within mysql_make_view() while holding LOCK_open whereas REVOKE ALL PRIVILEGES
    acquires LOCK_open holding LOCK_grant. The sequence of acquiring LOCK_grant,
    LOCK_open while handling REVOKE ALL PRIVILEGES happens only for the case when
    some of users listed in the statement arguments are handled successfully and
    some of them are failed AND the server is started with GTID_MODE=ON.
    In this case mysql_revoke_all() calls MYSQL_BIN_LOG::write_incident() that
    finally call open_table() on behalf of Gtid_table_access_context::init().
    On the other hand, acquiring of locks in the order LOCK_open, LOCK_grant while
    handling the statement SHOW TABLE STATUS happens in case there is a view and
    mysql_make_view() is called from open_table().
    
    There are two approaches to resolve this issue. The first one is to release
    LOCK_grant before calling MYSQL_BIN_LOG::write_incident() while handling
    REVOKE ALL PRIVILEGES. This modification could led potentially to reordering
    in binlog of statement REVOKE ALL PRIVILEGES with other statements running
    at the same moment. So this approach is too risky. The second one is to
    release LOCK_open just right after a view definition was read from the data
    dictionary. The second approach forces us to make refactoring that splits
    mysql_make_view() into two functions: open_and_read_view() and
    parse_view_definition(), replaces all occurences of mysql_make_view() by these
    two functions and releases LOCK_open just right after open_and_read_view()
    returned.
    
    To have a test case for this bug the function check_table_access()
    was modified in order to return immediately in case the debug variable
    force_check_table_access_return_ok is set. This allows to execute
    SET DEBUG_SYNC='...' from the test case while holding LOCK_grant.
    cf3d43d5
    BUG#21463167 - GTIDS: LOCK_OPEN DEADLOCK WITH REVOKE INCIDENT + SHOW TABLE STATUS CONCURRENTLY
    Dmitry Shulga authored
    For mysql server started with GTID_MODE=ON concurrent running of the statements
    SHOW TABLE STATUS and REVOKE ALL PRIVILEGES could lead to deadlock in case
    there is a view in database and REVOKE ALL PRIVILEGES partially failed for
    some of requested users, that is some users are handled successfully and
    some of them are failed.
    
    The reason for deadlock is that SHOW TABLE STATUS acquires the lock LOCK_grant
    within mysql_make_view() while holding LOCK_open whereas REVOKE ALL PRIVILEGES
    acquires LOCK_open holding LOCK_grant. The sequence of acquiring LOCK_grant,
    LOCK_open while handling REVOKE ALL PRIVILEGES happens only for the case when
    some of users listed in the statement arguments are handled successfully and
    some of them are failed AND the server is started with GTID_MODE=ON.
    In this case mysql_revoke_all() calls MYSQL_BIN_LOG::write_incident() that
    finally call open_table() on behalf of Gtid_table_access_context::init().
    On the other hand, acquiring of locks in the order LOCK_open, LOCK_grant while
    handling the statement SHOW TABLE STATUS happens in case there is a view and
    mysql_make_view() is called from open_table().
    
    There are two approaches to resolve this issue. The first one is to release
    LOCK_grant before calling MYSQL_BIN_LOG::write_incident() while handling
    REVOKE ALL PRIVILEGES. This modification could led potentially to reordering
    in binlog of statement REVOKE ALL PRIVILEGES with other statements running
    at the same moment. So this approach is too risky. The second one is to
    release LOCK_open just right after a view definition was read from the data
    dictionary. The second approach forces us to make refactoring that splits
    mysql_make_view() into two functions: open_and_read_view() and
    parse_view_definition(), replaces all occurences of mysql_make_view() by these
    two functions and releases LOCK_open just right after open_and_read_view()
    returned.
    
    To have a test case for this bug the function check_table_access()
    was modified in order to return immediately in case the debug variable
    force_check_table_access_return_ok is set. This allows to execute
    SET DEBUG_SYNC='...' from the test case while holding LOCK_grant.
Loading