xwayland/xwm: prevent onWrite infinite loop and clean orphan transfers (#13122)
Fixes #11411 - Add return 0 after erasing completed non-incremental transfer to stop event source polling - Add removeTransfer() helper to SXSelection for cleaning transfers by window ID - Add removeTransfersForWindow() helper to CXWM for cleaning all selections at once - Clean orphan transfers in handleDestroy before surface removal - Clean orphan transfers in handlePropertyNotify on missing window or failed reply - Add m_dndSelection to handleSelectionPropertyNotify cleanup loop - Initialize SXTransfer members with safe defaults to prevent undefined behavior - Fix race condition in getTransferData by using window ID lookup instead of index
This commit is contained in:
parent
7d209b2941
commit
c92fb5e85f
2 changed files with 33 additions and 11 deletions
|
|
@ -66,6 +66,8 @@ void CXWM::handleCreate(xcb_create_notify_event_t* e) {
|
|||
}
|
||||
|
||||
void CXWM::handleDestroy(xcb_destroy_notify_event_t* e) {
|
||||
removeTransfersForWindow(e->window);
|
||||
|
||||
const auto XSURF = windowForXID(e->window);
|
||||
|
||||
if (!XSURF)
|
||||
|
|
@ -341,14 +343,17 @@ void CXWM::readProp(SP<CXWaylandSurface> XSURF, uint32_t atom, xcb_get_property_
|
|||
void CXWM::handlePropertyNotify(xcb_property_notify_event_t* e) {
|
||||
const auto XSURF = windowForXID(e->window);
|
||||
|
||||
if (!XSURF)
|
||||
if (!XSURF) {
|
||||
removeTransfersForWindow(e->window);
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property(getConnection(), 0, XSURF->m_xID, e->atom, XCB_ATOM_ANY, 0, 2048);
|
||||
XCBReplyPtr<xcb_get_property_reply_t> reply(xcb_get_property_reply(getConnection(), cookie, nullptr));
|
||||
|
||||
if (!reply) {
|
||||
Log::logger->log(Log::ERR, "[xwm] Failed to read property notify cookie");
|
||||
Log::logger->log(Log::ERR, "[xwm] Failed to read property notify cookie for window {}", e->window);
|
||||
removeTransfersForWindow(e->window);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -646,7 +651,7 @@ bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) {
|
|||
if (e->state != XCB_PROPERTY_DELETE)
|
||||
return false;
|
||||
|
||||
for (auto* sel : {&m_clipboard, &m_primarySelection}) {
|
||||
for (auto* sel : {&m_clipboard, &m_primarySelection, &m_dndSelection}) {
|
||||
auto it = std::ranges::find_if(sel->transfers, [e](const auto& t) { return t->incomingWindow == e->window; });
|
||||
if (it != sel->transfers.end()) {
|
||||
if (!(*it)->getIncomingSelectionProp(true)) {
|
||||
|
|
@ -1315,20 +1320,23 @@ void CXWM::getTransferData(SXSelection& sel) {
|
|||
return;
|
||||
}
|
||||
|
||||
const size_t transferIndex = std::distance(sel.transfers.begin(), it);
|
||||
int writeResult = sel.onWrite();
|
||||
// Store window ID before onWrite() - transfer may be erased during the call
|
||||
const xcb_window_t targetWindow = transfer->incomingWindow;
|
||||
int writeResult = sel.onWrite();
|
||||
|
||||
if (writeResult != 1)
|
||||
return;
|
||||
|
||||
if (transferIndex >= sel.transfers.size())
|
||||
// Re-find the transfer by window ID (safe after potential vector modification)
|
||||
auto updatedIt = std::ranges::find_if(sel.transfers, [targetWindow](const auto& t) { return t->incomingWindow == targetWindow; });
|
||||
if (updatedIt == sel.transfers.end())
|
||||
return;
|
||||
|
||||
Hyprutils::Memory::CUniquePointer<SXTransfer>& updatedTransfer = sel.transfers[transferIndex];
|
||||
auto& updatedTransfer = *updatedIt;
|
||||
if (!updatedTransfer)
|
||||
return;
|
||||
|
||||
if (updatedTransfer->eventSource && updatedTransfer->wlFD.get() == -1)
|
||||
if (updatedTransfer->eventSource || updatedTransfer->wlFD.get() == -1)
|
||||
return;
|
||||
|
||||
updatedTransfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, updatedTransfer->wlFD.get(), WL_EVENT_WRITABLE, ::writeDataSource, &sel);
|
||||
|
|
@ -1607,6 +1615,7 @@ int SXSelection::onWrite() {
|
|||
Log::logger->log(Log::DEBUG, "[xwm] cb transfer to wl client complete, read {} bytes", len);
|
||||
if (!transfer->incremental) {
|
||||
transfers.erase(it);
|
||||
return 0;
|
||||
} else {
|
||||
free(transfer->propertyReply); // NOLINT(cppcoreguidelines-no-malloc)
|
||||
transfer->propertyReply = nullptr;
|
||||
|
|
@ -1617,6 +1626,16 @@ int SXSelection::onWrite() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
void SXSelection::removeTransfer(xcb_window_t window) {
|
||||
std::erase_if(transfers, [window](const auto& t) { return t->incomingWindow == window; });
|
||||
}
|
||||
|
||||
void CXWM::removeTransfersForWindow(xcb_window_t window) {
|
||||
m_clipboard.removeTransfer(window);
|
||||
m_primarySelection.removeTransfer(window);
|
||||
m_dndSelection.removeTransfer(window);
|
||||
}
|
||||
|
||||
SXTransfer::~SXTransfer() {
|
||||
if (eventSource)
|
||||
wl_event_source_remove(eventSource);
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ struct SXTransfer {
|
|||
|
||||
xcb_selection_request_event_t request;
|
||||
|
||||
int propertyStart;
|
||||
xcb_get_property_reply_t* propertyReply;
|
||||
xcb_window_t incomingWindow;
|
||||
int propertyStart = 0;
|
||||
xcb_get_property_reply_t* propertyReply = nullptr;
|
||||
xcb_window_t incomingWindow = 0;
|
||||
|
||||
bool getIncomingSelectionProp(bool erase);
|
||||
};
|
||||
|
|
@ -54,6 +54,7 @@ struct SXSelection {
|
|||
bool sendData(xcb_selection_request_event_t* e, std::string mime);
|
||||
int onRead(int fd, uint32_t mask);
|
||||
int onWrite();
|
||||
void removeTransfer(xcb_window_t window);
|
||||
|
||||
struct {
|
||||
CHyprSignalListener setSelection;
|
||||
|
|
@ -164,6 +165,8 @@ class CXWM {
|
|||
void handleFocusOut(xcb_focus_out_event_t* e);
|
||||
void handleError(xcb_value_error_t* e);
|
||||
|
||||
void removeTransfersForWindow(xcb_window_t window);
|
||||
|
||||
bool handleSelectionEvent(xcb_generic_event_t* e);
|
||||
void handleSelectionNotify(xcb_selection_notify_event_t* e);
|
||||
bool handleSelectionPropertyNotify(xcb_property_notify_event_t* e);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue