wayland: hook wl_proxy_get_listener avoiding QTBUG-145022 crash

Co-authored-by: Lemmy <studio@quadbyte.net>
This commit is contained in:
outfoxxed 2026-03-16 21:19:20 -07:00
parent 6705e2da77
commit 365bf16b1e
No known key found for this signature in database
GPG key ID: 4C88A185FB89301E
5 changed files with 54 additions and 1 deletions

View file

@ -73,6 +73,7 @@ endfunction()
# -----
qt_add_library(quickshell-wayland STATIC
wl_proxy_safe_deref.cpp
platformmenu.cpp
popupanchor.cpp
xdgshell.cpp
@ -80,6 +81,13 @@ qt_add_library(quickshell-wayland STATIC
output_tracking.cpp
)
# required for wl_proxy_safe_deref
target_link_libraries(quickshell-wayland PRIVATE ${CMAKE_DL_LIBS})
target_link_options(quickshell PRIVATE
"LINKER:--export-dynamic-symbol=wl_proxy_get_listener"
"LINKER:--require-defined=wl_proxy_get_listener"
)
# required to make sure the constructor is linked
add_library(quickshell-wayland-init OBJECT init.cpp)

View file

@ -10,6 +10,7 @@
#include "wlr_layershell/wlr_layershell.hpp"
#endif
void installWlProxySafeDeref(); // NOLINT(misc-use-internal-linkage)
void installPlatformMenuHook(); // NOLINT(misc-use-internal-linkage)
void installPopupPositioner(); // NOLINT(misc-use-internal-linkage)
@ -33,6 +34,7 @@ class WaylandPlugin: public QsEnginePlugin {
}
void init() override {
installWlProxySafeDeref();
installPlatformMenuHook();
installPopupPositioner();
}

View file

@ -8,9 +8,9 @@
#include <qobjectdefs.h>
#include <qproperty.h>
#include "../../windowmanager/screenprojection.hpp"
#include "../../windowmanager/windowmanager.hpp"
#include "../../windowmanager/windowset.hpp"
#include "../../windowmanager/screenprojection.hpp"
#include "ext_workspace.hpp"
namespace qs::wm::wayland {

View file

@ -0,0 +1,42 @@
#include <dlfcn.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <wayland-client-core.h>
#include <wayland-util.h>
#include "../core/logcat.hpp"
namespace {
QS_LOGGING_CATEGORY(logDeref, "quickshell.wayland.safederef", QtWarningMsg);
using wl_proxy_get_listener_t = const void* (*) (wl_proxy*);
wl_proxy_get_listener_t original_wl_proxy_get_listener = nullptr; // NOLINT
} // namespace
extern "C" {
WL_EXPORT const void* wl_proxy_get_listener(struct wl_proxy* proxy) {
// Avoid null derefs of protocol objects in qtbase.
// https://qt-project.atlassian.net/browse/QTBUG-145022
if (!proxy) [[unlikely]] {
qCCritical(logDeref) << "wl_proxy_get_listener called with a null proxy!";
return nullptr;
}
return original_wl_proxy_get_listener(proxy);
}
}
// NOLINTBEGIN (concurrency-mt-unsafe)
void installWlProxySafeDeref() {
dlerror(); // clear old errors
original_wl_proxy_get_listener =
reinterpret_cast<wl_proxy_get_listener_t>(dlsym(RTLD_NEXT, "wl_proxy_get_listener"));
if (auto* error = dlerror()) {
qCCritical(logDeref) << "Failed to find wl_proxy_get_listener for hooking:" << error;
} else {
qCInfo(logDeref) << "Installed wl_proxy_get_listener hook.";
}
}
// NOLINTEND