Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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::list<waybar_output*> pending_outputs_;
bool bars_scheduled_ = false;
};

} // namespace waybar
30 changes: 26 additions & 4 deletions src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,38 @@ 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() {
pending_outputs_.remove_if([this](auto* output) { return std::none_of(outputs_.begin(), outputs_.end(), [&output](const auto& o) { return &o == output; }); });
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I think this fixes potential use-after-free but I could be wrong.

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