diff options
| author | Craig Jennings <c@cjennings.net> | 2026-10-17 07:07:25 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-10-17 07:07:25 -0500 |
| commit | cd6e11b46ecf2b56bf63d6c11886dccc70875e10 (patch) | |
| tree | e2d17ef968ae24ef1b65babaadfc7637cc2024bb /working/keyring-crash/run-regression.sh | |
| parent | e530fd597b62bda520f9804be7da216eea582750 (diff) | |
| download | archsetup-claude/hungry-franklin-09fffc.tar.gz archsetup-claude/hungry-franklin-09fffc.zip | |
feat(keyring): ship a patched gnome-keyring and install it from the repoclaude/hungry-franklin-09fffc
gnome-keyring 48 through 51.1 aborts when a client's first OpenSession is dispatched before the daemon has created its per-client record. Each abort drops the unlocked state and the next request pops the password dialog. On ratio that happened about three times a day from 2026-10-11, when the ChatGPT desktop app's codex app-server started bursting keyring reads. The same-day glib and keyring upgrades were a coincidence, since 50.0 reproduces it too. Upstream's fix (merge request !112) has sat unmerged since August.
packages/gnome-keyring is Arch's recipe for 1:51.1-1 built from GNOME's tarball plus one patch. It registers the client at the point of use and returns a real D-Bus error instead of completing OpenSession with a NULL body. It resolves a collection alias only after the caller's session exists. It also locks the alias table so the worker-thread filter can't read an entry the main thread is freeing, a pre-existing defect that AddressSanitizer confirmed. I chose pkgrel 1.1 so stock sorts below the rebuild and any future Arch or upstream fix replaces it on a normal upgrade. Nothing pins the package.
install_gnome_keyring_fix replaces the plain stock install, which pacman's --needed would otherwise use to downgrade the rebuild on every re-run. It installs stock only when no gnome-keyring is present, compares versions with vercmp, then stages the recipe into the source dir and builds it with makepkg as the user. The rebuild is a no-op, anything newer skips with a visible note, and a failed build leaves stock in place. I also corrected the keyring-dir comment: the default alias only picks the collection and doesn't unlock anything under autologin.
working/keyring-crash holds the probe, the guest fixture and runners, and the deployment script with backup and rollback. status.org there records the cause and results. Stock aborts on the probe's first batch. The patched daemon, its sanitizer build and the installed package pass every mode, and the upstream suite scores the same for both.
Diffstat (limited to 'working/keyring-crash/run-regression.sh')
| -rwxr-xr-x | working/keyring-crash/run-regression.sh | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/working/keyring-crash/run-regression.sh b/working/keyring-crash/run-regression.sh new file mode 100755 index 0000000..a853e40 --- /dev/null +++ b/working/keyring-crash/run-regression.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# The sshpass password below is the disposable test guest's throwaway root +# password (scripts/testing/archsetup-test.conf), reachable only on loopback. +# It is not a credential for anything real. +# Host-side regression driver for the disposable keyring guest. +# run-regression.sh <label> <daemon-binary-in-guest> [ASAN_OPTIONS] +# Resets the guest fixture with that daemon, runs every probe mode, verifies +# the synthetic secret after each mode, and prints a summary. Output goes to +# stdout; the caller redirects it to a log. Exit 1 on any failure. +set -uo pipefail +label="$1"; daemon="$2"; asan="${3:-}" +here="$(cd "$(dirname "$0")" && pwd)" +PORT="${PORT:-2237}" +ssho=(-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -o LogLevel=ERROR -o PreferredAuthentications=password -o PubkeyAuthentication=no) +g() { sshpass -p archsetup ssh "${ssho[@]}" -p "$PORT" root@127.0.0.1 "$@"; } +gcp() { sshpass -p archsetup scp "${ssho[@]}" -P "$PORT" "$@"; } +as_user='runuser -u keyring-test -- env XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus' +fail=0 +echo "=== $label: daemon=$daemon asan=${asan:-none} $(date -Is)" +gcp "$here/probe.py" root@127.0.0.1:/opt/keyring-vm/probe.py >/dev/null +gcp "$here/setup-guest.sh" root@127.0.0.1:/root/setup-guest.sh >/dev/null +g "chmod +x /root/setup-guest.sh; KEYRING_DAEMON_BINARY='$daemon' ASAN_OPTIONS='$asan' /root/setup-guest.sh" || { echo "FIXTURE FAILED"; exit 1; } +since=$(g 'date "+%Y-%m-%d %H:%M:%S"') +owner0=$(g "$as_user busctl --user status org.freedesktop.secrets 2>/dev/null | grep -E '^(PID|Comm)='" | tr '\n' ' ') +echo "owner before: $owner0" +run_mode() { # mode clients workers burst + local out + out=$(g "$as_user python3 /opt/keyring-vm/probe.py $1 --clients $2 --workers $3 --burst $4 --batch 100" 2>&1) + local rc=$? + echo "$out" | tail -3 + [ $rc -eq 0 ] || { echo "MODE $1 FAILED (rc=$rc)"; fail=1; } + local secret + secret=$(g "$as_user secret-tool lookup purpose keyring-vm" 2>/dev/null) + if [ "$secret" = "synthetic-vm-secret" ]; then echo "secret after $1: ok"; else echo "secret after $1: FAILED (${secret:-empty})"; fail=1; fi +} +run_mode plain 4000 32 8 +run_mode dh 4000 32 8 +run_mode invalid 4000 32 8 +run_mode disconnect 4000 32 8 +run_mode pipeline 4000 32 8 +run_mode alias 2000 32 1 +run_mode isolation 2000 32 1 +owner1=$(g "$as_user busctl --user status org.freedesktop.secrets 2>/dev/null | grep -E '^(PID|Comm)='" | tr '\n' ' ') +echo "owner after: $owner1" +[ "$owner0" = "$owner1" ] || { echo "OWNER CHANGED"; fail=1; } +echo "--- daemon journal since $since (assertions/aborts/criticals)" +g "journalctl --since '$since' --no-pager -o short-iso 2>/dev/null | grep -E 'gnome-keyring|secrets|asan|AddressSanitizer' | grep -iE 'assert|abort|SIGABRT|critical|sanitizer|ERROR|dumped' | head -40" +if [ -n "$asan" ]; then echo "--- asan logs"; g 'ls /tmp/asan.log* 2>/dev/null && cat /tmp/asan.log* | head -80 || echo "no asan log files"'; fi +echo "=== $label result: $([ $fail -eq 0 ] && echo PASS || echo FAIL) $(date -Is)" +exit $fail |
