From ab5130dff2c645f3208ea405c0a49200b87da171 Mon Sep 17 00:00:00 2001 From: LFRon Date: Mon, 24 Aug 2026 12:10:10 +0800 Subject: [PATCH] fix: grant Xwayland cap_ipc_owner for cross-user MIT-SHM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XWayland runs as the display-manager user (e.g. "dde") while desktop X11 clients (especially Electron/Chromium apps) may be launched by the real login user. MIT-SHM (XShmPutImage) fails with BadAccess when the SysV shared memory segment UID differs from the X server UID — the kernel's ipcperms() denies shmat() for a 0600 segment owned by another user. Grant cap_ipc_owner to the Xwayland binary so it can attach segments created by any user on the machine. The X server still enforces its own per-client access check (Xext/shm.c:shm_access, which verifies the client owns the segment). Three layers ensure the capability is always present: - Install-time (debian/ddm.postinst): applies cap_ipc_owner=ep to the Xwayland binary via setcap, so the first boot after installation already has the fix. - Runtime (DaemonApp.cpp::applyXwaylandIpcCapability): re-applies the capability at every ddm startup, self-healing after Xwayland package upgrades that may clear the file capability. - Cleanup (debian/ddm.postrm): removes the capability on remove/purge via setcap -r. Depends on libcap2-bin (provides setcap). Pair this with the treeland.service unit changes that disable NoNewPrivileges and PrivateIPC (which would otherwise block the capability or hide the segment via IPC namespace isolation). --- debian/control | 1 + debian/ddm.postinst | 20 ++++++++++++++++ debian/ddm.postrm | 13 +++++++++++ src/daemon/DaemonApp.cpp | 50 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) diff --git a/debian/control b/debian/control index 118d855..00ddfc3 100644 --- a/debian/control +++ b/debian/control @@ -26,6 +26,7 @@ Depends: ${shlibs:Depends}, dde-seatd, treeland, libpam-systemd, + libcap2-bin, Description: a modern display manager for Wayland sessions aiming to be fast, simple and beautiful. Package: libddm diff --git a/debian/ddm.postinst b/debian/ddm.postinst index d66944d..91508f9 100644 --- a/debian/ddm.postinst +++ b/debian/ddm.postinst @@ -64,6 +64,26 @@ fi touch /var/log/ddm.log chown dde:dde /var/log/ddm.log +# Xwayland runs as the display-manager user (e.g. "dde") while X11 clients +# (e.g. Electron apps) may be launched by the real login user. MIT-SHM +# (XShmPutImage) fails with BadAccess when the SysV shared memory segment UID +# differs from the X server UID, leaving such windows blank. Grant cap_ipc_owner +# to the Xwayland binary so shmat() can attach segments of any user; the X +# server still enforces its own per-client access check (Xext/shm.c:shm_access). +# The daemon re-applies this at startup too, in case an Xwayland package +# upgrade clears the file capability. +if command -v setcap >/dev/null 2>&1; then + XWAYLAND_BIN="$(command -v Xwayland 2>/dev/null || true)" + if [ -z "$XWAYLAND_BIN" ]; then + XWAYLAND_BIN=/usr/bin/Xwayland + fi + if [ -x "$XWAYLAND_BIN" ]; then + if ! setcap cap_ipc_owner=ep "$XWAYLAND_BIN" 2>/dev/null; then + echo "ddm: warning: failed to set cap_ipc_owner on $XWAYLAND_BIN" >&2 + fi + fi +fi + #DEBHELPER# exit 0 diff --git a/debian/ddm.postrm b/debian/ddm.postrm index 03610bc..5aa5953 100644 --- a/debian/ddm.postrm +++ b/debian/ddm.postrm @@ -4,9 +4,21 @@ set -e DEFAULT_DISPLAY_MANAGER_FILE=/etc/X11/default-display-manager DEFAULT_SERVICE=/etc/systemd/system/display-manager.service +# Remove the cap_ipc_owner file capability granted at install time (see +# ddm.postinst). Only runs on remove/purge; upgrades keep the cap. +cleanup_xwayland_cap() { + if command -v setcap >/dev/null 2>&1; then + for BIN in "$(command -v Xwayland 2>/dev/null || true)" /usr/bin/Xwayland; do + [ -z "$BIN" ] && continue + [ -x "$BIN" ] && setcap -r "$BIN" 2>/dev/null || true + done + fi +} + case "$1" in purge) update-rc.d ddm remove > /dev/null + cleanup_xwayland_cap if [ -d /var/cache/ddm ]; then rm -r /var/cache/ddm; fi if [ -d /var/lib/ddm ]; then rm -r /var/lib/ddm; fi @@ -35,6 +47,7 @@ case "$1" in ;; remove) + cleanup_xwayland_cap # Update the display-manager.service symlink to point to the newly # chosen display manager (written by prerm into DEFAULT_DISPLAY_MANAGER_FILE) if [ -e "$DEFAULT_DISPLAY_MANAGER_FILE" ] && [ -d /etc/systemd/system/ ]; then diff --git a/src/daemon/DaemonApp.cpp b/src/daemon/DaemonApp.cpp index f80fdb8..2375d09 100644 --- a/src/daemon/DaemonApp.cpp +++ b/src/daemon/DaemonApp.cpp @@ -34,7 +34,11 @@ #include #include #include +#include +#include #include +#include +#include #include #include @@ -42,6 +46,41 @@ namespace DDM { DaemonApp *DaemonApp::self = nullptr; + static void applyXwaylandIpcCapability() + { + const QStringList candidates = [&]() -> QStringList { + QStringList list; + const QString found = QStandardPaths::findExecutable(QStringLiteral("Xwayland")); + if (!found.isEmpty()) + list << found; + list << QStringLiteral("/usr/bin/Xwayland"); + return list; + }(); + + const QString setcapBin = []() -> QString { + QString bin = QStandardPaths::findExecutable(QStringLiteral("setcap")); + if (bin.isEmpty() && QFile::exists(QStringLiteral("/usr/sbin/setcap"))) + bin = QStringLiteral("/usr/sbin/setcap"); + return bin; + }(); + if (setcapBin.isEmpty()) { + qWarning() << "setcap not found, cannot grant cap_ipc_owner to Xwayland"; + return; + } + + for (const QString &path : std::as_const(candidates)) { + if (!QFile::exists(path) || !QFileInfo(path).isExecutable()) + continue; + const int ret = QProcess::execute(setcapBin, + { QStringLiteral("cap_ipc_owner=ep"), path }); + if (ret == 0) + qInfo() << "Granted cap_ipc_owner to" << path; + else + qWarning() << "setcap on" << path << "failed (exit" << ret << ")"; + break; + } + } + DaemonApp::DaemonApp(int &argc, char **argv) : QCoreApplication(argc, argv) { // point instance to this self = this; @@ -95,6 +134,17 @@ namespace DDM { // log message qDebug() << "Starting..."; + // Xwayland runs as the display manager's user (e.g. "dde") while + // desktop X11 clients may be launched by the real login user. + // MIT-SHM (XShmPutImage) fails with BadAccess when the SysV shared + // memory segment UID differs from the X server UID ── shmat() is + // denied by the kernel. Grant cap_ipc_owner to the Xwayland binary + // so it can attach segments created by any user on the machine. + // The X server still enforces its own per-client access check + // (Xext/shm.c:shm_access, which verifies the client owns the seg). + // See also: debian/ddm.postinst (install-time) and postrm (cleanup). + applyXwaylandIpcCapability(); + m_seatManager->initialize(); }