From 89b5456cd3930d3bfa5a4782869732b3fb39385c Mon Sep 17 00:00:00 2001 From: Carlo Teubner Date: Sun, 24 May 2026 06:41:51 +0100 Subject: [PATCH 1/4] Sway IPC: fix socket path discovery Ensure to only read actual "sway --get-socketpath" output, not subsequent garbage in buffer. --- src/modules/sway/ipc/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 3ebccccd..53ad64a1 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -45,7 +45,7 @@ const std::string Ipc::getSocketPath() const { throw std::runtime_error("Failed to get socket path"); } while (fgets(buf, sizeof(buf), in) != nullptr) { - str_buf.append(buf, sizeof(buf)); + str_buf.append(buf); } pclose(in); str = str_buf; From 1bc633fc0ea454f1891d1de6f0c1aa857e9b4c34 Mon Sep 17 00:00:00 2001 From: Carlo Teubner Date: Sun, 24 May 2026 06:45:30 +0100 Subject: [PATCH 2/4] Sway IPC: more robust socket send/receive logic Handle EINTR/EAGAIN, partial sends, etc. --- src/modules/sway/ipc/client.cpp | 47 ++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 53ad64a1..fa6b31fd 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -1,11 +1,33 @@ #include "modules/sway/ipc/client.hpp" +#include #include #include #include +#include namespace waybar::modules::sway { +namespace { + +void sendAll(int fd, const char* data, size_t size, const char* what) { + size_t total = 0; + while (total < size) { + const auto res = ::send(fd, data + total, size - total, 0); + if (res < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + throw std::runtime_error(what); + } + if (res == 0) { + throw std::runtime_error(what); + } + total += static_cast(res); + } +} + +} // namespace Ipc::Ipc() { const std::string& socketPath = getSocketPath(); @@ -89,10 +111,16 @@ struct Ipc::ipc_response Ipc::recv(int fd) { // IPC is closed so just return an empty response return {0, 0, ""}; } - if (res <= 0) { + if (res < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } throw std::runtime_error("Unable to receive IPC header"); } - total += res; + if (res == 0) { + throw std::runtime_error("Unable to receive IPC header"); + } + total += static_cast(res); } auto magic = std::string(header.data(), header.data() + ipc_magic_.size()); if (magic != ipc_magic_) { @@ -110,9 +138,12 @@ struct Ipc::ipc_response Ipc::recv(int fd) { } throw std::runtime_error("Unable to receive IPC payload"); } - total += res; + if (res == 0) { + throw std::runtime_error("Unable to receive IPC payload"); + } + total += static_cast(res); } - return {data32[0], data32[1], &payload.front()}; + return {data32[0], data32[1], std::move(payload)}; } struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) { @@ -123,12 +154,8 @@ struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& pay data32[0] = payload.size(); data32[1] = type; - if (::send(fd, header.data(), ipc_header_size_, 0) == -1) { - throw std::runtime_error("Unable to send IPC header"); - } - if (::send(fd, payload.c_str(), payload.size(), 0) == -1) { - throw std::runtime_error("Unable to send IPC payload"); - } + sendAll(fd, header.data(), ipc_header_size_, "Unable to send IPC header"); + sendAll(fd, payload.data(), payload.size(), "Unable to send IPC payload"); return Ipc::recv(fd); } From d8848a478bdf2412c905f145d7311e27265c8841 Mon Sep 17 00:00:00 2001 From: Carlo Teubner Date: Sun, 24 May 2026 08:09:43 +0100 Subject: [PATCH 3/4] Sway IPC: code cleanups --- include/modules/sway/ipc/client.hpp | 13 ++--- src/modules/sway/ipc/client.cpp | 91 ++++++++++++++++------------- 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/include/modules/sway/ipc/client.hpp b/include/modules/sway/ipc/client.hpp index f6eb7c40..2665caf9 100644 --- a/include/modules/sway/ipc/client.hpp +++ b/include/modules/sway/ipc/client.hpp @@ -1,14 +1,10 @@ #pragma once #include -#include -#include -#include -#include -#include +#include +#include #include -#include #include #include "ipc.hpp" @@ -41,8 +37,9 @@ class Ipc { static inline const std::string ipc_magic_ = "i3-ipc"; static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8; - const std::string getSocketPath() const; - int open(const std::string&) const; + static std::string getSocketPath(); + static int open(const std::string&); + struct ipc_response send(int fd, uint32_t type, const std::string& payload = ""); struct ipc_response recv(int fd); diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index fa6b31fd..c833fe64 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -1,12 +1,22 @@ #include "modules/sway/ipc/client.hpp" -#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include "modules/sway/ipc/ipc.hpp" + namespace waybar::modules::sway { namespace { @@ -30,7 +40,7 @@ void sendAll(int fd, const char* data, size_t size, const char* what) { } // namespace Ipc::Ipc() { - const std::string& socketPath = getSocketPath(); + const std::string socketPath = getSocketPath(); fd_ = util::ScopedFd(open(socketPath)); fd_event_ = util::ScopedFd(open(socketPath)); } @@ -51,49 +61,49 @@ Ipc::~Ipc() { } } -void Ipc::setWorker(std::function&& func) { thread_ = func; } +void Ipc::setWorker(std::function&& func) { thread_ = std::move(func); } -const std::string Ipc::getSocketPath() const { +std::string Ipc::getSocketPath() { const char* env = getenv("SWAYSOCK"); - if (env != nullptr) { - return std::string(env); + if (env != nullptr && env[0] != '\0') { + return {env}; } + + FILE* in = popen("sway --get-socketpath 2>/dev/null", "r"); + if (in == nullptr) { + throw std::runtime_error("Failed to get socket path"); + } + std::string str; - { - std::string str_buf; - FILE* in; - char buf[512] = {0}; - if ((in = popen("sway --get-socketpath 2>/dev/null", "r")) == nullptr) { - throw std::runtime_error("Failed to get socket path"); - } - while (fgets(buf, sizeof(buf), in) != nullptr) { - str_buf.append(buf); - } - pclose(in); - str = str_buf; - if (str.empty()) { - throw std::runtime_error("Socket path is empty"); - } + char buf[512] = {0}; + while (fgets(buf, sizeof(buf), in) != nullptr) { + str.append(buf); } - if (str.back() == '\n') { + + if (pclose(in) == -1) { + throw std::runtime_error("Failed to get socket path"); + } + + if (str.ends_with('\n')) { str.pop_back(); } + if (str.empty()) { + throw std::runtime_error("Socket path is empty"); + } + return str; } -int Ipc::open(const std::string& socketPath) const { +int Ipc::open(const std::string& socketPath) { util::ScopedFd fd(socket(AF_UNIX, SOCK_STREAM, 0)); if (fd == -1) { throw std::runtime_error("Unable to open Unix socket"); } (void)fcntl(fd, F_SETFD, FD_CLOEXEC); - struct sockaddr_un addr; - memset(&addr, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; + struct sockaddr_un addr{.sun_family = AF_UNIX}; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; - int l = sizeof(struct sockaddr_un); - if (::connect(fd, reinterpret_cast(&addr), l) == -1) { + if (::connect(fd, reinterpret_cast(&addr), sizeof addr) == -1) { throw std::runtime_error("Unable to connect to Sway"); } return fd.release(); @@ -102,14 +112,13 @@ int Ipc::open(const std::string& socketPath) const { struct Ipc::ipc_response Ipc::recv(int fd) { std::string header; header.resize(ipc_header_size_); - auto data32 = reinterpret_cast(header.data() + ipc_magic_.size()); - size_t total = 0; + size_t total = 0; while (total < ipc_header_size_) { - auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0); + const ssize_t res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0); if (fd_event_ == -1 || fd_ == -1) { // IPC is closed so just return an empty response - return {0, 0, ""}; + return {.size = 0, .type = 0, .payload = ""}; } if (res < 0) { if (errno == EINTR || errno == EAGAIN) { @@ -122,16 +131,18 @@ struct Ipc::ipc_response Ipc::recv(int fd) { } total += static_cast(res); } - auto magic = std::string(header.data(), header.data() + ipc_magic_.size()); - if (magic != ipc_magic_) { + if (std::string_view(header.data(), ipc_magic_.size()) != ipc_magic_) { throw std::runtime_error("Invalid IPC magic"); } - total = 0; + const auto* data32 = reinterpret_cast(header.data() + ipc_magic_.size()); + std::string payload; payload.resize(data32[0]); + + total = 0; while (total < data32[0]) { - auto res = ::recv(fd, payload.data() + total, data32[0] - total, 0); + const ssize_t res = ::recv(fd, payload.data() + total, data32[0] - total, 0); if (res < 0) { if (errno == EINTR || errno == EAGAIN) { continue; @@ -143,19 +154,21 @@ struct Ipc::ipc_response Ipc::recv(int fd) { } total += static_cast(res); } - return {data32[0], data32[1], std::move(payload)}; + + return {.size = data32[0], .type = data32[1], .payload = std::move(payload)}; } struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) { std::string header; header.resize(ipc_header_size_); - auto data32 = reinterpret_cast(header.data() + ipc_magic_.size()); - memcpy(header.data(), ipc_magic_.c_str(), ipc_magic_.size()); + memcpy(header.data(), ipc_magic_.data(), ipc_magic_.size()); + auto* data32 = reinterpret_cast(header.data() + ipc_magic_.size()); data32[0] = payload.size(); data32[1] = type; sendAll(fd, header.data(), ipc_header_size_, "Unable to send IPC header"); sendAll(fd, payload.data(), payload.size(), "Unable to send IPC payload"); + return Ipc::recv(fd); } From 3056bad95a5911b528b1dbc29cfb6ab4367f0696 Mon Sep 17 00:00:00 2001 From: Carlo Teubner Date: Sun, 24 May 2026 08:13:05 +0100 Subject: [PATCH 4/4] Sway IPC: avoid reinterpret_cast --- src/modules/sway/ipc/client.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index c833fe64..dcbe7fa3 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -135,14 +136,18 @@ struct Ipc::ipc_response Ipc::recv(int fd) { throw std::runtime_error("Invalid IPC magic"); } - const auto* data32 = reinterpret_cast(header.data() + ipc_magic_.size()); + uint32_t payload_size = 0; + uint32_t payload_type = 0; + memcpy(&payload_size, header.data() + ipc_magic_.size(), sizeof payload_size); + memcpy(&payload_type, header.data() + ipc_magic_.size() + sizeof payload_size, + sizeof payload_type); std::string payload; - payload.resize(data32[0]); + payload.resize(payload_size); total = 0; - while (total < data32[0]) { - const ssize_t res = ::recv(fd, payload.data() + total, data32[0] - total, 0); + while (total < payload_size) { + const ssize_t res = ::recv(fd, payload.data() + total, payload_size - total, 0); if (res < 0) { if (errno == EINTR || errno == EAGAIN) { continue; @@ -155,16 +160,19 @@ struct Ipc::ipc_response Ipc::recv(int fd) { total += static_cast(res); } - return {.size = data32[0], .type = data32[1], .payload = std::move(payload)}; + return {.size = payload_size, .type = payload_type, .payload = std::move(payload)}; } struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) { std::string header; header.resize(ipc_header_size_); memcpy(header.data(), ipc_magic_.data(), ipc_magic_.size()); - auto* data32 = reinterpret_cast(header.data() + ipc_magic_.size()); - data32[0] = payload.size(); - data32[1] = type; + if (payload.size() > std::numeric_limits::max()) { + throw std::runtime_error("IPC payload is too large"); + } + const auto payload_size = static_cast(payload.size()); + memcpy(header.data() + ipc_magic_.size(), &payload_size, sizeof payload_size); + memcpy(header.data() + ipc_magic_.size() + sizeof payload_size, &type, sizeof type); sendAll(fd, header.data(), ipc_header_size_, "Unable to send IPC header"); sendAll(fd, payload.data(), payload.size(), "Unable to send IPC payload");