Skip to content

fix(desktop): keep launching when the setuid sandbox helper cannot be configured - #86718

Open
z80dev wants to merge 1 commit into
NousResearch:mainfrom
z80dev:fix/desktop-sandbox-no-tty
Open

fix(desktop): keep launching when the setuid sandbox helper cannot be configured#86718
z80dev wants to merge 1 commit into
NousResearch:mainfrom
z80dev:fix/desktop-sandbox-no-tty

Conversation

@z80dev

@z80dev z80dev commented Aug 15, 2026

Copy link
Copy Markdown

Problem

On Linux hosts where Chromium's unprivileged user-namespace sandbox works, hermes desktop fails on every launch whose context has no TTY — the .desktop entry (app grid, rofi, etc.) and the post-update detached relaunch. _desktop_linux_sandbox_fixup tries sudo chown/chmod when chrome-sandbox isn't root:root 4755; sudo cannot prompt; cmd_gui exits 1 with nothing visible, because GUI launches have no stdout. An in-app update rebuild resets the helper to user-owned and --build-only returns before the fixup, so nothing re-provisions it — every content-changing update re-manufactures the failure.

Fix

When the fixup fails, probe whether this process can actually create a user namespace — fork + unshare(CLONE_NEWUSER), mirroring Chromium's own sandbox::Credentials::CanCreateProcessInNewUserNS (~3 ms measured) — and if so, warn and continue with Chromium's namespace sandbox instead of exiting. Absence of apparmor_restrict_unprivileged_userns is deliberately not treated as evidence: kernel.unprivileged_userns_clone=0 (Debian, Arch linux-hardened), user.max_user_namespaces=0, kernels without CONFIG_USER_NS, and container seccomp policy all break userns without that sysctl.

The exit arm now prints the exact sudo chown/chmod recovery command.

Why this is safe

Chromium consults the setuid helper only when the userns sandbox is unavailable, and when neither is available it aborts (LOG(FATAL) in setuid_sandbox_host.cc, exit 133) rather than running unsandboxed. So this change cannot cause an unsandboxed launch; the worst case of a wrong host check is a crash, never a silent sandbox drop. The setuid helper and the userns sandbox are two implementations of the same layer-1 isolation, not two layers.

Unchanged: the --no-sandbox fallback for AppArmor-restricted hosts (54ea059), the ELECTRON_DISABLE_SANDBOX=1 override (pinned by a new test), root behavior, and the missing/symlink-helper rejection.

Behavior matrix

Host Terminal launch GUI launch (main) GUI launch (this PR)
macOS / Windows no gate no gate no gate
Ubuntu 23.10+ (AppArmor restricts userns) sudo prompts once, works --no-sandbox fallback unchanged
Arch/Fedora, userns available sudo prompts once, works exit 1, silently works, sandboxed
linux-hardened / Debian unprivileged_userns_clone=0 / container sudo prompts once, works exit 1 with printed reason unchanged (probe fails closed)

One intentional interactive-path change: on userns-capable hosts, declining the sudo prompt in a terminal launch now warns and continues instead of hard-failing, so the helper may stay unprovisioned. Acceptable — the namespace sandbox provides the same layer-1 isolation.

Testing

  • Restores the fixup-failure tests pruned in 6b81590 and adds coverage for the new branch: userns continuation, userns-unavailable exit, missing-helper exit, env-override precedence, and probe unit tests (off-linux, AppArmor short-circuit, fail-closed on fork error).
  • tests/hermes_cli/test_gui_command.py: 34 passed, 5 skipped (26/5 before, with zero tests reaching the changed branch).
  • Verified end-to-end on EndeavourOS/Hyprland: the .desktop Exec path launched detached with no TTY, sudo failed exactly as in the rofi case, and the app started with sandboxed zygote children while chrome-sandbox was user-owned 0755. One host confirms the mechanism, not a host class — hence the probe.

History

… configured

A desktop-launcher context (rofi, app menu, the post-update relaunch) has no
TTY, so the sudo chown/chmod fixup for chrome-sandbox always fails there and
the launch died. Probe whether this process can actually create a user
namespace (fork + unshare(CLONE_NEWUSER), mirroring Chromium's own
CanCreateProcessInNewUserNS) and, when it can, continue with Chromium's
namespace sandbox instead of exiting: Chromium only consults the setuid
helper when the userns sandbox is unavailable, and when neither is available
it aborts rather than running unsandboxed, so this cannot cause an
unsandboxed launch. The --no-sandbox fallback for AppArmor-restricted hosts,
the ELECTRON_DISABLE_SANDBOX=1 override, and root behavior are unchanged.

Absence of apparmor_restrict_unprivileged_userns is not sufficient evidence
(kernel.unprivileged_userns_clone=0, user.max_user_namespaces=0, missing
CONFIG_USER_NS, seccomp), hence the probe. Restores the fixup-failure tests
pruned in 6b81590 and adds coverage for the new branch.
Copilot AI lite review requested due to automatic review settings August 15, 2026 05:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves Linux desktop launch reliability by allowing hermes desktop to continue launching when Electron’s setuid chrome-sandbox helper cannot be configured in non-interactive (no-TTY) contexts, as long as the Chromium user-namespace sandbox is actually available.

Changes:

  • Adds an AppArmor userns restriction probe and a new fork + unshare(CLONE_NEWUSER) capability probe to decide whether to continue after a sandbox-helper fixup failure.
  • Updates the Linux desktop launch path to (a) fall back to --no-sandbox on AppArmor-restricted hosts, (b) continue with the userns sandbox when available, or (c) exit with an explicit recovery command when neither sandbox mechanism is usable.
  • Expands tests/hermes_cli/test_gui_command.py coverage for fixup-failure and userns-probe branches.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
hermes_cli/main.py Adds userns restriction + availability probes and updates Linux desktop launch decision logic after sandbox-helper fixup failure.
tests/hermes_cli/test_gui_command.py Restores/extends regression tests for sandbox-helper gate behavior and adds unit tests for the userns probe.
Suppressed comments (1)

tests/hermes_cli/test_gui_command.py:918

  • Same issue as above: prefer the OS lane marker (@pytest.mark.linux_only) over skipif(sys.platform != "linux") so this test is correctly selected/executed by the OS-specific CI lanes.
@pytest.mark.skipif(sys.platform != "linux", reason="needs os.fork")

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

# userns-sandbox continuation added for non-TTY launch contexts.


@pytest.mark.skipif(sys.platform != "linux", reason="Linux sandbox gate")
Comment thread hermes_cli/main.py
Comment on lines +7154 to +7172
import ctypes

try:
# Load libc before forking so the child only makes one syscall.
libc = ctypes.CDLL("libc.so.6", use_errno=True)
pid = os.fork()
except (OSError, AttributeError):
return False
if pid == 0:
try:
rc = libc.unshare(_CLONE_NEWUSER)
except BaseException:
os._exit(1)
os._exit(0 if rc == 0 else 1)
try:
_, status = os.waitpid(pid, 0)
except OSError:
return False
return os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0
@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Aug 15, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

fix(desktop): keep launching when the setuid sandbox helper cannot be configured

  1. Silent exit when chrome-sandbox is missing entirely: in cmd_gui the final else branch prints the diagnostic only when helper_present is True. If the helper file does not exist AND userns is available AND _desktop_linux_needs_no_sandbox() is False, the launcher exits 1 with no message — the user sees a bare failure. Add a message for the missing-helper case, consistent with the diagnostics this PR adds for the other branches.
  2. _linux_userns_sandbox_available() forks without a bounded wait: os.waitpid(pid, 0) blocks indefinitely if the child hangs (e.g. a kernel/container where unshare(CLONE_NEWUSER) never returns under seccomp), which would hang hermes gui instead of erroring. Consider a WNOHANG loop with a timeout. (Fail-closed on EINTR is already handled via the OSError branch.)
  3. Minor: the fork probe is safe at cmd_gui time (single-threaded launcher), but if it is ever invoked from a multi-threaded path (gateway/desktop spawn), fork inherits only the calling thread — the child only calls unshare + _exit, so the risk is minimal. A module-level memoization of the probe result would avoid repeated forks and cost nothing.

@vortexpjeff

Copy link
Copy Markdown

I reproduced this exact no-TTY failure on Ubuntu 24.04 under WSL2 and verified the proposed userns continuation end to end. I opened a focused follow-up into your branch for the outstanding review findings: z80dev#1

The follow-up adds the repo Linux CI markers, CDLL(None) portability, EINTR retry, a bounded kill+reap path, and the missing-helper diagnostic. Regression proof was red before the production edit; targeted results are 37 passed / 5 skipped plus 6 uninstall tests.

Live proof on WSL2 with user-owned 0755 chrome-sandbox: sudo fixup failed noninteractively, Desktop launched without --no-sandbox, and renderer zygotes showed distinct user/PID namespaces with NoNewPrivs=1 and Seccomp=2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants