diff --git a/.github/workflows/drm-capture.yml b/.github/workflows/drm-capture.yml index d9b610d57..b1a674c2f 100644 --- a/.github/workflows/drm-capture.yml +++ b/.github/workflows/drm-capture.yml @@ -397,7 +397,17 @@ jobs: # explicit rescue so a no-match grep reaches the `test -n` reporter instead of dying as a # bare pipeline failure. set -euo pipefail - deb="$(ls rustdesk-unattended-wayland-*-x86_64.deb)" + # Same nullglob array + count assertion as the assert step above, for the same two + # reasons: under set -e a zero-match `ls` aborts before anything can report WHY, and + # several matches make `deb` multi-line so dpkg-deb fails with an unrelated error. (This + # was the sibling left behind when that one was fixed.) + shopt -s nullglob + debs=(rustdesk-unattended-wayland-*-x86_64.deb) + if [ "${#debs[@]}" -ne 1 ]; then + echo "::error::expected exactly one renamed deb to measure, found ${#debs[@]}: ${debs[*]-none}" + exit 1 + fi + deb="${debs[0]}" rm -rf /tmp/debfloor && dpkg-deb -R "$deb" /tmp/debfloor floor="$(objdump -T /tmp/debfloor/usr/share/rustdesk/lib/librustdesk.so \ | grep -oE 'GLIBC_2\.[0-9]+' | sort -uV | tail -1 || true)" diff --git a/libs/scrap/src/common/drm_reader.rs b/libs/scrap/src/common/drm_reader.rs index 65863976d..3a54ecc44 100644 --- a/libs/scrap/src/common/drm_reader.rs +++ b/libs/scrap/src/common/drm_reader.rs @@ -440,11 +440,20 @@ impl DrmReader { } // dup the fd into an OwnedFd BEFORE releasing the frame: after release // the library may recycle its handle, but our dup (an independent fd on - // the same open dma-buf) keeps the buffer alive for the peer. dup(2) - // shares the same open file description, so it preserves the O_RDONLY - // access mode of libdrmtap's exported scanout fd (DRM_RDWR dropped) -- - // the peer's fd stays read-only and cannot write the live scanout. - let dup_fd = hbb_common::libc::dup(raw_fd); + // the same open dma-buf) keeps the buffer alive for the peer. It shares + // the same open file description, so it preserves the O_RDONLY access + // mode of libdrmtap's exported scanout fd (DRM_RDWR dropped) -- the + // peer's fd stays read-only and cannot write the live scanout. + // + // F_DUPFD_CLOEXEC, not dup(): `dup` never copies the close-on-exec flag, so this fd + // would be inherited by every child this process forks. This runs in the ROOT service, + // which does fork synchronously elsewhere (the `loginctl` active-uid lookup), and what + // this fd names is the LIVE SCANOUT -- the screen contents. Leaking that into an + // unrelated child is a disclosure even if no child ever reads it. Same fix, same + // reason, as `dup_to_drm_conn` in ipc/drm.rs, which closed this on the socket fd; this + // was its sibling and the one that carries the pixels. SCM_RIGHTS delivery is + // unaffected: the receiver gets its own descriptor with its own flags. + let dup_fd = hbb_common::libc::fcntl(raw_fd, hbb_common::libc::F_DUPFD_CLOEXEC, 0); if dup_fd < 0 { let e = io::Error::last_os_error(); (self.lib.frame_release)(self.ctx, &mut frame); diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 01a77a8c8..b6728ba63 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -2062,6 +2062,23 @@ mod desktop { self.display = "".to_owned(); self.xauth = "".to_owned(); self.is_rustdesk_subprocess = false; + // Resolve HOME even on this path. Upstream returned without it because nothing then + // consumed a login-Wayland Desktop, but the drm build starts a `--server` as the + // greeter uid here, and a child with no HOME has nowhere to put its config. The + // compositor variables (WAYLAND_DISPLAY, DBUS, DISPLAY, XAUTHORITY) are left blank + // on purpose and are NOT an oversight: the drm capture path talks to the root + // service over `_drm` and to a render node, never to the compositor or the portal, + // which is the entire reason it works at a login screen. `try_start_server_` skips + // empty entries, so the greeter child simply does not get them. + // + // NOT REPRODUCIBLE ON OUR HARDWARE, so it is a reasoned fix, not a measured one: + // `is_login_wayland` needs `is_gdm_user(username)`, and a current GDM runs its + // greeter as `gdm-greeter`, which that helper does not match -- measured on the + // test host, where the greeter server therefore takes the branch below and gets a + // fully populated environment. This is for the display managers whose greeter user + // does match. + #[cfg(feature = "drm")] + self.get_home(); return; }