From 802d7a6a3543cfee9e90cfb7d9bd63e9ce7bb514 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Mon, 28 Sep 2026 18:03:37 +0100 Subject: [PATCH 1/6] fix(linux): use drmIoctl calls within privileged drm_worker thread * Implement privileged_drmIoctl() call for drm_worker * Ensure that privileged calls that are thrown also produce logs. * Add doxygen briefs for new and existing wrapped functions. --- src/platform/linux/kmsgrab.cpp | 68 +++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index 909b84a06a0..7b90fabfc5f 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -95,33 +95,79 @@ namespace platf { instance().drop_privileges(); } - // deliberately align prototype to match path signature via init(const char *path) + /** + * @brief Open a DRM card file descriptor in the privileged worker thread. + * Note: prototype aligned to match path signature via init(const char *path). + * + * @param path Path to the DRM device node. + * @return File descriptor on success, or -1 on failure. + */ static int open_drm_card_fd_privileged(const char *path) { try { return instance().run([path] { return platf::open_drm_card_fd(path); }); - } catch (const privileged_drm_worker_stopped &) { + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); return -1; } } + /** + * @brief Wrap drmIoctl call in privileged worker thread. + * + * @param fd File descriptor + * @param request Request data. + * @param arg Argument + * @return Standard drmIoctl return codes passthrough when wrapped or -1 when thrown. + */ + template + static int drmIoctl_privileged(int fd, unsigned long request, T *arg) { + try { + return instance().run([fd, request, arg] { + return drmIoctl(fd, request, arg); + }); + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); + return -1; + } catch (...) { + BOOST_LOG(error) << "privileged_drm_worker: unknown error in "sv << __func__; + return -1; + } + } + + /** + * @brief Wrap drmModeGetFB2 call in privileged worker thread. + * + * @param fd DRM file descriptor. + * @param bufferId Framebuffer ID. + * @return Pointer to drmModeFB2 structure on success, or nullptr on failure. + */ static drmModeFB2Ptr drmModeGetFB2_privileged(int fd, uint32_t bufferId) { try { return instance().run([fd, bufferId] { return drmModeGetFB2(fd, bufferId); }); - } catch (const privileged_drm_worker_stopped &) { + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); return nullptr; } } + /** + * @brief Wrap drmModeGetFB call in privileged worker thread. + * + * @param fd DRM file descriptor. + * @param bufferId Framebuffer ID. + * @return Pointer to drmModeFB structure on success, or nullptr on failure. + */ static drmModeFBPtr drmModeGetFB_privileged(int fd, uint32_t bufferId) { try { return instance().run([fd, bufferId] { return drmModeGetFB(fd, bufferId); }); - } catch (const privileged_drm_worker_stopped &) { + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); return nullptr; } } @@ -164,7 +210,7 @@ namespace platf { } template - auto run(F &&f) -> std::invoke_result_t { + auto run(F &&f, const std::source_location &loc = std::source_location::current()) -> std::invoke_result_t { using R = std::invoke_result_t; auto task = std::make_shared>( [f = std::forward(f)]() mutable -> R { @@ -179,7 +225,9 @@ namespace platf { if (!queue_.raise([task]() mutable { (*task)(); })) { - throw privileged_drm_worker_stopped {"privileged_drm_worker: task rejected (worker stopping)"}; + throw privileged_drm_worker_stopped { + "privileged_drm_worker: task rejected in "s + loc.function_name() + " (worker stopping)"s + }; } return fut.get(); @@ -271,7 +319,7 @@ namespace platf { struct drm_gem_close close_args = {}; close_args.handle = handle; - drmIoctl(card_fd, DRM_IOCTL_GEM_CLOSE, &close_args); + platf::kms::privileged_drm_worker::drmIoctl_privileged(card_fd, DRM_IOCTL_GEM_CLOSE, &close_args); } }); @@ -1473,7 +1521,7 @@ namespace platf { if (mapped_data == MAP_FAILED && errno == ENOSYS) { drm_mode_map_dumb map = {}; map.handle = fb->handles[0]; - if (drmIoctl(card.fd.el, DRM_IOCTL_MODE_MAP_DUMB, &map) < 0) { + if (platf::kms::privileged_drm_worker::drmIoctl_privileged(card.fd.el, DRM_IOCTL_MODE_MAP_DUMB, &map) < 0) { BOOST_LOG(error) << "Failed to map cursor FB as dumb buffer: "sv << strerror(errno); captured_cursor.visible = false; return; @@ -1493,7 +1541,7 @@ namespace platf { // Prepare to read the dmabuf from the CPU struct dma_buf_sync sync; sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ; - drmIoctl(plane_fd.el, DMA_BUF_IOCTL_SYNC, &sync); + platf::kms::privileged_drm_worker::drmIoctl_privileged(plane_fd.el, DMA_BUF_IOCTL_SYNC, &sync); // If the image is tightly packed, copy it in one shot if (fb->pitches[0] == src_w * 4 && src_x == 0) { @@ -1508,7 +1556,7 @@ namespace platf { // End the CPU read and unmap the dmabuf sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ; - drmIoctl(plane_fd.el, DMA_BUF_IOCTL_SYNC, &sync); + platf::kms::privileged_drm_worker::drmIoctl_privileged(plane_fd.el, DMA_BUF_IOCTL_SYNC, &sync); munmap(mapped_data, mapped_size); From d2b5362dcc70ae18bec1d17d6eebccd312de97a7 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Mon, 28 Sep 2026 20:22:25 +0100 Subject: [PATCH 2/6] fixup: capture errno values correctly --- src/platform/linux/kmsgrab.cpp | 63 +++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index 7b90fabfc5f..d554d30c155 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -95,6 +95,22 @@ namespace platf { instance().drop_privileges(); } + /** + * @brief Run a function via the privileged worker and capture its errno value. + * + * @param f Function to execute. + * @return Pair containing the function result and worker thread errno value. + */ + template + static auto run_with_errno(F &&f) { + return instance().run([f = std::forward(f)]() mutable { + errno = 0; + const auto result = f(); + const auto errno_value = errno; + return std::pair {result, errno_value}; + }); + } + /** * @brief Open a DRM card file descriptor in the privileged worker thread. * Note: prototype aligned to match path signature via init(const char *path). @@ -104,9 +120,15 @@ namespace platf { */ static int open_drm_card_fd_privileged(const char *path) { try { - return instance().run([path] { + const auto [result, errno_value] = run_with_errno([path] { return platf::open_drm_card_fd(path); }); + + if (!result) { + errno = errno_value; + } + + return result; } catch (const privileged_drm_worker_stopped &err) { BOOST_LOG(error) << err.what(); return -1; @@ -124,15 +146,18 @@ namespace platf { template static int drmIoctl_privileged(int fd, unsigned long request, T *arg) { try { - return instance().run([fd, request, arg] { + const auto [result, errno_value] = run_with_errno([fd, request, arg] { return drmIoctl(fd, request, arg); }); + + if (result < 0) { + errno = errno_value; + } + + return result; } catch (const privileged_drm_worker_stopped &err) { BOOST_LOG(error) << err.what(); return -1; - } catch (...) { - BOOST_LOG(error) << "privileged_drm_worker: unknown error in "sv << __func__; - return -1; } } @@ -145,9 +170,16 @@ namespace platf { */ static drmModeFB2Ptr drmModeGetFB2_privileged(int fd, uint32_t bufferId) { try { - return instance().run([fd, bufferId] { + const auto [result, errno_value] = run_with_errno([fd, bufferId] { return drmModeGetFB2(fd, bufferId); }); + + if (!result) { + errno = errno_value; + } + + return result; + } catch (const privileged_drm_worker_stopped &err) { BOOST_LOG(error) << err.what(); return nullptr; @@ -163,9 +195,16 @@ namespace platf { */ static drmModeFBPtr drmModeGetFB_privileged(int fd, uint32_t bufferId) { try { - return instance().run([fd, bufferId] { + const auto [result, errno_value] = run_with_errno([fd, bufferId] { return drmModeGetFB(fd, bufferId); }); + + if (!result) { + errno = errno_value; + } + + return result; + } catch (const privileged_drm_worker_stopped &err) { BOOST_LOG(error) << err.what(); return nullptr; @@ -319,7 +358,15 @@ namespace platf { struct drm_gem_close close_args = {}; close_args.handle = handle; - platf::kms::privileged_drm_worker::drmIoctl_privileged(card_fd, DRM_IOCTL_GEM_CLOSE, &close_args); + try { + platf::kms::privileged_drm_worker::drmIoctl_privileged( + card_fd, + DRM_IOCTL_GEM_CLOSE, + &close_args + ); + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); + } } }); From 5eeee16e80cdd91a8460f2eaaa3cd54504240934 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Mon, 28 Sep 2026 21:06:20 +0100 Subject: [PATCH 3/6] fixup: address Sonar exception --- src/platform/linux/kmsgrab.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index d554d30c155..ca92b7cd088 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -353,27 +353,30 @@ namespace platf { } ~wrapper_fb() { - std::ranges::for_each(handles, [&](auto &handle) { - if (handle) { - struct drm_gem_close close_args = {}; - close_args.handle = handle; + try { + std::ranges::for_each(handles, [&](auto &handle) { + if (handle) { + struct drm_gem_close close_args = {}; + close_args.handle = handle; - try { platf::kms::privileged_drm_worker::drmIoctl_privileged( card_fd, DRM_IOCTL_GEM_CLOSE, &close_args ); - } catch (const privileged_drm_worker_stopped &err) { - BOOST_LOG(error) << err.what(); } + }); + + if (fb) { + drmModeFreeFB(fb); + } else if (fb2) { + drmModeFreeFB2(fb2); } - }); - if (fb) { - drmModeFreeFB(fb); - } else if (fb2) { - drmModeFreeFB2(fb2); + } catch (const std::exception &err) { + BOOST_LOG(error) << "Exception during DRM framebuffer cleanup: "sv << err.what(); + } catch (...) { + BOOST_LOG(error) << "Exception during DRM framebuffer cleanup: unknown exception"sv; } } From 7742830793f6fd83d3dcb47020986f4686d28852 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Tue, 29 Sep 2026 05:02:20 +0100 Subject: [PATCH 4/6] fixup: add drmSetClientCap, drmModeGetPlaneResources --- src/platform/linux/kmsgrab.cpp | 56 ++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index ca92b7cd088..873de06893e 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -211,6 +211,56 @@ namespace platf { } } + /** + * @brief Wrap drmModeGetPlaneResources call in privileged worker thread. + * + * @param fd DRM file descriptor. + * @return Pointer to drmModePlaneRes structure on success, or nullptr on failure. + */ + static drmModePlaneResPtr drmModeGetPlaneResources_privileged(int fd) { + try { + const auto [result, errno_value] = run_with_errno([fd] { + return drmModeGetPlaneResources(fd); + }); + + if (!result) { + errno = errno_value; + } + + return result; + + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); + return nullptr; + } + } + + /** + * @brief Wrap drmSetClientCap call in privileged worker thread. + * + * @param fd DRM file descriptor. + * @param capability Capability to set. + * @param capability Value to set. + * @return 1 on success, -1 on failure. + */ + static int drmSetClientCap_privileged(int fd, uint64_t capability, uint64_t value) { + try { + const auto [result, errno_value] = run_with_errno([fd, capability, value] { + return drmSetClientCap(fd, capability, value); + }); + + if (!result) { + errno = errno_value; + } + + return result; + + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); + return -1; + } + } + private: static privileged_drm_worker &instance() { static privileged_drm_worker w; @@ -715,12 +765,12 @@ namespace platf { render_fd.el = dup(fd.el); } - if (drmSetClientCap(fd.el, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { + if (platf::kms::privileged_drm_worker::drmSetClientCap_privileged(fd.el, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { BOOST_LOG(error) << "GPU driver doesn't support universal planes: "sv << path; return -1; } - if (drmSetClientCap(fd.el, DRM_CLIENT_CAP_ATOMIC, 1)) { + if (platf::kms::privileged_drm_worker::drmSetClientCap_privileged(fd.el, DRM_CLIENT_CAP_ATOMIC, 1)) { BOOST_LOG(warning) << "GPU driver doesn't support atomic mode-setting: "sv << path; #if defined(SUNSHINE_BUILD_X11) // We won't be able to capture the mouse cursor with KMS on non-atomic drivers, @@ -733,7 +783,7 @@ namespace platf { BOOST_LOG(warning) << "Cursor capture may fail without atomic mode-setting support!"sv; } - plane_res.reset(drmModeGetPlaneResources(fd.el)); + plane_res.reset(platf::kms::privileged_drm_worker::drmModeGetPlaneResources_privileged(fd.el)); if (!plane_res) { BOOST_LOG(error) << "Couldn't get drm plane resources"sv; return -1; From ef3d05d1c598c63297e183e7145fc884987eb08a Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Tue, 29 Sep 2026 16:17:32 +0100 Subject: [PATCH 5/6] fixup: make run_with_errno private --- src/platform/linux/kmsgrab.cpp | 138 ++++++++++++++++----------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index 873de06893e..5bf3c4ba400 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -86,13 +86,41 @@ namespace platf { * @brief Set up privileged worker thread exclusively for handling DRM capture resources. */ class privileged_drm_worker { - public: - static void ensure_started() { - instance(); + private: + static privileged_drm_worker &instance() { + static privileged_drm_worker w; + return w; } - static void drop_worker_privileges() { - instance().drop_privileges(); + void drop_privileges() { + instance().run([] { + platf::drop_elevated_privileges(true); + }); + } + + privileged_drm_worker(): + thread_ {[this] { + sigset_t all; + sigfillset(&all); + if (pthread_sigmask(SIG_BLOCK, &all, nullptr) != 0) { + BOOST_LOG(error) << "Failed to block signals in drm_worker"sv; + queue_.stop(); + return; + } + + platf::set_thread_name("drm_worker"); + for (;;) { + auto task = queue_.pop(); + if (!task) { + break; + } + (*task)(); + } + }} { + } + + ~privileged_drm_worker() { + queue_.stop(); } /** @@ -111,6 +139,42 @@ namespace platf { }); } + template + auto run(F &&f, const std::source_location &loc = std::source_location::current()) -> std::invoke_result_t { + using R = std::invoke_result_t; + auto task = std::make_shared>( + [f = std::forward(f)]() mutable -> R { + #if !defined(__FreeBSD__) + cap_sys_admin admin; + #endif + return f(); + } + ); + auto fut = task->get_future(); + + if (!queue_.raise([task]() mutable { + (*task)(); + })) { + throw privileged_drm_worker_stopped { + "privileged_drm_worker: task rejected in "s + loc.function_name() + " (worker stopping)"s + }; + } + + return fut.get(); + } + + safe::queue_t> queue_ {32, safe::queue_t>::overflow_policy_e::reject}; + std::jthread thread_; + + public: + static void ensure_started() { + instance(); + } + + static void drop_worker_privileges() { + instance().drop_privileges(); + } + /** * @brief Open a DRM card file descriptor in the privileged worker thread. * Note: prototype aligned to match path signature via init(const char *path). @@ -260,70 +324,6 @@ namespace platf { return -1; } } - - private: - static privileged_drm_worker &instance() { - static privileged_drm_worker w; - return w; - } - - void drop_privileges() { - instance().run([] { - platf::drop_elevated_privileges(true); - }); - } - - privileged_drm_worker(): - thread_ {[this] { - sigset_t all; - sigfillset(&all); - if (pthread_sigmask(SIG_BLOCK, &all, nullptr) != 0) { - BOOST_LOG(error) << "Failed to block signals in drm_worker"sv; - queue_.stop(); - return; - } - - platf::set_thread_name("drm_worker"); - for (;;) { - auto task = queue_.pop(); - if (!task) { - break; - } - (*task)(); - } - }} { - } - - ~privileged_drm_worker() { - queue_.stop(); - } - - template - auto run(F &&f, const std::source_location &loc = std::source_location::current()) -> std::invoke_result_t { - using R = std::invoke_result_t; - auto task = std::make_shared>( - [f = std::forward(f)]() mutable -> R { -#if !defined(__FreeBSD__) - cap_sys_admin admin; -#endif - return f(); - } - ); - auto fut = task->get_future(); - - if (!queue_.raise([task]() mutable { - (*task)(); - })) { - throw privileged_drm_worker_stopped { - "privileged_drm_worker: task rejected in "s + loc.function_name() + " (worker stopping)"s - }; - } - - return fut.get(); - } - - safe::queue_t> queue_ {32, safe::queue_t>::overflow_policy_e::reject}; - std::jthread thread_; }; } // namespace From a0d6cee8b3600b7bdca4ed6fcd45cbe8a1ac7afe Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Tue, 29 Sep 2026 16:36:44 +0100 Subject: [PATCH 6/6] fixup: wrap drmModeGetConnector, drmModeGetPlane, drmModeGetResources --- src/platform/linux/kmsgrab.cpp | 86 +++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index 5bf3c4ba400..311b08e6572 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -225,6 +225,31 @@ namespace platf { } } + /** + * @brief Wrap drmModeGetConnector call in privileged worker thread. + * + * @param fd DRM file descriptor. + * @param connectorId DRM connector id. + * @return Pointer to drmModeConnector structure on success, or nullptr on failure. + */ + static drmModeConnectorPtr drmModeGetConnector_privileged(int fd, uint32_t connectorId) { + try { + const auto [result, errno_value] = run_with_errno([fd, connectorId] { + return drmModeGetConnector(fd, connectorId); + }); + + if (!result) { + errno = errno_value; + } + + return result; + + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); + return nullptr; + } + } + /** * @brief Wrap drmModeGetFB2 call in privileged worker thread. * @@ -275,6 +300,31 @@ namespace platf { } } + /** + * @brief Wrap drmModeGetPlane call in privileged worker thread. + * + * @param fd DRM file descriptor. + * @param plane_id DRM plane id. + * @return Pointer to drmModePlane structure on success, or nullptr on failure. + */ + static drmModePlanePtr drmModeGetPlane_privileged(int fd, uint32_t plane_id) { + try { + const auto [result, errno_value] = run_with_errno([fd, plane_id] { + return drmModeGetPlane(fd, plane_id); + }); + + if (!result) { + errno = errno_value; + } + + return result; + + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); + return nullptr; + } + } + /** * @brief Wrap drmModeGetPlaneResources call in privileged worker thread. * @@ -299,6 +349,30 @@ namespace platf { } } + /** + * @brief Wrap drmModeGetResources call in privileged worker thread. + * + * @param fd DRM file descriptor. + * @return Pointer to drmModeRes structure on success, or nullptr on failure. + */ + static drmModeResPtr drmModeGetResources_privileged(int fd) { + try { + const auto [result, errno_value] = run_with_errno([fd] { + return drmModeGetResources(fd); + }); + + if (!result) { + errno = errno_value; + } + + return result; + + } catch (const privileged_drm_worker_stopped &err) { + BOOST_LOG(error) << err.what(); + return nullptr; + } + } + /** * @brief Wrap drmSetClientCap call in privileged worker thread. * @@ -656,7 +730,7 @@ namespace platf { this->plane.reset(); for (; plane_p != end; ++plane_p) { - plane_t plane = drmModeGetPlane(fd, *plane_p); + plane_t plane = platf::kms::privileged_drm_worker::drmModeGetPlane_privileged(fd, *plane_p); if (!plane) { BOOST_LOG(error) << "Couldn't get drm plane ["sv << (end - plane_p) << "]: "sv << strerror(errno); continue; @@ -838,7 +912,7 @@ namespace platf { * @return DRM card resource list. */ res_t res() { - return drmModeGetResources(fd.el); + return platf::kms::privileged_drm_worker::drmModeGetResources_privileged(fd.el); } /** @@ -928,7 +1002,7 @@ namespace platf { * @return Owning pointer to the DRM connector object. */ connector_interal_t connector(std::uint32_t id) { - return drmModeGetConnector(fd.el, id); + return platf::kms::privileged_drm_worker::drmModeGetConnector_privileged(fd.el, id); } /** @@ -1048,7 +1122,7 @@ namespace platf { * @return Plane metadata for the requested DRM plane. */ plane_t operator[](std::uint32_t index) { - return drmModeGetPlane(fd.el, plane_res->planes[index]); + return platf::kms::privileged_drm_worker::drmModeGetPlane_privileged(fd.el, plane_res->planes[index]); } /** @@ -1499,7 +1573,7 @@ namespace platf { return; } - plane_t plane = drmModeGetPlane(card.fd.el, cursor_plane_id); + plane_t plane = platf::kms::privileged_drm_worker::drmModeGetPlane_privileged(card.fd.el, cursor_plane_id); std::optional prop_crtc_x; std::optional prop_crtc_y; @@ -1690,7 +1764,7 @@ namespace platf { } } - plane_t plane = drmModeGetPlane(card.fd.el, plane_id); + plane_t plane = platf::kms::privileged_drm_worker::drmModeGetPlane_privileged(card.fd.el, plane_id); frame_timestamp = std::chrono::steady_clock::now(); auto fb = card.fb(plane.get());