List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:October 19 2007 3:57pm
Subject:bk commit into 5.1 tree (anozdrin:1.2593) BUG#31111
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of alik. When alik does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-10-19 19:57:08+04:00, anozdrin@station. +7 -0
  Patch for BUG#31111: --read-only crashes MySQL (events fail to load).
  
  There actually were several problems here:
    - WRITE-lock is required to load events from the mysql.event table,
      but in the read-only mode an ordinary user can not acquire it;
    - Security_context::master_access attribute was not properly
      initialized in Security_context::init(), which led to differences
      in behavior with and without debug configure options.
    - if the server failed to load events from mysql.event, it forgot to
      close the mysql.event table, that led to the coredump, described
      in the bug report.
  
  The patch is to fix all these problems:
    - Use the super-user to acquire WRITE-lock on the mysql.even table;
    - The WRITE-lock is acquired by the event scheduler in two cases:
      - on initial loading of events from the database;
      - when an event has been executed, so its attributes should
        be updated.
      Other cases when WRITE-lock is needed for the mysql.event table
      happen under the user account. So, nothing should be changed there
      for the read-only mode. The user is able to create/update/drop
      an event only if he is a super-user.
    - Initialize Security_context::master_access;
    - Close the mysql.event table in case something went wrong.

  mysql-test/r/events_bugs.result@stripped, 2007-10-19 19:57:06+04:00, anozdrin@station. +102 -1
    Update result file.

  mysql-test/t/events_bugs.test@stripped, 2007-10-19 19:57:06+04:00, anozdrin@station. +212 -12
    A test case for BUG#31111: --read-only crashes MySQL (events fail
    to load).

  sql/event_data_objects.cc@stripped, 2007-10-19 19:57:06+04:00, anozdrin@station. +14 -2
    When the worker thread is going to drop event after the execution
    we should do it under the super-user privileges in order to be able
    to lock the mysql.event table for writing in the read-only mode.
    
    This is a system operation, where user SQL can not be executed.
    So, there is no risk in compromising security by dropping an event
    under the super-user privileges.

  sql/event_db_repository.cc@stripped, 2007-10-19 19:57:06+04:00, anozdrin@station. +9 -0
    1. Close tables if something went wrong in simple_open_n_lock_tables();
    2. As soon as the system event scheduler thread is running under
       the super-user privileges, we should always be able to acquire
       WRITE-lock on the mysql.event table. However, let's have an assert
       to check this.

  sql/event_scheduler.cc@stripped, 2007-10-19 19:57:06+04:00, anozdrin@station. +7 -0
    Run the system event scheduler thread under the super-user privileges.
    In particular, this is needed to be able to lock the mysql.event table
    for writing when the server is running in the read-only mode.
    
    The event scheduler executes only system operations and does not
    execute user SQL (this is what the worker threads for). So, there
    is no risk in compromising security by running the event scheduler
    under the super-user privileges.

  sql/events.cc@stripped, 2007-10-19 19:57:06+04:00, anozdrin@station. +15 -1
    Open the mysql.event table as the super user to be able to acquire
    WRITE-lock in the read-only mode.

  sql/sql_class.cc@stripped, 2007-10-19 19:57:06+04:00, anozdrin@station. +1 -0
    Initialize Security_context::master_acces.

diff -Nrup a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result
--- a/mysql-test/r/events_bugs.result	2007-09-14 13:03:19 +04:00
+++ b/mysql-test/r/events_bugs.result	2007-10-19 19:57:06 +04:00
@@ -610,7 +610,6 @@ id	ev_nm	ev_cnt
 6	ev_sched_1823	6
 DROP TABLE event_log;
 SET GLOBAL event_scheduler = OFF;
-DROP DATABASE events_test;
 SET GLOBAL event_scheduler= ON;
 CREATE EVENT bug28641 ON SCHEDULE AT '2038.01.18 03:00:00'
             DO BEGIN
@@ -618,3 +617,105 @@ SELECT 1;
 END;|
 SET GLOBAL event_scheduler= OFF;
 DROP EVENT bug28641;
+
+#####################################################################
+#
+# BUG#31111: --read-only crashes MySQL (events fail to load).
+#
+#####################################################################
+
+DROP USER mysqltest_u1@localhost;
+DROP EVENT IF EXISTS e1;
+DROP EVENT IF EXISTS e2;
+
+GRANT EVENT ON *.* TO mysqltest_u1@localhost;
+
+SET GLOBAL READ_ONLY = 1;
+
+#
+# Connection: u1_con (mysqltest_u1@localhost/events_test).
+#
+
+CREATE EVENT e1 ON SCHEDULE AT '2020-01-01 00:00:00' DO SET @a = 1;
+ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
+
+ALTER EVENT e1 COMMENT 'comment';
+ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
+
+DROP EVENT e1;
+ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
+
+#
+# Connection: root_con (root@localhost/events_test).
+#
+
+CREATE EVENT e1 ON SCHEDULE AT '2020-01-01 00:00:00' DO SET @a = 1;
+
+ALTER EVENT e1 COMMENT 'comment';
+
+DROP EVENT e1;
+
+SET GLOBAL READ_ONLY = 0;
+
+#
+# Connection: u1_con (mysqltest_u1@localhost/test).
+#
+
+CREATE EVENT e1 ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 SECOND DO SET @a = 1;
+CREATE EVENT e2 ON SCHEDULE EVERY 1 SECOND DO SET @a = 1;
+
+SELECT
+event_name,
+last_executed IS NULL,
+definer
+FROM INFORMATION_SCHEMA.EVENTS
+WHERE event_schema = 'events_test';
+event_name	last_executed IS NULL	definer
+e1	1	mysqltest_u1@localhost
+e2	1	mysqltest_u1@localhost
+
+#
+# Connection: root_con (root@localhost/events_test).
+#
+
+SET GLOBAL READ_ONLY = 1;
+
+SET GLOBAL EVENT_SCHEDULER = ON;
+
+# Waiting for the event scheduler to execute and drop event e1...
+
+# Waiting for the event scheduler to execute and update event e2...
+
+SET GLOBAL EVENT_SCHEDULER = OFF;
+
+SELECT
+event_name,
+last_executed IS NULL,
+definer
+FROM INFORMATION_SCHEMA.EVENTS
+WHERE event_schema = 'events_test';
+event_name	last_executed IS NULL	definer
+e2	0	mysqltest_u1@localhost
+
+DROP EVENT e1;
+ERROR HY000: Unknown event 'e1'
+
+# Cleanup.
+
+DROP EVENT e2;
+
+SET GLOBAL READ_ONLY = 0;
+
+#
+# Connection: default
+#
+
+DROP USER mysqltest_u1@localhost;
+
+#####################################################################
+#
+# End of BUG#31111.
+#
+#####################################################################
+
+DROP DATABASE events_test;
diff -Nrup a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test
--- a/mysql-test/t/events_bugs.test	2007-07-25 11:43:47 +04:00
+++ b/mysql-test/t/events_bugs.test	2007-10-19 19:57:06 +04:00
@@ -712,18 +712,6 @@ DROP TABLE event_log;
 #DROP DATABASE ev_db_1;
 SET GLOBAL event_scheduler = OFF;
 
-# 
-# End of tests
-#
-
-let $wait_condition=
-  select count(*) = 0 from information_schema.processlist
-  where db='events_test' and command = 'Connect' and user=current_user();
---source include/wait_condition.inc
-
-DROP DATABASE events_test;
-
-
 #
 # Bug#28641 CREATE EVENT with '2038.01.18 03:00:00' let server crash.
 #
@@ -737,3 +725,215 @@ CREATE EVENT bug28641 ON SCHEDULE AT '20
 DELIMITER ;|
 SET GLOBAL event_scheduler= OFF;
 DROP EVENT bug28641;
+
+###########################################################################
+
+--echo
+--echo #####################################################################
+--echo #
+--echo # BUG#31111: --read-only crashes MySQL (events fail to load).
+--echo #
+--echo #####################################################################
+--echo
+
+--error 0,ER_CANNOT_USER
+DROP USER mysqltest_u1@localhost;
+
+--disable_warnings
+DROP EVENT IF EXISTS e1;
+DROP EVENT IF EXISTS e2;
+--enable_warnings
+
+--echo
+
+# Check that an ordinary user can not create/update/drop events in the
+# read-only mode.
+
+GRANT EVENT ON *.* TO mysqltest_u1@localhost;
+
+--echo
+
+SET GLOBAL READ_ONLY = 1;
+
+--echo
+
+--echo #
+--echo # Connection: u1_con (mysqltest_u1@localhost/events_test).
+--echo #
+
+--connect(u1_con,localhost,mysqltest_u1,,events_test)
+
+--echo
+
+--error ER_OPTION_PREVENTS_STATEMENT
+CREATE EVENT e1 ON SCHEDULE AT '2020-01-01 00:00:00' DO SET @a = 1;
+
+--echo
+
+--error ER_OPTION_PREVENTS_STATEMENT
+ALTER EVENT e1 COMMENT 'comment';
+
+--echo
+
+--error ER_OPTION_PREVENTS_STATEMENT
+DROP EVENT e1;
+
+--echo
+
+# Check that the super user still can create/update/drop events.
+
+--echo #
+--echo # Connection: root_con (root@localhost/events_test).
+--echo #
+
+--connect(root_con,localhost,root,,events_test)
+
+--echo
+
+CREATE EVENT e1 ON SCHEDULE AT '2020-01-01 00:00:00' DO SET @a = 1;
+
+--echo
+
+ALTER EVENT e1 COMMENT 'comment';
+
+--echo
+
+DROP EVENT e1;
+
+--echo
+
+#
+# Switch to read-write mode; create test events under the user mysqltest_u1;
+# switch back to read-only mode.
+#
+
+SET GLOBAL READ_ONLY = 0;
+
+--echo
+
+--echo #
+--echo # Connection: u1_con (mysqltest_u1@localhost/test).
+--echo #
+
+--connection u1_con
+
+--echo
+
+CREATE EVENT e1 ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 SECOND DO SET @a = 1;
+CREATE EVENT e2 ON SCHEDULE EVERY 1 SECOND DO SET @a = 1;
+
+--echo
+
+SELECT
+  event_name,
+  last_executed IS NULL,
+  definer
+FROM INFORMATION_SCHEMA.EVENTS
+WHERE event_schema = 'events_test';
+
+--echo
+
+--echo #
+--echo # Connection: root_con (root@localhost/events_test).
+--echo #
+
+--connection root_con
+
+--echo
+
+SET GLOBAL READ_ONLY = 1;
+
+# Check that the event scheduler is able to update event.
+
+--echo
+
+SET GLOBAL EVENT_SCHEDULER = ON;
+
+--echo
+
+--echo # Waiting for the event scheduler to execute and drop event e1...
+
+let $wait_timeout = 2;
+let $wait_condition =
+  SELECT COUNT(*) = 0
+  FROM INFORMATION_SCHEMA.EVENTS
+  WHERE event_schema = 'events_test' AND event_name = 'e1';
+--source include/wait_condition.inc
+
+--echo
+
+--echo # Waiting for the event scheduler to execute and update event e2...
+
+let $wait_condition =
+  SELECT last_executed IS NOT NULL
+  FROM INFORMATION_SCHEMA.EVENTS
+  WHERE event_schema = 'events_test' AND event_name = 'e2';
+--source include/wait_condition.inc
+
+--echo
+
+SET GLOBAL EVENT_SCHEDULER = OFF;
+
+--echo
+
+SELECT
+  event_name,
+  last_executed IS NULL,
+  definer
+FROM INFORMATION_SCHEMA.EVENTS
+WHERE event_schema = 'events_test';
+
+--echo
+
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT e1;
+
+--echo
+--echo # Cleanup.
+--echo
+
+DROP EVENT e2;
+
+--echo
+
+SET GLOBAL READ_ONLY = 0;
+
+--echo
+
+--echo #
+--echo # Connection: default
+--echo #
+
+--disconnect u1_con
+--disconnect root_con
+--connection default
+
+--echo
+
+DROP USER mysqltest_u1@localhost;
+
+--echo
+--echo #####################################################################
+--echo #
+--echo # End of BUG#31111.
+--echo #
+--echo #####################################################################
+--echo
+
+
+###########################################################################
+# 
+# End of tests
+#
+# !!! KEEP this section AT THE END of this file !!!
+#
+###########################################################################
+
+let $wait_condition=
+  select count(*) = 0 from information_schema.processlist
+  where db='events_test' and command = 'Connect' and user=current_user();
+--source include/wait_condition.inc
+
+DROP DATABASE events_test;
+
+# THIS MUST BE THE LAST LINE in this file.
diff -Nrup a/sql/event_data_objects.cc b/sql/event_data_objects.cc
--- a/sql/event_data_objects.cc	2007-08-15 19:08:40 +04:00
+++ b/sql/event_data_objects.cc	2007-10-19 19:57:06 +04:00
@@ -2017,6 +2017,7 @@ end_no_lex_start:
       ret= 1;
     else
     {
+      ulong saved_master_access;
       /*
         Peculiar initialization order is a crutch to avoid races in SHOW
         PROCESSLIST which reads thd->{query/query_length} without a mutex.
@@ -2024,8 +2025,19 @@ end_no_lex_start:
       thd->query_length= 0;
       thd->query= sp_sql.c_ptr_safe();
       thd->query_length= sp_sql.length();
-      if (Events::drop_event(thd, dbname, name, FALSE))
-        ret= 1;
+
+      /*
+        NOTE: even if we run in read-only mode, we should be able to lock
+        the mysql.event table for writing. In order to achieve this, we
+        should call mysql_lock_tables() under the super-user.
+      */
+
+      saved_master_access= thd->security_ctx->master_access;
+      thd->security_ctx->master_access |= SUPER_ACL;
+
+      ret= Events::drop_event(thd, dbname, name, FALSE);
+
+      thd->security_ctx->master_access= saved_master_access;
     }
   }
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
diff -Nrup a/sql/event_db_repository.cc b/sql/event_db_repository.cc
--- a/sql/event_db_repository.cc	2007-08-25 12:43:10 +04:00
+++ b/sql/event_db_repository.cc	2007-10-19 19:57:06 +04:00
@@ -525,6 +525,10 @@ Event_db_repository::fill_schema_events(
   - whether this open mode would work under LOCK TABLES, or inside a
   stored function or trigger.
 
+  Note that if the table can't be locked successfully this operation will
+  close it. Therefore it provides guarantee that it either opens and locks
+  table or fails without leaving any tables open.
+
   @param[in]  thd  Thread context
   @param[in]  lock_type  How to lock the table
   @param[out] table  We will store the open table here
@@ -544,7 +548,10 @@ Event_db_repository::open_event_table(TH
   tables.init_one_table("mysql", "event", lock_type);
 
   if (simple_open_n_lock_tables(thd, &tables))
+  {
+    close_thread_tables(thd, FALSE, FALSE);
     DBUG_RETURN(TRUE);
+  }
 
   *table= tables.table;
   tables.table->use_all_columns();
@@ -994,6 +1001,8 @@ update_timing_fields_for_event(THD *thd,
   */
   if (thd->current_stmt_binlog_row_based)
     thd->clear_current_stmt_binlog_row_based();
+
+  DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);
 
   if (open_event_table(thd, TL_WRITE, &table))
     goto end;
diff -Nrup a/sql/event_scheduler.cc b/sql/event_scheduler.cc
--- a/sql/event_scheduler.cc	2007-08-15 19:08:40 +04:00
+++ b/sql/event_scheduler.cc	2007-10-19 19:57:06 +04:00
@@ -399,6 +399,13 @@ Event_scheduler::start()
   new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
   new_thd->command= COM_DAEMON;
 
+  /*
+    We should run the event scheduler thread under the super-user privileges.
+    In particular, this is needed to be able to lock the mysql.event table
+    for writing when the server is running in the read-only mode.
+  */
+  new_thd->security_ctx->master_access |= SUPER_ACL;
+
   scheduler_param_value=
     (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0));
   scheduler_param_value->thd= new_thd;
diff -Nrup a/sql/events.cc b/sql/events.cc
--- a/sql/events.cc	2007-08-15 19:08:40 +04:00
+++ b/sql/events.cc	2007-10-19 19:57:06 +04:00
@@ -1124,11 +1124,25 @@ Events::load_events_from_db(THD *thd)
   READ_RECORD read_record_info;
   bool ret= TRUE;
   uint count= 0;
+  ulong saved_master_access;
 
   DBUG_ENTER("Events::load_events_from_db");
   DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
 
-  if (db_repository->open_event_table(thd, TL_WRITE, &table))
+  /*
+    NOTE: even if we run in read-only mode, we should be able to lock the
+    mysql.event table for writing. In order to achieve this, we should call
+    mysql_lock_tables() under the super user.
+  */
+
+  saved_master_access= thd->security_ctx->master_access;
+  thd->security_ctx->master_access |= SUPER_ACL;
+
+  ret= db_repository->open_event_table(thd, TL_WRITE, &table);
+
+  thd->security_ctx->master_access= saved_master_access;
+
+  if (ret)
   {
     sql_print_error("Event Scheduler: Failed to open table mysql.event");
     DBUG_RETURN(TRUE);
diff -Nrup a/sql/sql_class.cc b/sql/sql_class.cc
--- a/sql/sql_class.cc	2007-10-19 02:57:41 +04:00
+++ b/sql/sql_class.cc	2007-10-19 19:57:06 +04:00
@@ -2404,6 +2404,7 @@ void Security_context::init()
   host= user= priv_user= ip= 0;
   host_or_ip= "connecting host";
   priv_host[0]= '\0';
+  master_access= 0;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   db_access= NO_ACCESS;
 #endif
Thread
bk commit into 5.1 tree (anozdrin:1.2593) BUG#31111Alexander Nozdrin19 Oct
  • Re: bk commit into 5.1 tree (anozdrin:1.2593) BUG#31111Dmitri Lenev19 Oct