Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.

Inbox
ReportR-1025GitHub issuePublicFixed

Lock screen re-arms fingerprint PAM indefinitely — 6,451 cycles / 6,454 USB resets over 53h

Reported by surfstyk · filed Sep 3, 2026Closed upstream on 2026-10-11 by the bot omarchybot, as a duplicate of #9913 (R-1029). GitHub close reason: duplicate.

In the audited area (lock) — decided by agents · verified. Not validated yet.

At a glance

1 · What is claimed?
Only the report text below — no validator has restated it as checkable claims yet.
2 · What does it affect?
AreaIn the audited arealock· Decided by agents · verified
3 · Is it reproducible?
No validation has been run yet.
4 · What remains unclear?
  • Not validated yet: no claims, no reproducer.
5 · What happens next?
Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. So far: in the audited area (lock) — decided by agents · verified. The outcome (valid, duplicate, not a bug, …) is not decided yet. See the decision.

Original report

Lock screen re-arms fingerprint PAM indefinitely — 6,451 cycles / 6,454 USB resets over 53hrevision 1 · captured 2026-09-28 21:24 UTC via poll

Summary

The Quickshell lock screen re-arms fingerprint PAM unconditionally and without bound for the entire duration of a lock. On a machine that stays locked for a long time, this produces a continuous ~30s authentication cycle that never stops. On my laptop it ran for 53 hours straight — 6,451 PAM cycles and 6,454 USB resets of the fingerprint sensor — and the session ended in a hard kernel freeze whose last log line was one of those USB resets.

The unbounded retry is clearly visible in the source. The freeze is circumstantial and I am not claiming it as proven — but the retry loop itself looks like a bug worth bounding regardless.

Environment

  • Omarchy 4.0.2-1
  • Hyprland 0.56.2, SDDM, kernel 7.1.9-arch1-2
  • Dell Inspiron 16 5635, AMD Ryzen 7 7730U w/ Radeon Graphics
  • Fingerprint sensor: Goodix 27c6:639c ("Goodix MOC Fingerprint Sensor (press)"), one enrolled finger
  • /etc/pam.d/omarchy-lock-fingerprint present and correctly wired (auth required pam_fprintd.so) — this is not the Omarchy 3 / hyprlock "PAM not wired up" bug from #5503

Steps to reproduce

  1. Enroll a fingerprint (Setup > Security > Fingerprint).
  2. Configure the machine to stay awake while unattended — in my case an always-on host with:
   # /etc/systemd/logind.conf.d/30-alwayson.conf
   HandleLidSwitch=lock
   IdleAction=ignore
  1. Let the screen lock and leave it locked for hours (lid closed is enough).
  2. Watch the journal.

Observed behaviour

A cycle repeating every ~30.3s, indefinitely:

15:53:23 omarchy-shell[1390293]: quickshell.service.pam.subprocess: Starting pam session for user "..." with config "omarchy-lock-fingerprint"
15:53:23 kernel: usb 3-4: reset full-speed USB device number 3 using xhci_hcd
15:53:23 omarchy-shell[1390293]: Relaying pam message: "Place your right index finger on the fingerprint reader"
15:53:53 omarchy-shell[1390293]: Relaying pam message: "Verification timed out"
15:53:53 omarchy-shell[1390293]: Error while authenticating: "Authentication service cannot retrieve authentication info" (code 9)
15:53:53 omarchy-shell[1390488]: Starting pam session ... (next subprocess, new PID)

The 30s is fprintd's own verification timeout; the extra 250ms is fingerprintRetryTimer.

Totals for that single boot (Aug 31 07:34:52 → Sep 2 15:56:58, ~53h):

  • 6451 × "Place your right index finger"
  • 6454 × usb 3-4: reset ... using xhci_hcd

The loop began 15 minutes after the first idle-lock following the 4.0.1 → 4.0.2 upgrade and reboot.

Root cause

shell/plugins/lock/Service.qml re-arms on every non-success result, and again on error:

function handleFingerprintFinished(result) {
  fingerprintAuthenticating = false
  if (!lockRequested) return
  if (result === PamResult.Success) {
    finishUnlock()
  } else if (fingerprintConfigured) {
    fingerprintRetryTimer.restart()      // no cap, no backoff
  }
}
onError: function(error) {
  root.fingerprintAuthenticating = false
  if (root.lockRequested && root.fingerprintConfigured) fingerprintRetryTimer.restart()
}
Timer { id: fingerprintRetryTimer; interval: 250; repeat: false; onTriggered: root.startFingerprint() }

The intent is documented at ~line 427 ("The fingerprint PAM stays armed for the whole lock"), so this is by design — but the design has no upper bound, no backoff, and no way to opt out short of deleting /etc/pam.d/omarchy-lock-fingerprint. shell.json exposes only idle.screensaver and idle.lock.

A timeout is treated identically to a genuine failed match. Nothing distinguishes "nobody is at the machine" from "someone is trying to unlock".

Why it matters

Each cycle spawns a PAM subprocess and triggers a USB device reset. Over two days that is ~6.5k of each. Beyond the wear on the sensor and the journal noise, an unattended locked machine is exactly the state where nobody is watching for trouble.

On the freeze, stated carefully: the journal ends abruptly at 15:57:28 mid-boot with no shutdown, no panic, no OOM, no thermal or DRM errors, and the final line is the 6,454th usb 3-4: reset ... xhci_hcd. That is suggestive, not conclusive — nothing was written to disk. I mention it only as motivation for bounding the loop, not as a diagnosis.

Suggested fix

Any of:

  • Cap consecutive retries, and stop re-arming after N failures until user input (a keypress or pointer motion) is seen.
  • Back off exponentially instead of a fixed 250ms.
  • Distinguish PamResult timeout from a real match failure, and don't re-arm on timeout when the session has been idle.
  • Expose a bound in shell.json (e.g. lock.fingerprintRetries).

The first seems closest to intent: a person actually standing at the machine generates input events, so gating re-arm on recent input would keep fingerprint unlock instant for real use while letting an untouched machine go quiet.

Workaround

sudo mv /etc/pam.d/omarchy-lock-fingerprint{,.disabled} — the fingerprintCheckProc in Service.qml tests for that file, so this cleanly sets fingerprintConfigured = false and stops the loop. Password unlock is unaffected, and fingerprint for sudo / polkit-1 keeps working since those PAM stacks reference pam_fprintd.so independently.

View raw text
## Summary

The Quickshell lock screen re-arms fingerprint PAM unconditionally and without bound for the entire duration of a lock. On a machine that stays locked for a long time, this produces a continuous ~30s authentication cycle that never stops. On my laptop it ran for **53 hours straight — 6,451 PAM cycles and 6,454 USB resets of the fingerprint sensor** — and the session ended in a hard kernel freeze whose last log line was one of those USB resets.

The unbounded retry is clearly visible in the source. The freeze is circumstantial and I am **not** claiming it as proven — but the retry loop itself looks like a bug worth bounding regardless.

## Environment

- Omarchy `4.0.2-1`
- Hyprland `0.56.2`, SDDM, kernel `7.1.9-arch1-2`
- Dell Inspiron 16 5635, AMD Ryzen 7 7730U w/ Radeon Graphics
- Fingerprint sensor: Goodix `27c6:639c` ("Goodix MOC Fingerprint Sensor (press)"), one enrolled finger
- `/etc/pam.d/omarchy-lock-fingerprint` present and correctly wired (`auth required pam_fprintd.so`) — this is **not** the Omarchy 3 / hyprlock "PAM not wired up" bug from #5503

## Steps to reproduce

1. Enroll a fingerprint (*Setup > Security > Fingerprint*).
2. Configure the machine to stay awake while unattended — in my case an always-on host with:
   ```
   # /etc/systemd/logind.conf.d/30-alwayson.conf
   HandleLidSwitch=lock
   IdleAction=ignore
   ```
3. Let the screen lock and leave it locked for hours (lid closed is enough).
4. Watch the journal.

## Observed behaviour

A cycle repeating every ~30.3s, indefinitely:

```
15:53:23 omarchy-shell[1390293]: quickshell.service.pam.subprocess: Starting pam session for user "..." with config "omarchy-lock-fingerprint"
15:53:23 kernel: usb 3-4: reset full-speed USB device number 3 using xhci_hcd
15:53:23 omarchy-shell[1390293]: Relaying pam message: "Place your right index finger on the fingerprint reader"
15:53:53 omarchy-shell[1390293]: Relaying pam message: "Verification timed out"
15:53:53 omarchy-shell[1390293]: Error while authenticating: "Authentication service cannot retrieve authentication info" (code 9)
15:53:53 omarchy-shell[1390488]: Starting pam session ... (next subprocess, new PID)
```

The 30s is fprintd's own verification timeout; the extra 250ms is `fingerprintRetryTimer`.

Totals for that single boot (Aug 31 07:34:52 → Sep 2 15:56:58, ~53h):

- `6451` × "Place your right index finger"
- `6454` × `usb 3-4: reset ... using xhci_hcd`

The loop began 15 minutes after the first idle-lock following the `4.0.1 → 4.0.2` upgrade and reboot.

## Root cause

`shell/plugins/lock/Service.qml` re-arms on **every** non-success result, and again on error:

```qml
function handleFingerprintFinished(result) {
  fingerprintAuthenticating = false
  if (!lockRequested) return
  if (result === PamResult.Success) {
    finishUnlock()
  } else if (fingerprintConfigured) {
    fingerprintRetryTimer.restart()      // no cap, no backoff
  }
}
```

```qml
onError: function(error) {
  root.fingerprintAuthenticating = false
  if (root.lockRequested && root.fingerprintConfigured) fingerprintRetryTimer.restart()
}
```

```qml
Timer { id: fingerprintRetryTimer; interval: 250; repeat: false; onTriggered: root.startFingerprint() }
```

The intent is documented at ~line 427 (*"The fingerprint PAM stays armed for the whole lock"*), so this is by design — but the design has no upper bound, no backoff, and no way to opt out short of deleting `/etc/pam.d/omarchy-lock-fingerprint`. `shell.json` exposes only `idle.screensaver` and `idle.lock`.

A timeout is treated identically to a genuine failed match. Nothing distinguishes "nobody is at the machine" from "someone is trying to unlock".

## Why it matters

Each cycle spawns a PAM subprocess and triggers a USB device reset. Over two days that is ~6.5k of each. Beyond the wear on the sensor and the journal noise, an unattended locked machine is exactly the state where nobody is watching for trouble.

**On the freeze**, stated carefully: the journal ends abruptly at `15:57:28` mid-boot with no shutdown, no panic, no OOM, no thermal or DRM errors, and the final line is the 6,454th `usb 3-4: reset ... xhci_hcd`. That is suggestive, not conclusive — nothing was written to disk. I mention it only as motivation for bounding the loop, not as a diagnosis.

## Suggested fix

Any of:

- Cap consecutive retries, and stop re-arming after N failures until user input (a keypress or pointer motion) is seen.
- Back off exponentially instead of a fixed 250ms.
- Distinguish `PamResult` timeout from a real match failure, and don't re-arm on timeout when the session has been idle.
- Expose a bound in `shell.json` (e.g. `lock.fingerprintRetries`).

The first seems closest to intent: a person actually standing at the machine generates input events, so gating re-arm on recent input would keep fingerprint unlock instant for real use while letting an untouched machine go quiet.

## Workaround

`sudo mv /etc/pam.d/omarchy-lock-fingerprint{,.disabled}` — the `fingerprintCheckProc` in `Service.qml` tests for that file, so this cleanly sets `fingerprintConfigured = false` and stops the loop. Password unlock is unaffected, and fingerprint for `sudo` / `polkit-1` keeps working since those PAM stacks reference `pam_fprintd.so` independently.

Validation

Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.

Linked

  • Fixed bypr/7179· confirmed
  • Related PRpr/10653· confirmed
  • Fixed bypr/12461· confirmed
  • Fixed bypr/12420· confirmed
  • Related PRpr/10393· confirmed
  • Fixed bypr/11273· confirmed
  • Fixed bypr/12690· confirmed

Decision

Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.

  • Decided by agents · verified: in the audited area (lock)

The outcome (valid, duplicate, not a bug, …) is not decided yet.