privacy: disconnect pending timeouts on destruction

The privacy module registers timeout callbacks in the GLib main context.
The GeoClue callback uses sigc::mem_fun() with the Privacy object, but the
connection remains active when the module is destroyed during a bar reload.

If the timeout runs after destruction, it dereferences the freed Privacy
object and crashes Waybar:

  #0 waybar::modules::privacy::Privacy::locationTimeout(bool)
  #4 g_main_context_iteration()
  #5 g_application_run()

Disconnect the GeoClue and visibility timeout connections in the Privacy
destructor so no callback can run after the module is gone.

The unpatched binary crashed after four SIGUSR2 reloads at 1.5-second
intervals. The patched binary survived ten reloads.

Fixes: eeb7bc702e ("Added GeoClue2 privacy item")
Assisted-by: Claude Opus + Cursor
This commit is contained in:
Shivank Garg
2026-08-13 01:07:14 +05:30
committed by Shivank Garg
parent 084d87401d
commit 38cc11c3b0
2 changed files with 10 additions and 0 deletions
+1
View File
@@ -17,6 +17,7 @@ namespace waybar::modules::privacy {
class Privacy : public AModule { class Privacy : public AModule {
public: public:
Privacy(const std::string&, const Json::Value&, Gtk::Orientation, const std::string& pos); Privacy(const std::string&, const Json::Value&, Gtk::Orientation, const std::string& pos);
~Privacy() override;
auto update() -> void override; auto update() -> void override;
private: private:
+9
View File
@@ -119,6 +119,15 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, Gtk::Orientat
sigc::mem_fun(*this, &Privacy::onGeoCluePrivacyNodesChanged)); sigc::mem_fun(*this, &Privacy::onGeoCluePrivacyNodesChanged));
} }
Privacy::~Privacy() {
// Both timeouts are attached to the global main context, which outlives this
// module when the bar is reloaded. sigc::connection's destructor does not
// disconnect, and the members are torn down after the backends they use, so a
// pending timeout would dispatch onto freed memory.
geoclue_timeout_conn.disconnect();
visibility_conn.disconnect();
}
void Privacy::onPWPrivacyNodesChanged() { void Privacy::onPWPrivacyNodesChanged() {
mutex_.lock(); mutex_.lock();
nodes_audio_out.clear(); nodes_audio_out.clear();