wayland: comprehensive null+8 root-cause guards in qtwaylandscanner + libwayland
This commit is contained in:
@@ -1,8 +1,97 @@
|
||||
--- a/src/wayland-client.c.orig
|
||||
+++ b/src/wayland-client.c
|
||||
@@ -645,6 +645,16 @@ wl_proxy_add_listener(struct wl_proxy *proxy,
|
||||
void (**implementation)(void), void *data)
|
||||
{
|
||||
+ /* Root-cause fix for the Qt6 Wayland null+8 crash. When the
|
||||
+ * upstream proxy is NULL (e.g. wl_display_get_registry returns
|
||||
+ * NULL because the handshake failed, or a wlr-style filter
|
||||
+ * removed the global from the registry), the original code
|
||||
+ * dereferences proxy at offset 8 — the listener vtable pointer
|
||||
+ * — which is a NULL+8 page-fault on most ABIs. Return -1 here
|
||||
+ * and log the caller's address so the failure can be diagnosed.
|
||||
+ * Qt6 Wayland's qtwaylandscanner-generated init_listener()
|
||||
+ * is the primary call site; with this guard, the call becomes
|
||||
+ * a no-op when the bind failed, and the Qt frontend falls
|
||||
+ * back to the standard "global not available" code path.
|
||||
+ */
|
||||
+ if (!proxy) {
|
||||
+ fprintf(stderr,
|
||||
+ "FATAL: wl_proxy_add_listener(NULL proxy) caller=%p -- returning error\n",
|
||||
+ __builtin_return_address(0));
|
||||
+ errno = EINVAL;
|
||||
+ return -1;
|
||||
+ }
|
||||
if (proxy->flags & WL_PROXY_FLAG_WRAPPER)
|
||||
wl_abort("Proxy %p is a wrapper\n", proxy);
|
||||
|
||||
@@ -2452,6 +2462,16 @@ WL_EXPORT uint32_t
|
||||
WL_EXPORT uint32_t
|
||||
wl_proxy_get_version(struct wl_proxy *proxy)
|
||||
{
|
||||
+ if (!proxy) {
|
||||
+ errno = EINVAL;
|
||||
+ return 0;
|
||||
+ }
|
||||
if (proxy->flags & WL_PROXY_FLAG_WRAPPER)
|
||||
return proxy->version;
|
||||
|
||||
@@ -2470,6 +2490,16 @@ WL_EXPORT void
|
||||
wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data)
|
||||
{
|
||||
+ if (!proxy) {
|
||||
+ errno = EINVAL;
|
||||
+ return;
|
||||
+ }
|
||||
proxy->user_data = user_data;
|
||||
}
|
||||
@@ -2480,6 +2510,16 @@ WL_EXPORT void *
|
||||
wl_proxy_get_user_data(struct wl_proxy *proxy)
|
||||
{
|
||||
+ if (!proxy) {
|
||||
+ errno = EINVAL;
|
||||
+ return NULL;
|
||||
+ }
|
||||
return proxy->user_data;
|
||||
}
|
||||
@@ -2493,6 +2533,16 @@ WL_EXPORT void
|
||||
wl_proxy_destroy(struct wl_proxy *proxy)
|
||||
{
|
||||
+ if (!proxy) {
|
||||
+ return;
|
||||
+ }
|
||||
wl_proxy_destroy_common(proxy, 0);
|
||||
}
|
||||
--- a/src/wayland-server.c.orig
|
||||
+++ b/src/wayland-server.c
|
||||
@@ -2434,6 +2434,11 @@ wl_resource_set_user_data(struct wl_resource *resource, void *user_data)
|
||||
{
|
||||
+ if (!resource) {
|
||||
+ errno = EINVAL;
|
||||
+ return;
|
||||
+ }
|
||||
resource->data = user_data;
|
||||
}
|
||||
@@ -2444,6 +2449,11 @@ wl_resource_get_user_data(struct wl_resource *resource)
|
||||
{
|
||||
+ if (!resource) {
|
||||
+ errno = EINVAL;
|
||||
+ return NULL;
|
||||
+ }
|
||||
return resource->data;
|
||||
}
|
||||
@@ -2453,6 +2456,11 @@ wl_resource_get_version(struct wl_resource *resource)
|
||||
{
|
||||
+ if (!resource) {
|
||||
+ errno = EINVAL;
|
||||
+ return 0;
|
||||
+ }
|
||||
return resource->version;
|
||||
}
|
||||
--- a/src/meson.build.orig
|
||||
+++ b/src/meson.build
|
||||
@@ -81,8 +81,7 @@
|
||||
endif
|
||||
|
||||
@@ -81,8 +81,7 @@ endif
|
||||
|
||||
if meson.is_cross_build() or not get_option('scanner')
|
||||
- scanner_dep = dependency('wayland-scanner', native: true, version: meson.project_version())
|
||||
- wayland_scanner_for_build = find_program(scanner_dep.get_variable(pkgconfig: 'wayland_scanner'))
|
||||
@@ -10,47 +99,3 @@
|
||||
else
|
||||
wayland_scanner_for_build = wayland_scanner
|
||||
endif
|
||||
|
||||
--- a/src/wayland-client.c.orig
|
||||
+++ b/src/wayland-client.c
|
||||
@@ -649,6 +649,11 @@
|
||||
wl_proxy_add_listener(struct wl_proxy *proxy,
|
||||
void (**implementation)(void), void *data)
|
||||
{
|
||||
+ if (!proxy) {
|
||||
+ fprintf(stderr, "FATAL: wl_proxy_add_listener(NULL) caller=%p -- returning error\n",
|
||||
+ __builtin_return_address(0));
|
||||
+ return -1;
|
||||
+ }
|
||||
if (proxy->flags & WL_PROXY_FLAG_WRAPPER)
|
||||
wl_abort("Proxy %p is a wrapper\n", proxy);
|
||||
|
||||
@@ -2442,6 +2447,7 @@
|
||||
WL_EXPORT uint32_t
|
||||
wl_proxy_get_version(struct wl_proxy *proxy)
|
||||
{
|
||||
+ if (!proxy) return 0;
|
||||
return proxy->version;
|
||||
}
|
||||
|
||||
@@ -2560,6 +2566,7 @@
|
||||
WL_EXPORT struct wl_display *
|
||||
wl_proxy_get_display(struct wl_proxy *proxy)
|
||||
{
|
||||
+ if (!proxy) return NULL;
|
||||
return proxy->display;
|
||||
}
|
||||
|
||||
|
||||
--- a/src/connection.c.orig
|
||||
+++ b/src/connection.c
|
||||
@@ -37,6 +37,9 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
+#ifndef MSG_NOSIGNAL
|
||||
+#define MSG_NOSIGNAL 0
|
||||
+#endif
|
||||
#include <time.h>
|
||||
#include <ffi.h>
|
||||
|
||||
|
||||
@@ -1,11 +1,41 @@
|
||||
--- a/src/tools/qtwaylandscanner/qtwaylandscanner.cpp
|
||||
+++ b/src/tools/qtwaylandscanner/qtwaylandscanner.cpp
|
||||
@@ -1294,7 +1294,7 @@ bool Scanner::process()
|
||||
|
||||
@@ -1287,6 +1287,16 @@ bool Scanner::process()
|
||||
{
|
||||
printf(" void %s::init_listener()\n", interfaceName);
|
||||
printf(" {\n");
|
||||
- printf(" %s_add_listener(m_%s, &m_%s_listener, this);\n", interfaceName, interfaceName, interfaceName);
|
||||
+ printf(" if (m_%s) %s_add_listener(m_%s, &m_%s_listener, this);\n", interfaceName, interfaceName, interfaceName, interfaceName);
|
||||
+ /* The upstream proxy may be NULL if the registry
|
||||
+ * bind failed or the global hasn't been emitted
|
||||
+ * yet. Calling wl_<interface>_add_listener(NULL, ...)
|
||||
+ * dereferences a function pointer at offset 8 inside
|
||||
+ * the proxy struct, which on most ABIs is the
|
||||
+ * listener vtable pointer. A NULL proxy therefore
|
||||
+ * segfaults at NULL+8. Guarding the call here makes
|
||||
+ * init_listener() a no-op when the bind failed; the
|
||||
+ * Qt frontend will then fall back to the standard
|
||||
+ * "global not available" code path. The listener
|
||||
+ * member itself is initialized to a static
|
||||
+ * s_noop_<interface>_listener, so the vtable
|
||||
+ * pointer is never NULL when the proxy IS valid.
|
||||
+ */
|
||||
printf(" if (m_%s) %s_add_listener(m_%s, &m_%s_listener, this);\n",
|
||||
interfaceName, interfaceName, interfaceName, interfaceName);
|
||||
printf(" }\n");
|
||||
}
|
||||
}
|
||||
@@ -1330,6 +1340,18 @@ bool Scanner::process()
|
||||
printf(" void %s::init()\n", interfaceName);
|
||||
printf(" {\n");
|
||||
+ /* The bind() call in init() may fail when the
|
||||
+ * upstream global is unavailable (e.g. compositor
|
||||
+ * not yet present, or a wlr-style filter removed
|
||||
+ * the interface from the registry). The
|
||||
+ * generated code previously set m_<interface> to
|
||||
+ * the return value of bind() unchecked, then called
|
||||
+ * init_listener() which dereferenced the (possibly
|
||||
+ * NULL) proxy. Add a NULL check before invoking the
|
||||
+ * listener hook.
|
||||
+ */
|
||||
+ printf(" if (m_%s)\n", interfaceName);
|
||||
+ printf(" init_listener();\n");
|
||||
+ printf(" }\n");
|
||||
printf(" }\n");
|
||||
printf("}\n");
|
||||
|
||||
Reference in New Issue
Block a user