Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Client {
const char* interface, uint32_t version);
static void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name);
static void handleOutputDone(void*, struct zxdg_output_v1*);
void createBarsBatch();
static void handleOutputName(void*, struct zxdg_output_v1*, const char*);
static void handleOutputDescription(void*, struct zxdg_output_v1*, const char*);
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
Expand All @@ -58,6 +59,8 @@ class Client {
std::string m_cssFile;
sigc::connection monitor_added_connection_;
sigc::connection monitor_removed_connection_;
std::vector<waybar_output*> pending_outputs_;
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pending_outputs_ stores raw pointers to elements of outputs_, but outputs_ is erased in handleDeferredMonitorRemoval() and cleared in bindInterfaces(). If a monitor is removed (or a reload happens) before the scheduled batch runs, createBarsBatch() may dereference a dangling pointer. Consider clearing pending_outputs_/resetting bars_scheduled_ when outputs are cleared/removed, or store a stable key (e.g., monitor id) and re-resolve via getOutput() at batch time.

Suggested change
std::vector<waybar_output*> pending_outputs_;
std::vector<void*> pending_outputs_;

Copilot uses AI. Check for mistakes.
bool bars_scheduled_ = false;
};

} // namespace waybar
29 changes: 25 additions & 4 deletions src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,37 @@ void waybar::Client::handleOutputDone(void* data, struct zxdg_output_v1* /*xdg_o
output.xdg_output.reset();
spdlog::debug("Output detection done: {} ({})", output.name, output.identifier);

auto configs = client->getOutputConfigs(output);
client->pending_outputs_.push_back(&output);

if (!client->bars_scheduled_) {
client->bars_scheduled_ = true;

Glib::signal_idle().connect_once([client]() {
client->createBarsBatch();
}, Glib::PRIORITY_HIGH_IDLE);
}
}
} catch (const std::exception& e) {
spdlog::warn("caught exception in zxdg_output_v1_listener::done: {}", e.what());
}
}

void waybar::Client::createBarsBatch() {
for (auto* output : pending_outputs_) {
try {
auto configs = getOutputConfigs(*output);
if (!configs.empty()) {
for (const auto& config : configs) {
client->bars.emplace_back(std::make_unique<Bar>(&output, config));
bars.emplace_back(std::make_unique<Bar>(output, config));
}
Comment on lines +109 to 117
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createBarsBatch() dereferences pointers collected in pending_outputs_ without verifying they still refer to an element in outputs_. Since outputs can be removed asynchronously (see handleMonitorRemoved() -> deferred removal), this can become a use-after-free. Suggest re-validating each pointer (e.g., resolve via getOutput(static_cast<void*>(output)) and skip on failure) or using a representation that can't dangle.

Copilot uses AI. Check for mistakes.
}
} catch (const std::exception& e) {
spdlog::warn("Error creating bar: {}", e.what());
}
} catch (const std::exception& e) {
spdlog::warn("caught exception in zxdg_output_v1_listener::done: {}", e.what());
}

pending_outputs_.clear();
bars_scheduled_ = false;
}

void waybar::Client::handleOutputName(void* data, struct zxdg_output_v1* /*xdg_output*/,
Expand Down
Loading