From bcdec9fa8be4243acd1fefa6daa4ee4248b55e88 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Wed, 21 Aug 2019 20:20:10 -0400 Subject: [PATCH] common/config_proxy: hold lock while accessing mutable container The 'call_gate_leave' method was accessing the 'obs_call_gate' map without holding the required lock. This data structure could be manipulated by another thread underneath the observer callback thread context. Fixes: https://tracker.ceph.com/issues/41354 Signed-off-by: Jason Dillaman (cherry picked from commit 0cc2dd7bd1021dd8b5e22850495ce97da7d98716) --- src/common/config_proxy.h | 84 +++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/src/common/config_proxy.h b/src/common/config_proxy.h index 51aeaa842d74e..ce7c35d83ce85 100644 --- a/src/common/config_proxy.h +++ b/src/common/config_proxy.h @@ -81,12 +81,17 @@ class ConfigProxy { std::map obs_call_gate; - void call_observers(rev_obs_map_t &rev_obs) { + void call_observers(std::unique_lock& locker, + rev_obs_map_t& rev_obs) { + // observers are notified outside of lock + locker.unlock(); for (auto& [obs, keys] : rev_obs) { obs->handle_conf_change(*this, keys); - // this can be done outside the lock as call_gate_enter() - // and remove_observer() are serialized via lock - call_gate_leave(obs); + } + locker.lock(); + + for (auto& rev_ob : rev_obs) { + call_gate_leave(rev_ob.first); } } @@ -184,16 +189,14 @@ public: } // for those want to reexpand special meta, e.g, $pid void finalize_reexpand_meta() { + std::unique_lock locker(lock); rev_obs_map_t rev_obs; - { - std::lock_guard l(lock); - if (config.finalize_reexpand_meta(values, obs_mgr)) { - _gather_changes(values.changed, &rev_obs, nullptr); - values.changed.clear(); - } + if (config.finalize_reexpand_meta(values, obs_mgr)) { + _gather_changes(values.changed, &rev_obs, nullptr); + values.changed.clear(); } - call_observers(rev_obs); + call_observers(locker, rev_obs); } void add_observer(md_config_obs_t* obs) { std::lock_guard l(lock); @@ -207,16 +210,14 @@ public: obs_mgr.remove_observer(obs); } void call_all_observers() { + std::unique_lock locker(lock); rev_obs_map_t rev_obs; - { - std::lock_guard l(lock); - obs_mgr.for_each_observer( - [this, &rev_obs](md_config_obs_t *obs, const std::string &key) { - map_observer_changes(obs, key, &rev_obs); - }); - } + obs_mgr.for_each_observer( + [this, &rev_obs](md_config_obs_t *obs, const std::string &key) { + map_observer_changes(obs, key, &rev_obs); + }); - call_observers(rev_obs); + call_observers(locker, rev_obs); } void set_safe_to_start_threads() { config.set_safe_to_start_threads(); @@ -242,18 +243,17 @@ public: } // Expand all metavariables. Make any pending observer callbacks. void apply_changes(std::ostream* oss) { + std::unique_lock locker(lock); rev_obs_map_t rev_obs; - { - std::lock_guard l{lock}; - // apply changes until the cluster name is assigned - if (!values.cluster.empty()) { - // meta expands could have modified anything. Copy it all out again. - _gather_changes(values.changed, &rev_obs, oss); - values.changed.clear(); - } + + // apply changes until the cluster name is assigned + if (!values.cluster.empty()) { + // meta expands could have modified anything. Copy it all out again. + _gather_changes(values.changed, &rev_obs, oss); + values.changed.clear(); } - call_observers(rev_obs); + call_observers(locker, rev_obs); } void _gather_changes(std::set &changes, rev_obs_map_t *rev_obs, std::ostream* oss) { @@ -279,29 +279,25 @@ public: int set_mon_vals(CephContext *cct, const map& kv, md_config_t::config_callback config_cb) { - int ret; + std::unique_lock locker(lock); + int ret = config.set_mon_vals(cct, values, obs_mgr, kv, config_cb); + rev_obs_map_t rev_obs; - { - std::lock_guard l{lock}; - ret = config.set_mon_vals(cct, values, obs_mgr, kv, config_cb); - _gather_changes(values.changed, &rev_obs, nullptr); - values.changed.clear(); - } + _gather_changes(values.changed, &rev_obs, nullptr); + values.changed.clear(); - call_observers(rev_obs); + call_observers(locker, rev_obs); return ret; } int injectargs(const std::string &s, std::ostream *oss) { - int ret; + std::unique_lock locker(lock); + int ret = config.injectargs(values, obs_mgr, s, oss); + rev_obs_map_t rev_obs; - { - std::lock_guard l{lock}; - ret = config.injectargs(values, obs_mgr, s, oss); - _gather_changes(values.changed, &rev_obs, oss); - values.changed.clear(); - } + _gather_changes(values.changed, &rev_obs, oss); + values.changed.clear(); - call_observers(rev_obs); + call_observers(locker, rev_obs); return ret; } void parse_env(unsigned entity_type, -- 2.39.5