Skip to content

Rework xnamespaces - #4074

Draft
Jipok wants to merge 5 commits into
X11Libre:masterfrom
Jipok:jbx-poc
Draft

Jipok wants to merge 5 commits into
X11Libre:masterfrom
Jipok:jbx-poc

Conversation

@Jipok

@Jipok Jipok commented Nov 4, 2026 •

Copy link
Copy Markdown

Summary

This series completes Xext/namespace so that real-world desktop applications (GTK3/GTK4, Qt5/Qt6, SDL2/SDL3, Firefox, Chromium/Electron, system tray apps) run out of the box inside isolated X11 namespaces under reparenting/tiling window managers (such as i3, openbox, xfwm4, kwin) while closing remaining cross-namespace snooping, input-grab, and window-tree information leaks.

It also adds optional cgroup v2 auto-provisioning (cgroup <prefix>, default <root|anon>) and Option "NamespaceConfig" in xorg.conf, three live-switchable clipboard modes (off, clipboard-auto, clipboard), host-to-namespace XDND and selection helper routing (_XNS_SELECTION_HELPER_<ns>), and a 31-test pyxtest suite (test/pyxtest/test_namespace.py).


Motivation & Problems Solved

  1. Reparenting / Tiling Window Manager Geometry & Tree Projection (dix/, Xi/):

    • Reparenting and tiling WMs (e.g. i3) wrap client top-level windows inside multi-level container hierarchies (root -> workspace -> split -> frame -> client).
    • Previously, ProcQueryTree did not filter children through XaceHookResourceAccess and exposed the parent container XID, while ProcGetGeometry returned container-relative coordinates (pWin->origin.x/y == (0, 0)) that conflicted with root-relative synthetic ConfigureNotify events and TranslateCoordinates. In Firefox (libgdk-3 + libxul), this mismatch caused gdk_window_x11_get_frame_extents to compute a bogus frame margin equal to the tile's screen offset and enter an infinite ConfigureRequest ↔ ConfigureNotify resize loop.
    • Solution: Without granting access to foreign WM frame windows, project reparented top-level windows transparently as direct children of root:
      • ProcGetGeometry: when pWin->parent is inaccessible under XaceHookResourceAccess(..., DixGetAttrAccess), return root-relative screen coordinates (pWin->drawable.x/y - wBorderWidth(pWin)).
      • ProcQueryTree: filter children with XaceHookResourceAccess; project reply.parent = root when pWin->parent is inaccessible; and when querying root, recursively collect accessible descendants reparented inside inaccessible WM containers.
      • ProcTranslateCoords, ProcQueryPointer, ProcXIQueryPointer: descend through inaccessible ancestor containers to report the caller's own window as child while preserving occlusion by foreign windows.
  2. Background Input State & Active Grab Isolation (dix/, Xi/, xkb/, Xext/namespace):

    • ProcQueryPointer, ProcXIQueryPointer, ProcXQueryDeviceState, ProcXkbGetState, ProcXkbGetIndicatorState, ProcXkbGetNamedIndicator, and XkbSendStateNotify previously leaked live modifier, button, key, or LED state without checking dixCallDeviceAccessCallback(..., DixReadAccess).
    • GrabDevice returned a protocol error (BadAccess) when denied by XACE, which libX11's XGrabPointer / XGrabKeyboard misinterprets as 0 (GrabSuccess).
    • An active GrabKeyboard acquired while focused persisted after the host WM moved focus to another window.
    • Solution:
      • Gate live device state (DixReadAccess) in hookDevice on XnsNamespaceHasFocus(subj->ns) (and pointer coordinates in QueryPointer/XIQueryPointer on allowMouseMotion), and always deny global keymap polling (QueryKeymap, XQueryDeviceState) so confined clients receive zeroed state bitmaps instead of fatal X errors.
      • Return AlreadyGrabbed in GrabDevice when dixCallDeviceAccessCallback returns BadAccess.
      • In SetInputFocus, deactivate any active device grab whose owner no longer passes dixCallDeviceAccessCallback(..., DixGrabAccess) after focus moves.
      • Grant a 1000ms input-focus ticket (XnsGrantInputFocusTicket) when a genuine (non-SendEvent) button/key press is delivered to one of a namespace's own windows so clicking an unfocused tray icon can open and grab a popup menu.
  3. Access-Mode Hook Model & Desktop Integration (Xext/namespace):

    • Replace hardcoded per-extension opcode switch tables in hook-resource.c, hook-device.c, hook-server.c, and hook-ext-dispatch.c with Dix*Access bitmask checks.
    • Make GrabServer / UngrabServer a safe no-op (Success) for non-superPower clients so toolkits (SDL3, GDK) cannot freeze the display or crash on BadAccess.
    • Gate ProcKillClient(AllTemporary) with dixCallServerAccessCallback(..., DixManageAccess).
    • Populate self-referential _NET_SUPPORTING_WM_CHECK and _NET_WM_NAME on virtual roots; allow read-only access and PropertyNotify / RandR events for non-sensitive global root properties while keeping _NET_CLIENT_LIST*, _NET_ACTIVE_WINDOW, _NET_SUPPORTING_WM_CHECK, and CUT_BUFFER* isolated.
    • Allow confined clients to discover _NET_SYSTEM_TRAY_S*, _NET_WM_CM_S*, and WM_S* selections and send _NET_SYSTEM_TRAY_OPCODE dock requests only for their own icon windows.
    • Track host-initiated XDND drag sessions (XdndEnter / XdndPosition / XdndDrop from superPower clients) so target namespaces with clipboard access can read XdndTypeList / XdndActionList and XdndSelection and reply with XdndStatus / XdndFinished, and optionally route ConvertSelection requests for targets listed in _XNS_SELECTION_TARGETS (XA_ATOM[]) on a host _XNS_SELECTION_HELPER_<ns> owner window through that helper (allowing a sandbox supervisor to stage dragged/pasted host files before delivering SelectionNotify).
    • Restrict EWMH ClientMessage requests sent to the real root window so event.window must belong to the sender's own namespace (or its virtual root), preventing global WM commands such as _NET_CURRENT_DESKTOP.
    • Gate XNS_HOOK_LOG behind XNS_DEBUG=1.
  4. Three Live-Switchable Clipboard Modes (off, clipboard-auto, clipboard):

    • Add XNS_CAPABILITY_CLIPBOARD (1 << 6) and XNS_CAPABILITY_CLIPBOARD_AUTO (1 << 7), controllable via config or at runtime via XnsSetNamespaceFlags:
      • off (default): selections are rewritten to <ns>CLIPBOARD / <ns>PRIMARY.
      • on (allow clipboard): unrestricted shared selections across namespaces with clipboard access.
      • auto (allow clipboard-auto): a focused namespace can claim global selection ownership (copy out), while reading a foreign selection requires a 600ms paste ticket granted on a physical paste gesture (Ctrl+V, Shift+Insert, Super+V, or Button2). Cross-namespace property writes during ConvertSelection are scoped to the active (requestor, property) pair.
  5. Cgroup v2 Auto-Provisioning & xorg.conf Integration:

    • Add default <root|anon> and cgroup <prefix> directives to xnamespace.conf to automatically place local clients matching 0::<prefix>/[<uid>/]<name> into transient namespaces without per-app xauth token setup.
    • Add Option "NamespaceConfig" "<path>" in Section "ServerFlags" of xorg.conf / xorg.conf.d/*.conf.

Commit Breakdown

  1. dix, Xi, xkb: transparent window-tree projection and device state XACE filtering
  2. Xext: namespace: support cgroup-based provisioning and xorg.conf NamespaceConfig
  3. Xext: namespace: add CLIPBOARD and CLIPBOARD_AUTO capabilities
  4. Xext: namespace: access-mode hook model, desktop integration, and input isolation
  5. test/pyxtest: add Xnamespace isolation and window-tree projection test suite

Testing

  • test/pyxtest/test_namespace.py: 31 automated pytest tests running against Xvfb -namespace ... (including native and byte-swapped clients), verified against code mutants across all modified subsystems (31 passed).
  • Live desktop testing on Xorg + i3: verified with Firefox, Chromium, Telegram, GTK3/Qt5/SDL3 apps, system tray icons (jbx-tray, blueman-applet), XDND file drops from Thunar, xclip, and multi-level i3 split containers.

@Jipok

Jipok commented Nov 4, 2026

Copy link
Copy Markdown
Author

To be honest, the work above (and the description) was done entirely by my LLM agents. This is not mindless vibecoding, I spent a lot of time on real testing, applied my best practices for managing agents, and spent quite a lot of money on API calls. Still, I don't know X11 internals well, so I can't fully evaluate the code quality myself.

For context: I am building a lightweight Linux sandbox for LLM agents, desktop applications, and server workloads (as a Docker replacement on my own servers). I am already daily-driving it on my live desktop alongside Xnamespace to dogfood these changes. The sandbox project is almost ready for public release, and I will switch this PR from Draft to Ready as soon as I publish the repository on GitHub.

@Jipok
Jipok force-pushed the jbx-poc branch 2 times, most recently from d5846d0 to a6f1a7b Compare November 4, 2026 13:21
@cepelinas9000
cepelinas9000 requested a review from metux November 4, 2026 15:33
@cepelinas9000

Copy link
Copy Markdown
Contributor

Cgroup v2 Auto-Provisioning & xorg.conf Integration:

Add default <root|anon> and cgroup directives to xnamespace.conf to automatically place local clients > matching 0::/[/] into transient namespaces without per-app xauth token setup.
Add Option "NamespaceConfig" "" in Section "ServerFlags" of xorg.conf / xorg.conf.d/*.conf.

Off topic here, can it have similar treatment with Freebsd jails @b-aaz ?

* Query and modify per-namespace capability flags
(`mouse-motion`, `shape`, `transparency`, `xinput`, `xkeyboard`,
`superpower`).
`superpower`, `clipboard`, `clipboard-auto`).

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.

In my opinion it should be named clipboard-gated or similar, not reading documentation implies some sort default will be used (my first thought is copy-out).

Jipok added 3 commits November 6, 2026 19:56
…E filtering

1. Window-tree projection for reparented windows and virtual roots
   (dix/dispatch.c, dix/events.c, dix/property.c, Xext/xinput/xiquerypointer.c):
   - ProcGetGeometry: when pWin->parent is inaccessible under
     XaceHookResourceAccess(..., DixGetAttrAccess), return root-relative
     coordinates (pWin->drawable.x/y - wBorderWidth(pWin)) so client
     geometry queries match synthetic ConfigureNotify and TranslateCoords
     without exposing foreign WM frame/container windows.
   - ProcQueryTree: filter children through XaceHookResourceAccess; when
     pWin->parent is inaccessible, report root as parent, and when
     querying root, recursively collect accessible descendants reparented
     inside inaccessible WM containers.
   - ProcTranslateCoords, ProcQueryPointer, ProcXIQueryPointer: descend
     through inaccessible ancestor containers to report the caller's own
     window as child while preserving occlusion by foreign windows.
   - deliverPropertyNotifyEvent: when a property changes on a server-owned
     per-namespace virtual root window parented to the screen root, report
     the screen root XID in PropertyNotify and deliver it to clients
     subscribed on the screen root that pass XaceHookReceiveAccess on the
     virtual root.

2. Device state, active grab revocation, and server access filtering
   (dix/dispatch.c, dix/events.c, dix/selection.c, Xext/xinput/,
   Xext/xkeyboard/):
   - ProcKillClient: gate AllTemporary with dixCallServerAccessCallback
     (..., DixManageAccess).
   - GrabDevice: return AlreadyGrabbed on DixGrabAccess BadAccess instead
     of an X error that libX11 XGrabPointer misinterprets as GrabSuccess.
   - SetInputFocus: deactivate any active device grab whose owner no
     longer passes dixCallDeviceAccessCallback(..., DixGrabAccess) after
     focus moves to the new window.
   - ProcQueryPointer, ProcXIQueryPointer, ProcXQueryDeviceState,
     ProcXkbGetState, ProcXkbGetIndicatorState, ProcXkbGetNamedIndicator,
     XkbSendStateNotify: suppress live modifier, button, indicator, and
     key state when dixCallDeviceAccessCallback(..., DixReadAccess) is
     denied.
   - ProcConvertSelection: pass SELECTION_FILTER_NOTIFY when generating
     an ownerless SelectionNotify event.
…spaceConfig

1. Add 'default <root|anon>' and 'cgroup <prefix>' directives to the
   namespace configuration file parser (config.c):
   - A 'cgroup <prefix>' block configures a cgroup v2 path prefix (e.g.
     'cgroup /xns') and acts as a capability template using the existing
     'allow' directives. Local clients whose /proc/<pid>/cgroup matches
     0::<prefix>/[<uid>/]<name> are automatically placed into a transient
     namespace <name> created with those capabilities and removed when
     its last client exits.
   - 'default root' assigns verified local processes (pid > 0) outside the
     configured cgroup prefix to ns_root so the host window manager and
     desktop session operate without per-client xauth token generation,
     while non-local (e.g. TCP) or unidentifiable clients always fail
     closed to ns_anon.
2. Add Option "NamespaceConfig" "<path>" to Section "ServerFlags" in
   xorg.conf / xorg.conf.d (and fall back to /etc/X11/xnamespace.conf when
   the NAMESPACE extension is enabled via Section "Extensions"), allowing
   display managers (GDM, SDDM, LightDM) and distribution packages to
   enable Xnamespace via drop-in config files without altering Xserver
   command-line arguments.
Add two per-namespace clipboard capability bits to support 3 clipboard
modes (off, auto, on) that can be toggled live via SetNamespaceFlags
without reconnecting clients:

- off (!allowClipboard && !allowClipboardAuto): selections are rewritten to
  per-namespace atoms (<ns>CLIPBOARD, <ns>PRIMARY), isolating the namespace
  from the host and other namespaces while keeping intra-namespace
  selections working.
- on (allowClipboard, XNS_CAP_CLIPBOARD = 1 << 6): unrestricted access to
  global selections and ICCCM selection property/SelectionNotify transfers
  across namespaces that share clipboard access.
- auto (allowClipboardAuto, XNS_CAP_CLIPBOARD_AUTO = 1 << 7): focus-gated
  copy and physical input-gated paste workflow. A namespace whose window
  currently holds keyboard focus may set ownership of global selections
  (exporting data to the host or other namespaces), while reading global
  selections owned by another namespace requires a genuine physical key or
  button press delivered while focused, and is revoked as soon as focus
  leaves the namespace or another namespace takes ownership of a global
  selection.
@Jipok
Jipok force-pushed the jbx-poc branch 2 times, most recently from dc5543e to e04523c Compare November 7, 2026 15:07
Jipok added 2 commits November 7, 2026 22:34
…ut isolation

1. Access-mode hook model and server hardening (hook-resource.c,
   hook-property.c, hook-server.c, hook-ext-access.c, hook-ext-dispatch.c,
   namespace.c, hooks.h):
   - Enforce Dix*Access bitmasks in hookResourceAccess, hookPropertyAccess,
     and hookServerAccess; add missing superPower bypass and BadAccess
     status in hookPropertyAccess and hookReceive.
   - Make GrabServer / UngrabServer a no-op (Success) for non-superPower
     clients so confined toolkits (e.g. SDL3) cannot freeze the display.
   - Allow QueryExtension for SHAPE and XINPUT so client libraries do not
     permanently cache extension absence; block Composite, XTest, Record,
     Security, and MIT-Screen-Saver for non-superPower namespaces.
   - Gate XNS_HOOK_LOG behind the XNS_DEBUG environment variable.

2. Virtual root EWMH, root properties, and system tray integration
   (hook-init-rootwindow.c, hook-windowproperty.c, hook-receive.c,
   hook-selection.c, hook-send.c, config.c):
   - Populate self-referential _NET_SUPPORTING_WM_CHECK and _NET_WM_NAME
     on per-namespace virtual root windows.
   - Allow read-only access to non-sensitive global root properties and
     deliver corresponding PropertyNotify and RandR events on root while
     keeping _NET_CLIENT_LIST*, _NET_ACTIVE_WINDOW, _NET_SUPPORTING_WM_CHECK,
     and CUT_BUFFER* isolated.
   - Allow confined clients to discover _NET_SYSTEM_TRAY_S*, _NET_WM_CM_S*,
     and WM_S* selections and send _NET_SYSTEM_TRAY_OPCODE dock requests
     for their own tray icon windows.

3. Background input and focus isolation (hook-device.c, hook-receive.c,
   config.c):
   - Gate DixReadAccess (live device state) on XnsNamespaceHasFocus (and
     pointer coordinates in QueryPointer/XIQueryPointer on allowMouseMotion),
     preventing unfocused background namespaces from polling keymaps,
     modifiers, buttons, or receiving XI_RawMotion.
   - Grant a 1000ms input-focus ticket on physical button/key delivery to
     a namespace's own window so clicking a tray icon allows popup menu
     pointer/keyboard grabs while keyboard focus is in another window.
…t suite

Add optional auth_proto / auth_data handshake support to RawX11Connection
and a 30-test pytest suite (test/pyxtest/test_namespace.py) verified
against 33 code mutants:
- cross-namespace window, property, KillClient(AllTemporary), GrabServer
  no-op, and root EWMH ClientMessage target-window isolation;
- transparent reparented WM window-tree projection (QueryTree, GetGeometry,
  TranslateCoordinates, QueryPointer, XIQueryPointer) for both native and
  byte-swapped clients;
- read-only non-sensitive root properties, ListProperties and CUT_BUFFER0
  filtering, PropertyNotify filtering, virtual root PropertyNotify delivery
  (ChangeProperty, DeleteProperty, partial vs full GetProperty(delete=True),
  RotateProperties), and virtual root EWMH properties
  (_NET_SUPPORTING_WM_CHECK, _NET_WM_NAME);
- background input isolation (QueryKeymap, XQueryDeviceState, XkbGetState,
  XkbGetIndicatorState, XkbStateNotify, XI_RawMotion, GrabPointer returning
  AlreadyGrabbed, active keyboard grab revocation on focus loss, 1000ms
  focus ticket on physical click, QueryPointer/XIQueryPointer without
  allow-mouse-motion);
- clipboard off/on/auto selection isolation, physical key/button press
  gating while focused (revoked on focus loss or new selection owner,
  rejecting synthetic SendEvent), cross-namespace SelectionRequest ->
  ChangeProperty (including DixPostAccess overwrite and multi-chunk INCR) ->
  SelectionNotify transfer, export revocation on host selection ownership,
  and live SetNamespaceFlags toggling;
- _NET_SYSTEM_TRAY_S0 discovery and _NET_SYSTEM_TRAY_OPCODE dock validation;
- extension access filtering (Composite, XTEST, RECORD, SECURITY,
  MIT-SCREEN-SAVER, X-NAMESPACE vs SHAPE, XInputExtension, XKEYBOARD,
  RANDR, XFIXES).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants