From be6c875277c1b66cd0cd90c58c5c71c7173a1aba Mon Sep 17 00:00:00 2001 From: The412Banner Date: Sun, 27 Sep 2026 16:52:51 -0400 Subject: [PATCH] Session stop: kill guest processes proot lost, and cap the session log Twice today a session's Xwayland aborted from one thread ("The futex facility returned an unexpected error code"), proot lost the tracee, and the process lived on reparented to init: proot's seccomp filter, with no tracer left, answers ENOSYS, so Xwayland's main loop logged "WaitForSomething(): poll: Function not implemented" until something killed it - 9 GB after Dolphin, 13 GB after PPSSPP. --kill-on-exit and the tree sweep only reach what proot still traces, and the straggler sweep ran only at the next session's start. Now a session stop also kills every process of ours whose executable lives in the Linux runtime, and bannerlator-session keeps only the first BL_LOG_CAP bytes (256 MB) of session.log, draining the rest so a writer that filled it is not killed by SIGPIPE. --- .../launcher/session/SessionService.kt | 25 +++++++++++++++++++ .../overlay/usr/local/bin/bannerlator-session | 5 +++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt index 730c43787..419b4258f 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt @@ -741,6 +741,30 @@ class SessionService : Service() { * behind, still holding the Wayland socket and the audio server the next session needs. They * are our uid, so they are ours to kill. */ + /** + * Every process of ours still running a program out of the Linux runtime once proot is gone. + * proot's --kill-on-exit and the tree sweep only reach what proot still traces; a tracee it + * lost - an Xwayland that aborted from one thread and then looped on a syscall proot's own + * seccomp filter, with no tracer left, answers ENOSYS - survives both, reparented to init, + * and wrote 9 GB of one line into the session log before anything killed it. + */ + private fun killGuestLeftovers() { + val me = android.os.Process.myPid() + val rootfs = try { File(filesDir, "linuxfs").canonicalPath } catch (e: Exception) { return } + "/" + val procs = File("/proc").listFiles { f -> f.name.all { it.isDigit() } } ?: return + var killed = 0 + for (proc in procs) { + val pid = proc.name.toIntOrNull() ?: continue + if (pid == me) continue + // Another uid's exe is not ours to read; a process already gone has none. + val exe = try { File(proc, "exe").canonicalPath } catch (e: Exception) { continue } + if (!exe.startsWith(rootfs)) continue + android.os.Process.killProcess(pid) + killed++ + } + if (killed > 0) Log.w(TAG, "killed $killed guest process(es) proot no longer tracked") + } + private fun killStragglers() { val me = android.os.Process.myPid() val procs = File("/proc").listFiles { f -> f.name.all { it.isDigit() } } ?: return @@ -917,6 +941,7 @@ class SessionService : Service() { Thread({ auxiliary.forEach { (pid, started) -> teardown(pid, started) } if (prootPid > 1) teardown(prootPid) + killGuestLeftovers() finishSessionStop(status) }, "session-teardown").start() } else { diff --git a/tools/linuxfs/overlay/usr/local/bin/bannerlator-session b/tools/linuxfs/overlay/usr/local/bin/bannerlator-session index 65e6e414e..4e8402291 100755 --- a/tools/linuxfs/overlay/usr/local/bin/bannerlator-session +++ b/tools/linuxfs/overlay/usr/local/bin/bannerlator-session @@ -19,7 +19,10 @@ if [ -n "${BL_LOG:-}" ] && [ -z "${BL_INSIDE:-}" ]; then if [ "$(stat -c %s "$BL_LOG" 2>/dev/null || echo 0)" -gt 8388608 ]; then mv -f "$BL_LOG" "$BL_LOG.1" fi - exec >>"$BL_LOG" 2>&1 + # A guest looping on an error writes gigabytes in minutes (an Xwayland proot had lost track of + # managed 9 GB of one line in 19 min): the log keeps its first BL_LOG_CAP bytes and the rest is + # drained, not refused - a writer that filled it must not die of SIGPIPE. + exec > >({ head -c "${BL_LOG_CAP:-268435456}" >>"$BL_LOG"; cat >/dev/null; }) 2>&1 echo "== bannerlator-session $(date) mode=$mode $*" echo "== uid=$(id -u) size=${BL_WIDTH:-?}x${BL_HEIGHT:-?} fps=${BL_FPS:-0} icd=${VK_ICD_FILENAMES:-none}" echo "== gamescope: $(gamescope --version 2>&1 | head -1) at $(command -v gamescope)"