Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/daemon/Auth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
#include "SignalHandler.h"
#include "VirtualTerminal.h"

#include <pwd.h>

Check warning on line 14 in src/daemon/Auth.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <pwd.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <security/pam_appl.h>

Check warning on line 15 in src/daemon/Auth.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <security/pam_appl.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <pthread.h>

Check warning on line 16 in src/daemon/Auth.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <pthread.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <signal.h>

Check warning on line 17 in src/daemon/Auth.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <signal.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <unistd.h>

Check warning on line 18 in src/daemon/Auth.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <unistd.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <utmp.h>

Check warning on line 19 in src/daemon/Auth.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <utmp.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <utmpx.h>

namespace DDM {
Expand Down Expand Up @@ -266,6 +267,19 @@
env.insert(QStringLiteral("LOGNAME"), QString::fromLocal8Bit(pw->pw_name));
}

// PAM modules (e.g. pam_gnome_keyring) may fork() internally and
// call exit() instead of _exit() in the fork child, which triggers
// Qt's atexit cleanup (including libQt6DBus joining its dispatcher
// thread). After fork(), that thread doesn't exist, so the join
// blocks forever on a futex/semaphore.
//
// Register a pthread_atfork child handler here so that any
// grandchild processes spawned by PAM will have an atexit handler
Comment on lines +276 to +277
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question (bug_risk): Scope and lifetime of the atfork-based workaround may be broader than intended.

Since pthread_atfork is process-wide, this handler will run on every future fork(), not just those triggered indirectly by PAM, and will change termination behavior for all child processes by installing the _exit(0) atexit hook. If that broader effect isn’t intended, consider scoping the workaround more narrowly (e.g., via a helper process or only around the specific PAM operations known to fork) so other fork-using paths aren’t unintentionally altered.

// that runs _exit() first (LIFO), bypassing Qt's broken cleanup.
pthread_atfork(nullptr, nullptr, []() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Consider handling the pthread_atfork return code and avoiding repeated registration.

pthread_atfork can fail (e.g., ENOMEM); right now that failure is silently ignored, so the workaround may never be installed and the caller can’t detect it. In addition, if this path is executed multiple times, each call adds another atfork handler that then adds another atexit handler in children, causing unbounded growth of handler lists. Please check and handle the pthread_atfork return value, and ensure registration happens only once per process (e.g., via std::once_flag).

Suggested implementation:

            // Register a pthread_atfork child handler here so that any
            // grandchild processes spawned by PAM will have an atexit handler
            // that runs _exit() first (LIFO), bypassing Qt's broken cleanup.
            //
            // Use std::call_once to avoid registering multiple handlers in the
            // same process, and check the pthread_atfork return value.
            static std::once_flag pamAtforkOnce;
            std::call_once(pamAtforkOnce, [] {
                const int rc = pthread_atfork(
                    nullptr,
                    nullptr,
                    []() {
                        atexit([]() { _exit(0); });
                    }
                );
                if (rc != 0) {
                    // Best-effort logging; failure means the workaround is not installed.
                    qWarning() << "Failed to register pthread_atfork handler for PAM:"
                               << strerror(rc);
                }
            });

            // Open session
            auto sessionEnv = openSessionInternal(env);

To compile successfully, you will also need to:

  1. Ensure the appropriate headers are included near the top of src/daemon/Auth.cpp (if they are not already):
    • #include <mutex> for std::once_flag and std::call_once.
    • #include <cstring> for strerror.
  2. Ensure that qWarning() is available in this translation unit (it usually is via common Qt headers such as <QDebug> or similar). If not already present, add the appropriate Qt debug/logging include used elsewhere in this project.
  3. If your project uses a different logging mechanism than qWarning(), replace the logging line inside the if (rc != 0) block with the project's standard error-logging macro or function.

atexit([]() { _exit(0); });
});
Comment on lines +279 to +281

// Open session
auto sessionEnv = openSessionInternal(env);
if (!sessionEnv.has_value()) {
Expand Down
Loading