summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authornyx <nnyyxxxx@protonmail.com>2025-02-15 14:03:37 -0500
committerGitHub <noreply@github.com>2025-02-15 20:03:37 +0100
commit9228116c9aa682477aae1a52d2d7d023bb075352 (patch)
treeaea45053ca19d777c17b9e326c4afc8957567c93 /src
parent410da2e46fc44d93196cd902a070391a416cff01 (diff)
xwayland: fix a possible clipboard race condition (#9394)
Diffstat (limited to 'src')
-rw-r--r--src/xwayland/XDataSource.cpp15
-rw-r--r--src/xwayland/XWM.cpp81
-rw-r--r--src/xwayland/XWM.hpp2
3 files changed, 73 insertions, 25 deletions
diff --git a/src/xwayland/XDataSource.cpp b/src/xwayland/XDataSource.cpp
index 9384db69..003f6c9f 100644
--- a/src/xwayland/XDataSource.cpp
+++ b/src/xwayland/XDataSource.cpp
@@ -71,19 +71,20 @@ void CXDataSource::send(const std::string& mime, CFileDescriptor fd) {
Debug::log(LOG, "[XDataSource] send with mime {} to fd {}", mime, fd.get());
- selection.transfer = makeUnique<SXTransfer>(selection);
- selection.transfer->incomingWindow = xcb_generate_id(g_pXWayland->pWM->connection);
- const uint32_t MASK = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE;
- xcb_create_window(g_pXWayland->pWM->connection, XCB_COPY_FROM_PARENT, selection.transfer->incomingWindow, g_pXWayland->pWM->screen->root, 0, 0, 10, 10, 0,
- XCB_WINDOW_CLASS_INPUT_OUTPUT, g_pXWayland->pWM->screen->root_visual, XCB_CW_EVENT_MASK, &MASK);
+ auto transfer = makeUnique<SXTransfer>(selection);
+ transfer->incomingWindow = xcb_generate_id(g_pXWayland->pWM->connection);
+ const uint32_t MASK = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_create_window(g_pXWayland->pWM->connection, XCB_COPY_FROM_PARENT, transfer->incomingWindow, g_pXWayland->pWM->screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ g_pXWayland->pWM->screen->root_visual, XCB_CW_EVENT_MASK, &MASK);
- xcb_convert_selection(g_pXWayland->pWM->connection, selection.transfer->incomingWindow, HYPRATOMS["CLIPBOARD"], mimeAtom, HYPRATOMS["_WL_SELECTION"], XCB_TIME_CURRENT_TIME);
+ xcb_convert_selection(g_pXWayland->pWM->connection, transfer->incomingWindow, HYPRATOMS["CLIPBOARD"], mimeAtom, HYPRATOMS["_WL_SELECTION"], XCB_TIME_CURRENT_TIME);
xcb_flush(g_pXWayland->pWM->connection);
//TODO: make CFileDescriptor setflags take SETFL aswell
fcntl(fd.get(), F_SETFL, O_WRONLY | O_NONBLOCK);
- selection.transfer->wlFD = std::move(fd);
+ transfer->wlFD = std::move(fd);
+ selection.transfers.emplace_back(std::move(transfer));
}
void CXDataSource::accepted(const std::string& mime) {
diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp
index 68552dd6..f2a9b794 100644
--- a/src/xwayland/XWM.cpp
+++ b/src/xwayland/XWM.cpp
@@ -571,9 +571,10 @@ void CXWM::handleSelectionNotify(xcb_selection_notify_event_t* e) {
SXSelection* sel = getSelection(e->selection);
if (e->property == XCB_ATOM_NONE) {
- if (sel->transfer) {
+ auto it = std::ranges::find_if(sel->transfers, [](const auto& t) { return !t->propertyReply; });
+ if (it != sel->transfers.end()) {
Debug::log(TRACE, "[xwm] converting selection failed");
- sel->transfer.reset();
+ sel->transfers.erase(it);
}
} else if (e->target == HYPRATOMS["TARGETS"] && sel == &clipboard) {
if (!focusedSurface) {
@@ -582,7 +583,7 @@ void CXWM::handleSelectionNotify(xcb_selection_notify_event_t* e) {
}
setClipboardToWayland(*sel);
- } else if (sel->transfer)
+ } else if (!sel->transfers.empty())
getTransferData(*sel);
}
@@ -1177,17 +1178,50 @@ static int writeDataSource(int fd, uint32_t mask, void* data) {
void CXWM::getTransferData(SXSelection& sel) {
Debug::log(LOG, "[xwm] getTransferData");
- sel.transfer->getIncomingSelectionProp(true);
+ auto it = std::ranges::find_if(sel.transfers, [](const auto& t) { return !t->propertyReply; });
+ if (it == sel.transfers.end()) {
+ Debug::log(ERR, "[xwm] No pending transfer found");
+ return;
+ }
+
+ auto& transfer = *it;
+ if (!transfer || !transfer->incomingWindow) {
+ Debug::log(ERR, "[xwm] Invalid transfer state");
+ sel.transfers.erase(it);
+ return;
+ }
+
+ if (!transfer->getIncomingSelectionProp(true)) {
+ Debug::log(ERR, "[xwm] Failed to get property data");
+ sel.transfers.erase(it);
+ return;
+ }
+
+ if (!transfer->propertyReply) {
+ Debug::log(ERR, "[xwm] No property reply");
+ sel.transfers.erase(it);
+ return;
+ }
- if (sel.transfer->propertyReply->type == HYPRATOMS["INCR"]) {
+ if (transfer->propertyReply->type == HYPRATOMS["INCR"]) {
Debug::log(ERR, "[xwm] Transfer is INCR, which we don't support :(");
- sel.transfer.reset();
+ sel.transfers.erase(it);
return;
- } else {
- sel.onWrite();
- if (sel.transfer)
- sel.transfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, sel.transfer->wlFD.get(), WL_EVENT_WRITABLE, ::writeDataSource, &sel);
}
+
+ const size_t pos = std::distance(sel.transfers.begin(), it);
+ sel.onWrite();
+
+ if (pos >= sel.transfers.size())
+ return;
+
+ auto newIt = sel.transfers.begin() + pos;
+ if (newIt == sel.transfers.end() || !(*newIt))
+ return;
+
+ auto& updatedTransfer = *newIt;
+ if (updatedTransfer->eventSource && updatedTransfer->wlFD.get() != -1)
+ updatedTransfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, updatedTransfer->wlFD.get(), WL_EVENT_WRITABLE, ::writeDataSource, &sel);
}
void CXWM::setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot) {
@@ -1290,16 +1324,21 @@ void SXSelection::onKeyboardFocus() {
}
int SXSelection::onRead(int fd, uint32_t mask) {
- // TODO: support INCR
+ auto it = std::ranges::find_if(transfers, [fd](const auto& t) { return t->wlFD.get() == fd; });
+ if (it == transfers.end()) {
+ Debug::log(ERR, "[xwm] No transfer found for fd {}", fd);
+ return 0;
+ }
- size_t pre = transfer->data.size();
+ auto& transfer = *it;
+ size_t pre = transfer->data.size();
transfer->data.resize(INCR_CHUNK_SIZE + pre);
auto len = read(fd, transfer->data.data() + pre, INCR_CHUNK_SIZE - 1);
if (len < 0) {
Debug::log(ERR, "[xwm] readDataSource died");
g_pXWayland->pWM->selectionSendNotify(&transfer->request, false);
- transfer.reset();
+ transfers.erase(it);
return 0;
}
@@ -1311,7 +1350,7 @@ int SXSelection::onRead(int fd, uint32_t mask) {
transfer->data.size(), transfer->data.data());
xcb_flush(g_pXWayland->pWM->connection);
g_pXWayland->pWM->selectionSendNotify(&transfer->request, true);
- transfer.reset();
+ transfers.erase(it);
} else
Debug::log(LOG, "[xwm] Received {} bytes, waiting...", len);
@@ -1346,7 +1385,7 @@ bool SXSelection::sendData(xcb_selection_request_event_t* e, std::string mime) {
mime = *MIMES.begin();
}
- transfer = makeUnique<SXTransfer>(*this);
+ auto transfer = makeUnique<SXTransfer>(*this);
transfer->request = *e;
int p[2];
@@ -1368,18 +1407,26 @@ bool SXSelection::sendData(xcb_selection_request_event_t* e, std::string mime) {
selection->send(mime, CFileDescriptor{p[1]});
transfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, transfer->wlFD.get(), WL_EVENT_READABLE, ::readDataSource, this);
+ transfers.emplace_back(std::move(transfer));
return true;
}
int SXSelection::onWrite() {
+ auto it = std::ranges::find_if(transfers, [](const auto& t) { return t->propertyReply; });
+ if (it == transfers.end()) {
+ Debug::log(ERR, "[xwm] No transfer with property data found");
+ return 0;
+ }
+
+ auto& transfer = *it;
char* property = (char*)xcb_get_property_value(transfer->propertyReply);
int remainder = xcb_get_property_value_length(transfer->propertyReply) - transfer->propertyStart;
ssize_t len = write(transfer->wlFD.get(), property + transfer->propertyStart, remainder);
if (len == -1) {
Debug::log(ERR, "[xwm] write died in transfer get");
- transfer.reset();
+ transfers.erase(it);
return 0;
}
@@ -1388,7 +1435,7 @@ int SXSelection::onWrite() {
Debug::log(LOG, "[xwm] wl client read partially: len {}", len);
} else {
Debug::log(LOG, "[xwm] cb transfer to wl client complete, read {} bytes", len);
- transfer.reset();
+ transfers.erase(it);
}
return 1;
diff --git a/src/xwayland/XWM.hpp b/src/xwayland/XWM.hpp
index 73a02a86..f6dcee54 100644
--- a/src/xwayland/XWM.hpp
+++ b/src/xwayland/XWM.hpp
@@ -58,7 +58,7 @@ struct SXSelection {
CHyprSignalListener keyboardFocusChange;
} listeners;
- UP<SXTransfer> transfer;
+ std::vector<UP<SXTransfer>> transfers;
};
class CXCBConnection {