Skip to content

fingerprint: reset device proxy on terminate - #1068

Open
juriSacchetta wants to merge 1 commit into
hyprwm:mainfrom
juriSacchetta:fix/fingerprint-proxy-outlives-dbus-connection
Open

juriSacchetta wants to merge 1 commit into
hyprwm:mainfrom
juriSacchetta:fix/fingerprint-proxy-outlives-dbus-connection

Conversation

@juriSacchetta

Copy link
Copy Markdown

Fixes #1067.

The bug

CFingerprint::terminate() only released the fprintd device when the verification had not been aborted:

void CFingerprint::terminate() {
    if (!m_sDBUSState.abort)
        releaseDevice();   // this is what resets m_sDBUSState.device
}

m_sDBUSState.abort is set in handleVerifyStatus() on MATCH_DISCONNECTED, i.e. whenever fprintd reports verify-disconnected. On that path the device proxy is kept alive.

The proxy is created with the reference overload:

m_sDBUSState.device = sdbus::createProxy(*g_dbus->m_connection, FPRINT, path);

which borrows the connection instead of sharing ownership of it, so the proxy must not outlive it. But run() destroys the connection at g_dbus.reset() while g_pAuth is left to static destruction, so the ordering at exit is:

  1. g_pAuth->terminate() — skips releaseDevice() because of abort, proxy survives
  2. g_dbus.reset() — CDbus and m_connection destroyed
  3. main returns, exit() destroys g_pAuth → ~CFingerprint → ~Proxy

~Proxy unrefs its signal slots through a lambda capturing the now-dangling Connection, and hyprlock segfaults in Connection.cpp:640. Backtrace and logs are in #1067.

The session is already unlocked at that point, so the user-visible damage is limited, but hyprlock terminates on a signal instead of returning 0.

Regression

Yes, and I got this wrong in the issue — I have corrected it there.

Before 8bac7da (#1029) SDBUSState looked like this:

struct SDBUSState {
    std::shared_ptr<sdbus::IConnection> connection;
    std::unique_ptr<sdbus::IProxy>      login;
    std::unique_ptr<sdbus::IProxy>      device;
    ...
};

Members are destroyed in reverse declaration order, so device was always destroyed before connection and the early return in terminate() was harmless. Moving the connection out to g_dbus removed that guarantee without changing terminate(). v0.9.6 is the first tag containing the commit.

The fix

Reset the proxy unconditionally, so it cannot outlive the connection regardless of which branch was taken. terminate() runs before g_dbus.reset() in run(), so this is early enough.

Note for maintainers

This makes the reported crash go away, but the underlying fragility is that g_pAuth is the only global in that teardown block left to static destruction — everything around it (g_pSeatManager, g_asyncResourceManager, g_pRenderer, g_pEGL) is reset explicitly before g_dbus. Adding g_pAuth.reset(); before g_dbus.reset(); would make the ordering explicit and cover any future proxy held by an auth implementation. I left it out to keep this diff to the actual bug — happy to add it if you would prefer that instead of, or on top of, this change.

Testing

  • Builds clean against sdbus-cpp 2.3.1, hyprutils 0.14.1, hyprlang 0.6.8, gcc 16.2.1.
  • clang-format --dry-run --Werror clean.
  • Not verified at runtime against the crash. I have a single core dump and no confirmed repro recipe yet — the fix follows from the destruction order in the backtrace rather than from an observed before/after. Flagging that explicitly so it gets the review it deserves.

terminate() only released the device when the verification had not been
aborted. On abort the proxy stayed alive, and since it borrows
g_dbus->m_connection rather than sharing ownership of it, it outlived the
connection that run() destroys a few lines later. The proxy was then
destroyed during static destruction of g_pAuth, unreffing its signal slots
through a dangling Connection and segfaulting in libsdbus-c++.

abort is set when fprintd reports verify-disconnected, so any sensor
disconnect while locked turned a successful unlock into a SIGSEGV at exit.

Before 8bac7da the connection was a member of SDBUSState declared ahead of
the proxies, so reverse member destruction kept the ordering safe and the
early return was harmless. Moving the connection to g_dbus removed that
guarantee.
Copilot AI lite review requested due to automatic review settings September 1, 2026 20:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a shutdown-time use-after-free in the fingerprint auth implementation by ensuring the fprintd device proxy cannot outlive the D-Bus connection it borrows, addressing the segfault described in #1067.

Changes:

  • Update CFingerprint::terminate() to always reset m_sDBUSState.device, even when the session was aborted (e.g., verify-disconnected).
  • Add an inline comment documenting the proxy lifetime constraint (borrowed connection).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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.

SIGSEGV at exit: fingerprint device proxy outlives the D-Bus connection when VerifyStatus reports verify-disconnected

2 participants