Skip to content
  • Marc Alff's avatar
    78be5ef1
    Bug#27471510 PERFORMANCE_SCHEMA STATUS · 78be5ef1
    Marc Alff authored
      AND VARIABLE BY THREAD ARE NOT SAFE
    
    Patch for 5.7.
    
    Before this fix, executing:
      SELECT * FROM performance_schema.status_by_thread
      SELECT * FROM performance_schema.variables_by_thread
    under heavy load was unsafe.
    
    Root cause 1
    ============
    
    SELECT * from performance_schema.status_by_thread
    inspects the status variables of running sessions.
    
    For sessions using SSL, SSL status variables are inspected.
    
    For example, the status variable "Ssl_cipher_list" is evaluated
    by executing function show_ssl_get_cipher_list()
    
    This function is implemented as follows:
    
    show_ssl_get_cipher_list()
    {
      if (thd->get_protocol()->get_ssl()) // (a)
      {
        ...
        SSL_get_cipher(thd->get_protocol()->get_ssl())); // (b)
        ...
      }
    }
    
    The problem is that evaluating
      thd->get_protocol()->get_ssl()
    to access the underlying SSL structure is unsafe,
    and subject to race conditions.
    
    The value returned in (a) can change by the time (b)
    is evaluated, for example when using prepared statements,
    because the thd->get_protocol() pointer will change
    during execution of PREPARE and EXECUTE.
    
    Fix 1
    =====
    
    thd->get_protocol()->get_ssl()
    is not a proper way to access SSL data for the session.
    
    Instead, THD::m_SSL now keeps the SSL data attached to the
    THD session.
    
    THD::m_SSL is set after the SSL connection is established,
    is reset upon disconnect,
    and is immutable during the session execution.
    
    Inspecting this attribute is safe when LOCK_thd_data is held,
    which make table performance_schema.status_by_thread safe.
    
    Root cause 2
    ============
    
    SELECT * from performance_schema.variables_by_thread
    inspects the variables of running sessions.
    
    In particular, variable "session_track_system_variables"
    is inspected.
    
    This variable value is stored in THD::variables.track_sysvars_ptr
    
    On the session connection,
      THD::variables.track_sysvars_ptr
    is duplicated and points to allocated memory,
    in this call:
        thd->session_sysvar_res_mgr.init(&thd->variables.track_sysvars_ptr,
    thd->charset());
    
    This is because Session_sysvar_resource_manager::init()
    modifies the THD::variables.track_sysvars_ptr pointer itself.
    
    On session disconnect,
      Session_sysvar_resource_manager::deinit()
    free the allocated memory,
    which leaves the THD::variables.track_sysvars_ptr pointer
    invalid, referencing freed memory.
    
    As soon as cleanup_variables() unlocks LOCK_thd_data,
    a race condition is possible,
    when using the now invalid THD::variables.track_sysvars_ptr pointer.
    
    Fix 2
    =====
    
    In cleanup_variables(),
    clear the offending pointer before freeing the underlying memory
    with the call to session_sysvar_res_mgr.deinit()
    
    This makes table performance_schema.variables_by_thread safe.
    78be5ef1
    Bug#27471510 PERFORMANCE_SCHEMA STATUS
    Marc Alff authored
      AND VARIABLE BY THREAD ARE NOT SAFE
    
    Patch for 5.7.
    
    Before this fix, executing:
      SELECT * FROM performance_schema.status_by_thread
      SELECT * FROM performance_schema.variables_by_thread
    under heavy load was unsafe.
    
    Root cause 1
    ============
    
    SELECT * from performance_schema.status_by_thread
    inspects the status variables of running sessions.
    
    For sessions using SSL, SSL status variables are inspected.
    
    For example, the status variable "Ssl_cipher_list" is evaluated
    by executing function show_ssl_get_cipher_list()
    
    This function is implemented as follows:
    
    show_ssl_get_cipher_list()
    {
      if (thd->get_protocol()->get_ssl()) // (a)
      {
        ...
        SSL_get_cipher(thd->get_protocol()->get_ssl())); // (b)
        ...
      }
    }
    
    The problem is that evaluating
      thd->get_protocol()->get_ssl()
    to access the underlying SSL structure is unsafe,
    and subject to race conditions.
    
    The value returned in (a) can change by the time (b)
    is evaluated, for example when using prepared statements,
    because the thd->get_protocol() pointer will change
    during execution of PREPARE and EXECUTE.
    
    Fix 1
    =====
    
    thd->get_protocol()->get_ssl()
    is not a proper way to access SSL data for the session.
    
    Instead, THD::m_SSL now keeps the SSL data attached to the
    THD session.
    
    THD::m_SSL is set after the SSL connection is established,
    is reset upon disconnect,
    and is immutable during the session execution.
    
    Inspecting this attribute is safe when LOCK_thd_data is held,
    which make table performance_schema.status_by_thread safe.
    
    Root cause 2
    ============
    
    SELECT * from performance_schema.variables_by_thread
    inspects the variables of running sessions.
    
    In particular, variable "session_track_system_variables"
    is inspected.
    
    This variable value is stored in THD::variables.track_sysvars_ptr
    
    On the session connection,
      THD::variables.track_sysvars_ptr
    is duplicated and points to allocated memory,
    in this call:
        thd->session_sysvar_res_mgr.init(&thd->variables.track_sysvars_ptr,
    thd->charset());
    
    This is because Session_sysvar_resource_manager::init()
    modifies the THD::variables.track_sysvars_ptr pointer itself.
    
    On session disconnect,
      Session_sysvar_resource_manager::deinit()
    free the allocated memory,
    which leaves the THD::variables.track_sysvars_ptr pointer
    invalid, referencing freed memory.
    
    As soon as cleanup_variables() unlocks LOCK_thd_data,
    a race condition is possible,
    when using the now invalid THD::variables.track_sysvars_ptr pointer.
    
    Fix 2
    =====
    
    In cleanup_variables(),
    clear the offending pointer before freeing the underlying memory
    with the call to session_sysvar_res_mgr.deinit()
    
    This makes table performance_schema.variables_by_thread safe.
Loading