Skip to content

pam/go-exec: run PAM action in calling thread, not a new GLib thread - #1726

Open
adombeck wants to merge 1 commit into
mainfrom
1725-kde-lockscreen-silently-hangs
Open

pam/go-exec: run PAM action in calling thread, not a new GLib thread#1726
adombeck wants to merge 1 commit into
mainfrom
1725-kde-lockscreen-silently-hangs

Conversation

@adombeck

@adombeck adombeck commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

kscreenlocker's PAM worker is a Qt thread (QThread). Its conversation callback (converse()) creates a nested QEventLoop to wait for the user's password via a QMetaObject::invokeMethod(..., Qt::QueuedConnection) posted by PamAuthenticator::respond().

The previous design spawned a dedicated GLib thread for every PAM action and blocked the calling thread in g_thread_join(). This caused a deadlock:

  1. authd-pam invoked the D-Bus Prompt method from the GLib action thread.
  2. The GLib thread called pam_prompt()converse()QEventLoop::exec().
  3. When the user submitted a password, respond() posted promptResponseReceived via QMetaObject::invokeMethod(d, ..., Qt::QueuedConnection) targeting the Qt PAM worker thread (d's thread).
  4. But the Qt worker thread was permanently blocked in g_thread_join() and could never drain its event queue, so promptResponseReceived was never emitted, QEventLoop::exec() waited forever, and the lockscreen appeared to ignore the submitted password entirely.

Fix

Call do_pam_action_thread() directly in the calling thread, removing the g_thread_new/g_thread_join indirection.

The isolated GMainContext created by do_pam_action_thread() (via g_main_context_pusher_new) is unrelated to the calling application's own GLib or Qt event loop, so there is no interference. With the GLib main loop running in the Qt PAM worker thread, pam_prompt() is also called there, the nested QEventLoop::exec() processes Qt events for that thread, and respond()promptResponseReceivedQEventLoop::exit() works as intended.

Closes #1725
UDENG-10997

@adombeck adombeck added the e2e-tests This issue is related to end-to-end tests / Run end-to-end tests on this pull request label Jul 10, 2026
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.00%. Comparing base (a0262c9) to head (977bd59).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1726   +/-   ##
=======================================
  Coverage   88.00%   88.00%           
=======================================
  Files          96       96           
  Lines        7009     7003    -6     
  Branches      112      112           
=======================================
- Hits         6168     6163    -5     
+ Misses        785      784    -1     
  Partials       56       56           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@adombeck
adombeck marked this pull request as ready for review July 14, 2026 07:13
Comment thread pam/go-exec/module.c Outdated
* disturbed. The GLib main loop run inside do_pam_action_thread() only
* iterates sources attached to that isolated context.
*/
return do_pam_action_thread (pamh, action, flags, argc, argv);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This may be renamed now.

Comment thread pam/go-exec/module.c Outdated
Comment on lines +1261 to +1267
* Running in the calling thread is safe because do_pam_action_thread() uses
* its own isolated GMainContext (not the thread-default or the default
* context). It temporarily installs that context as the thread-default via
* g_main_context_pusher_new() and restores the previous value on return, so
* sources belonging to the calling application's GLib main loop are not
* disturbed. The GLib main loop run inside do_pam_action_thread() only
* iterates sources attached to that isolated context.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is only partially true.

By pushing a new thread-default thread, then any new source that may be created in a calling application will be use that new context as their thread so not something we would like to do in general as it was explained in 13d47e2.

IMHO the way would be instead ensuring that m_prompt (pamh, style, &response, "%s", prompt) is called in an idle in the main loop (e.g the actual main thread).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copilot came up with another solution. I'm waiting for e2e-test results before requesting another review, but you can already take a look if you want.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nah, don't think that's the way either since it assumes that there's a context.

I'd rather go with #1734

kscreenlocker handles PAM conversations on its Qt worker thread. Running
the PAM action there lets the queued password response be processed, but
the action also needs an isolated GLib context for its private D-Bus
server.

Restore the caller thread-default GLib context only while invoking the
PAM conversation callback. This preserves the caller's callback
semantics without binding unrelated application sources to authd's
private D-Bus context.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e-tests This issue is related to end-to-end tests / Run end-to-end tests on this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE lockscreen silently hangs after authd password prompt

4 participants