Repository navigation
Rework xnamespaces - #4074
Rework xnamespaces#4074Jipok wants to merge 5 commits into
Conversation
|
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 |
d5846d0 to
a6f1a7b
Compare
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`). |
There was a problem hiding this comment.
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).
…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.
dc5543e to
e04523c
Compare
…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).
Summary
This series completes
Xext/namespaceso 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 asi3,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>) andOption "NamespaceConfig"inxorg.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-testpyxtestsuite (test/pyxtest/test_namespace.py).Motivation & Problems Solved
Reparenting / Tiling Window Manager Geometry & Tree Projection (
dix/,Xi/):i3) wrap client top-level windows inside multi-level container hierarchies (root -> workspace -> split -> frame -> client).ProcQueryTreedid not filter children throughXaceHookResourceAccessand exposed the parent container XID, whileProcGetGeometryreturned container-relative coordinates (pWin->origin.x/y == (0, 0)) that conflicted with root-relative syntheticConfigureNotifyevents andTranslateCoordinates. In Firefox (libgdk-3+libxul), this mismatch causedgdk_window_x11_get_frame_extentsto compute a bogus frame margin equal to the tile's screen offset and enter an infiniteConfigureRequest↔ConfigureNotifyresize loop.root:ProcGetGeometry: whenpWin->parentis inaccessible underXaceHookResourceAccess(..., DixGetAttrAccess), return root-relative screen coordinates (pWin->drawable.x/y - wBorderWidth(pWin)).ProcQueryTree: filter children withXaceHookResourceAccess; projectreply.parent = rootwhenpWin->parentis inaccessible; and when queryingroot, recursively collect accessible descendants reparented inside inaccessible WM containers.ProcTranslateCoords,ProcQueryPointer,ProcXIQueryPointer: descend through inaccessible ancestor containers to report the caller's own window aschildwhile preserving occlusion by foreign windows.Background Input State & Active Grab Isolation (
dix/,Xi/,xkb/,Xext/namespace):ProcQueryPointer,ProcXIQueryPointer,ProcXQueryDeviceState,ProcXkbGetState,ProcXkbGetIndicatorState,ProcXkbGetNamedIndicator, andXkbSendStateNotifypreviously leaked live modifier, button, key, or LED state without checkingdixCallDeviceAccessCallback(..., DixReadAccess).GrabDevicereturned a protocol error (BadAccess) when denied by XACE, whichlibX11'sXGrabPointer/XGrabKeyboardmisinterprets as0(GrabSuccess).GrabKeyboardacquired while focused persisted after the host WM moved focus to another window.DixReadAccess) inhookDeviceonXnsNamespaceHasFocus(subj->ns)(and pointer coordinates inQueryPointer/XIQueryPointeronallowMouseMotion), and always deny global keymap polling (QueryKeymap,XQueryDeviceState) so confined clients receive zeroed state bitmaps instead of fatal X errors.AlreadyGrabbedinGrabDevicewhendixCallDeviceAccessCallbackreturnsBadAccess.SetInputFocus, deactivate any active device grab whose owner no longer passesdixCallDeviceAccessCallback(..., DixGrabAccess)after focus moves.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.Access-Mode Hook Model & Desktop Integration (
Xext/namespace):hook-resource.c,hook-device.c,hook-server.c, andhook-ext-dispatch.cwithDix*Accessbitmask checks.GrabServer/UngrabServera safe no-op (Success) for non-superPowerclients so toolkits (SDL3, GDK) cannot freeze the display or crash onBadAccess.ProcKillClient(AllTemporary)withdixCallServerAccessCallback(..., DixManageAccess)._NET_SUPPORTING_WM_CHECKand_NET_WM_NAMEon virtual roots; allow read-only access andPropertyNotify/ RandR events for non-sensitive global root properties while keeping_NET_CLIENT_LIST*,_NET_ACTIVE_WINDOW,_NET_SUPPORTING_WM_CHECK, andCUT_BUFFER*isolated._NET_SYSTEM_TRAY_S*,_NET_WM_CM_S*, andWM_S*selections and send_NET_SYSTEM_TRAY_OPCODEdock requests only for their own icon windows.XdndEnter/XdndPosition/XdndDropfromsuperPowerclients) so target namespaces with clipboard access can readXdndTypeList/XdndActionListandXdndSelectionand reply withXdndStatus/XdndFinished, and optionally routeConvertSelectionrequests 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 deliveringSelectionNotify).ClientMessagerequests sent to the real root window soevent.windowmust belong to the sender's own namespace (or its virtual root), preventing global WM commands such as_NET_CURRENT_DESKTOP.XNS_HOOK_LOGbehindXNS_DEBUG=1.Three Live-Switchable Clipboard Modes (
off,clipboard-auto,clipboard):XNS_CAPABILITY_CLIPBOARD(1 << 6) andXNS_CAPABILITY_CLIPBOARD_AUTO(1 << 7), controllable via config or at runtime viaXnsSetNamespaceFlags:<ns>CLIPBOARD/<ns>PRIMARY.allow clipboard): unrestricted shared selections across namespaces with clipboard access.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, orButton2). Cross-namespace property writes duringConvertSelectionare scoped to the active(requestor, property)pair.Cgroup v2 Auto-Provisioning &
xorg.confIntegration:default <root|anon>andcgroup <prefix>directives toxnamespace.confto automatically place local clients matching0::<prefix>/[<uid>/]<name>into transient namespaces without per-appxauthtoken setup.Option "NamespaceConfig" "<path>"inSection "ServerFlags"ofxorg.conf/xorg.conf.d/*.conf.Commit Breakdown
dix, Xi, xkb: transparent window-tree projection and device state XACE filteringXext: namespace: support cgroup-based provisioning and xorg.conf NamespaceConfigXext: namespace: add CLIPBOARD and CLIPBOARD_AUTO capabilitiesXext: namespace: access-mode hook model, desktop integration, and input isolationtest/pyxtest: add Xnamespace isolation and window-tree projection test suiteTesting
test/pyxtest/test_namespace.py: 31 automated pytest tests running againstXvfb -namespace ...(including native and byte-swapped clients), verified against code mutants across all modified subsystems (31 passed).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-leveli3split containers.