Repository navigation
Conversation
… pages dwl faulted for a zone go with it A page dwl reads before the zone writes it is charged to dwl, and a zone that kept its descriptor kept that page after the pool was gone, which no bound on live pools held. The proxy now keeps its own copy of each pool's descriptor and punches the pool's range (fallocate, FALLOC_FL_PUNCH_HOLE) once the pool and its buffers are deleted and no surface shows one. It refuses a pool it could not punch (not open for writing, sealed against writes, not shared memory) and seals each one it takes against a later write seal. The pools of a connection that ended are punched a second later, once dwl has read the hang-up, and count against the zone until then. The proxy's call list gains fstatfs and fallocate. The pool tests use memfds, as clients do.
…y hold, so 32 clients still fit The proxy keeps a copy of each wl_shm pool's descriptor to punch it, up to MAX_SHM_POOLS_PER_ZONE (256), and reserves them: under kryptikd's limit as it was, 32 clients' worth plus a few, it would have taken 30 clients. The limit grows by 256, and the descriptor test holds it to 32.
… two sessions still fit The proxy now reserves 256 descriptors for the copies of its zone's pools; under the test's old limit of 300 one session fit, not two.
…le are punched by the last pool_fd read the seals and only then added F_SEAL_SEAL, so a client thread could add F_SEAL_FUTURE_WRITE between the two. Its mapping keeps working under that seal, and the punch then fails with EPERM, leaving the pages dwl faulted charged to dwl. The seal now comes first, which makes the read final, and a failed read refuses the pool. wl_shm.create_pool takes any descriptor, so two pools may share one file, and punching the first to go cleared pages the other still shows. Each pool now keeps its file's device and inode. One that goes while another on its file remains hands that one its range and its charge, and the last punches the widest. So a large pool released before a small one on the same file stays counted until its pages are freed. A punch that fails is logged through the main loop's rate-limited log. The proxy's filter allows fstat, and fallocate only for a punch that keeps the size.
…xt sharp at 1.5 At a fractional output scale a zone's client could only draw at the next whole scale and have the compositor scale it down, which blurs text and costs memory. The proxy now lets wp_fractional_scale_manager_v1 through, and dwl's zone sockets offer it too: the compositor tells each window the scale of its output in 120ths, and Firefox, which uses the protocol by default, draws at that scale through wp_viewporter. It tells a zone only its output's scale, which wl_output already gives it rounded up. The proxy's tables are generated again with fractional-scale-v1.xml from wayland-protocols 1.49, the others unchanged byte for byte. A proxy test passes the request and the scale; wlprobe scale asks for it, and the desktop suite's zone window hears it through its proxy.
…nstead of starting a second With no remote, a second firefox in a zone whose browser is open only says that Firefox is already running. dwl now has an action for the browser key, focusorlaunch: it takes the zone's window whose app_id is the proxy's kryptik.<zone>.firefox-default, matched with the zone of the client's socket, views its first tag on its monitor when it is not shown and gives it the keyboard, and starts the zone's Firefox only when there is no such window. The claimed app_id is named once in dwl-config.h. The menu keeps every launch, as a persistent zone's passphrase is asked on its terminal. dwl's status names the zones whose browser has a window, which the chrome records in browsers; when it names the zone, b writes the zone's name to browser-front in the session's runtime directory instead. dwl takes that request only from a regular file of the session user's that no one else may write, in a directory of the user's that no one else may write, removes it before acting, and only ever puts a window in front. The focus record gains the window's app_id and the tags viewed. The desktop suite presses the key from another tag: tag 3 is viewed, Firefox has the keyboard, and no second Firefox starts. A request root wrote is removed unheard, the user's puts Firefox in front, and with the zone stopped the key starts Firefox.
… stamped it The browser key matches that app_id; printed where Firefox has the keyboard, it shows the value dwl-config.h should name whatever the later checks find.
…nd the design names the case left As the chrome writes it, so the check does not depend on the suite's own umask. A press of the key or b while the zone's Firefox is starting, before its window shows, still starts a second one. The app_id is the program name GTK takes, which Firefox sets to its remoting name.
…s shown and has the keyboard A zone file may say [desktop] hold_awake = true. It is off by default; personal and work set it, untrusted sets it false beside its reason, and the nic zone is refused it. kryptikd starts the zone's proxy with --hold-awake from it, and only then does the proxy let zwp_idle_inhibit_manager_v1 through. The protocol tables gain idle-inhibit-unstable-v1.xml from wayland-protocols 1.49, beside the six hashes they already name. zone-colours.h carries the same setting for dwl, from the same files. dwl offers the inhibitor on such a zone's socket alone, and counts one only from such a zone, on a mapped window shown on a lit output, while a window of that zone has the keyboard and the session is unlocked. A hold lasts four hours from when it began or a key or button was last pressed. dwl's status names the zone holding the screen awake, for the chrome. A keyboard focus change, an output's power and the cap's timer each check the hold again. Upstream dwl 4847f97 comes as patch 0003: a zone can now keep an inhibitor on a surface whose window it unmaps, and the check reads that surface's pointer. wlprobe gains an inhibit mode and an idle mode, and the desktop suite checks that personal's window holds zone 0's idle timer off, that the hold ends when a zone 0 window or the lock takes the keyboard, and that untrusted is offered no inhibitor.
…own, and the design says when a hold begins again Both were joined to the line before by a line continuation the edit did not mean. A hold that a lid close, a suspend or the lock ended counts again, with its four hours anew, once its window has the keyboard back after the unlock.
…unts, and the chrome names the holder kryptik-lock --idle waited on ext-idle-notify-v1's input-only notification, so no hold could keep the session unlocked. dwl now counts a hold only from a zone whose file allows it, on a shown window with the keyboard, for four hours from the last key or button press. So all three of the watcher's timers wait on the notification that heeds inhibitors, and bind version 1, which has it. The chrome records dwl's holdawake line in the session's runtime directory, as it records browsers, and the menu names the zone holding off the lock and the screens going dark. The desktop suite checks the record during the hold and after it ends, and that a 2 s idle lock started during the hold is still waiting 6 s later.
…l counts It still said the idle lock counted input alone, the protocol's version 2, which this branch changed.
…ry zone window in the suite does On tag 1 the chrome's zone 0 window has the keyboard, so a zone window maps behind it and gets none until the user picks it. The hold's block waits for the window to draw and has the host press Alt+j, which reaches it whether zone 0's window is there or not.
…er-focus-or-launch # Conflicts: # build/guest-tests/gui-check.sh # tools/desktop/kryptik-chrome # tools/image/gui-test.sh
…ld-awake # Conflicts: # compartments/kryptikd/src/zone.rs # compartments/kryptikd/src/zone/tests.rs # tools/desktop/wlprobe.c
…ake, beside the microphone's and the camera's holders A hold's usual window is a video in front, which covers the chrome that would name the holder. The bar takes it from dwl's own count; the marks' look each second draws it again when the holder changes.
A zone name is at most 12 bytes, so the name, the microphone's, the camera's and the awake marks take 71 with the terminator; 64 cut the last.
dwl ran in the root cgroup with no limit, and a zone's pool pages that dwl reads before the zone writes them are charged to dwl: a zone could make the compositor hold memory outside every limit. The session's last step, kryptik-launch --desktop, asks the launch daemon to move it into kryptik-desktop, then becomes dwl, so everything the desktop starts is inside. The group sits beside kryptik/ rather than in it, under a memory.max of a quarter of the memory and 1 GiB at least. The daemon moves only the asker, and refuses root, a pid another uid holds and any process in a zone's or a proxy's cgroup. A desktop it cannot bound starts anyway, and the session log says so. Checked by cgroup.rs's unit test, launch-desktop.py, serve.sh's S3h and gui-check's desktop-memory-bound. launch-desktop.py checks that the launcher becomes the desktop in its own process whatever the daemon answers.
wlprobe charge keep commits one unwritten 4 MiB buffer in untrusted, then, 4 s after it is drawn, drops the buffer and its pool and unmaps the window while it keeps the pool's descriptor, as a zone may. gui-check reads the desktop cgroup's memory.current before, while dwl shows the buffer, and once the pool is gone, and prints the rises on a desktop-charge line: the measurement the design asked for. It is what the proxy's punch will change: until the proxy punches the range, the pages dwl faulted stay the desktop's. tools/tests/desktop-tools.sh compiles wlprobe.c and kryptik-launch.c with the stage's hardening flags and checks their usage, so a change to either is caught in CI before a Distro build.
A zone that outlives the desktop and keeps its pools' descriptors keeps their pages charged to the desktop's group: they are shmem, which nothing reclaims without swap. The next login's dwl joined that same group and could start at its bound. join_desktop now removes the group first. rmdir succeeds on an unpopulated group whatever it still has charged, and its charges then count at the root. EBUSY keeps a group an earlier desktop's processes still hold.
dwl-zone-borders.py makes about seventy exact-string edits to dwl 0.8, and CI checked only that they apply as text: a C error in them showed hours into a Distro build, in stage 04. tools/tests/dwl-compile.sh fetches the pinned wlroots, wayland-protocols and dwl, trying each source as fetch-sources.sh does, and keeps each only with sources.lock's hash. It writes the two headers meson would generate for wlroots and every protocol's server header, then applies build/patches/dwl-0.8 and the zone-border edits in stage 04's order with its config. It compiles dwl.c and util.c with dwl's own warning flags, -fsyntax-only, against the pinned headers. A negative control, a member that does not exist, must fail. The compartment job installs the system headers and runs it; without them it exits 77.
…dwl's filter refuses exec The first part of the compositor's own confinement (docs/design/compositor-separation.md, step three). Once the status command runs, dwl forks a spawner. The spawner closes everything but one end of a socket, names itself dwl-spawner, is not dumpable and ends with dwl. It runs only the commands in dwl-config.h's spawncmds, each as dwl's spawn did, sent to it by their index. dwl then sets no-new-privileges and a seccomp filter that refuses execve, execveat, ptrace and process_vm_writev, and kills calls of another architecture or ABI. A dwl made to run code reaches no program but the table's, and cannot steer the spawner. gui-check's dwl-execs-nothing reads Seccomp: 2 and NoNewPrivs: 1 in dwl's status and finds its spawner child; every key the suite presses goes through the spawner.
wlroots 0.19.3's version.h.in names WLR_VERSION_STR, _MAJOR, _MINOR and _MICRO with #mesondefine, not @var@, which the placeholder check caught on its first run in CI.
Its second run in CI found the check working end to end, negative control included, and two errors that were the host's: Kryptik's edits call wl_client_get_user_data and wl_client_set_user_data, which libwayland 1.23 added, and Ubuntu 24.04's headers are 1.22. The script now fetches the pinned wayland (1.26.0) as it does wlroots, writes its wayland-version.h and its core protocol's server header, and puts its src/ before the host's include path.
kryptik-launch returns once the zone runs, so waiting on the launch did not wait for the probe, which keeps its window 10 s. The socket checks after it then found untrusted still running and could not start their own, and failed with no list to read.
…rd back Killing the background job ended the shell around su, not the probe under it, so the zone 0 window stayed in front with the keyboard and hold-ends-at-lock saw none come back. The window is ended by its command, as the suite's other probes are.
… any pages the proxy left memory.current counts dwl's own allocations too, and #565's proof showed it rising after the pool went (+2024 KiB shown, +2748 KiB gone), which says nothing either way about the punch. The pool is shmem, so the desktop cgroup's shmem in memory.stat shows its pages alone; the proxy's log says if a punch failed.
… zone ends first, and its INFO reads shmem
… the charge probe's fixes
…e the dwl suites to CI
…ver name a stage is_version took any run of letters, digits and . + - ~, so a signed statement or manifest naming the version '..' would have had staging() resolve to the update directory itself, listed names landing beside pointer and wanted. Only a signed statement can name a version, so this closes what the signature alone stood for.
…R off, queues taken
A zone could hold its launcher in close(2). It hands the broker a TCP socket whose send queue a
peer never reads, with SO_LINGER {1, INT_MAX}, and closes its own copy. The launcher's close of
the last copy then waits on the linger, relaying and reaping nothing, and the zone's cleanup
(grants revoked, the volume closed and its key wiped) waits with it.
Every descriptor recv_with_fds takes now has SO_LINGER turned off as it arrives. The kernel
closes descriptors in three more places on the receiving thread, and each is closed off:
- what does not fit the control buffer: it now has room for every descriptor a message can
carry (253), and past twelve a request is refused with all of them released;
- descriptors sent with later bytes, which the broker read with recv(): it reads with recvmsg;
- descriptors still queued when a connection goes: the broker shuts and drains each connection
it is done with, and the daemon each one whose request it has read.
release() also shuts and drains a Unix socket handed over before closing it, and releases
whatever was queued on it the same way.
time-offset also refuses a zone without the network before finding the floor, which can check
the release under the update chain's lock.
…zone's launcher up to a check's 60 s Each piece is answered under the 5 s deadline, but the piece that completes the list and its signature, and the hourly newest pointer, also wait on a check of the release, which CHECK_DEADLINE bounds at 60 s.
…sockets are held while it is An empty datagram queue answers EAGAIN even when shut, so a 0 ends the drain only on a stream, which carries no empty message: a datagram socket's empty datagrams no longer stop the drain ahead of the descriptors behind them. A seqpacket's 0 may be an empty message or its end, so it keeps the bound of 1024 messages. Descriptors taken from a queue that are not Unix sockets close at once, already defused, so only the sockets whose own queues are still to take are held.
…keeps it GNU coreutils installs chroot in /usr/sbin, and uutils, Ubuntu 26.04's coreutils, in /usr/bin. The check named /usr/sbin/chroot, so on 26.04 the zone's shell found nothing to run and exited 127 where the filter's kill gives 159.
Back from tty2, dwl answers clients as soon as its seat is enabled again, and libinput reopens the keyboards through the seat a moment later. The single Alt+p the host sends on GT KEY-MENU-AFTER-VT could arrive in between and be lost, as in the compositor proof's run (menu windows: 1 before Alt+p, 1 after). The check now waits until dwl holds as many /dev/input/event descriptors as before the switch, says so when they do not come back, and names both counts if no menu opens.
…k waits for dwl's input devices
…t medium Stage 06 signs the two image lists the guest must refuse, one in the release manifests' namespace and one for another root, only for a development build, since a production release key signs real lists alone. The guest still ran both checks on a production medium, where keep_list found nothing and the checks failed on the previous zone's result, and zones-test.sh required both. Acceptance's zones part would so have failed on every production build: the v1.0.0 tag, and a role=production rehearsal. The guest now runs them on a development medium and says why it skips them on a production one; the suite takes the role from the payload's manifest, as the update suite does for its controls.
…ces counted by their links
… a keyboard added after it confines itself takes keys wlroots 0.19.3 gives each keyboard its keymap in a file made by shm_open under /dev/shm. dwl's Landlock rules refused that create, so a keyboard libinput added after dwl confined itself had no keymap. wlroots then left it out of dwl's keyboard group, and its keys reached nothing. This covers the switch back from the administration console, a keyboard plugged in after login, and one found again after a sleep. The rule beneath /dev/shm grants what that path does: create, open for reading and writing, open again read-only, truncate and unlink.
…lroots writes under /dev/shm
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Not for merging: a Distro proof of the batch that follows the compositor stack. It is assembled on #585's head 0d9fb3e, so it proves the stack and the batch together. Each PR lands through its own branch once this run is green.
The batch, each at its read head:
Merge resolutions (rerere recorded):
JobKind::Desktopand A stick is taken back as a job of the launch daemon's, so a slow one holds no other request #595'sJobKind::StickEject, both variants and both finish_job arms.