pam/go-exec: run PAM action in calling thread, not a new GLib thread - #1726
pam/go-exec: run PAM action in calling thread, not a new GLib thread#1726adombeck wants to merge 1 commit into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 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. 🚀 New features to boost your workflow:
|
| * 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); |
| * 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. |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>
977bd59 to
13dac52
Compare
kscreenlocker's PAM worker is a Qt thread (
QThread). Its conversation callback (converse()) creates a nestedQEventLoopto wait for the user's password via aQMetaObject::invokeMethod(..., Qt::QueuedConnection)posted byPamAuthenticator::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:authd-paminvoked the D-BusPromptmethod from the GLib action thread.pam_prompt()→converse()→QEventLoop::exec().respond()postedpromptResponseReceivedviaQMetaObject::invokeMethod(d, ..., Qt::QueuedConnection)targeting the Qt PAM worker thread (d's thread).g_thread_join()and could never drain its event queue, sopromptResponseReceivedwas 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 theg_thread_new/g_thread_joinindirection.The isolated
GMainContextcreated bydo_pam_action_thread()(viag_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 nestedQEventLoop::exec()processes Qt events for that thread, andrespond()→promptResponseReceived→QEventLoop::exit()works as intended.Closes #1725
UDENG-10997