-
Notifications
You must be signed in to change notification settings - Fork 359
fix: restart greeter when helper is in a wrong state #2103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
Signed-off-by: tsrk. <tsrk@tsrk.me>
|
There's actually one more case of this: sddm/src/helper/UserSession.cpp Line 50 in 08a4a77
That connects |
|
Should be good now sddm/src/helper/UserSession.cpp Line 159 in 3fd079f
For this line, I think this is fine, as we explicitly specify an enum, and diff --git a/src/helper/UserSession.cpp b/src/helper/UserSession.cpp
index 5fd378a..eb6b598 100644
--- a/src/helper/UserSession.cpp
+++ b/src/helper/UserSession.cpp
@@ -156,7 +156,7 @@ namespace SDDM {
}
}
} else {
- Q_EMIT finished(Auth::HELPER_OTHER_ERROR, QProcess::NormalExit);
+ exit(Auth::HELPER_OTHER_ERROR);
}
}
|
Vogtinator
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM otherwise. FWICT this is effectively a noop in the "happy path", so not that likely to cause regressions.
Signed-off-by: tsrk. <tsrk@tsrk.me>
A QProcess::CrashExit happen in most cases when a signal killed it. And returning from a signal-killed process on Linux makes QProcess:finished return the signal, which is a really bad idea to cast into a random enum. Qt also mentions that `exitCode` is only valid for QProcess::NormalExit invocations Signed-off-by: tsrk. <tsrk@tsrk.me>
This will allow HelperApp to dinstinguish between a crash (most likely by a signal) from a normal exit. Signed-off-by: tsrk. <tsrk@tsrk.me>
… exit Signed-off-by: tsrk. <tsrk@tsrk.me>
fb774db to
7052ba8
Compare
|
Actually, hold off merging this one. I might've actually introduced new issues where SDDM would restart for seemingly no reason (opposite problem). I'm poking around to see what the root cause might be. |
This comment was marked as off-topic.
This comment was marked as off-topic.
49b5912 to
7522be5
Compare
If you have a concrete case where this can fail, please report this to Qt upstream. |
src/helper/HelperApp.cpp
Outdated
| void HelperApp::sessionFinished(int status) { | ||
| exit(status); | ||
| void HelperApp::sessionFinished(int exitCode, QProcess::ExitStatus exitStatus) { | ||
| disconnect(m_session, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &HelperApp::sessionFinished); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can never be reached unless something went really wrong: All paths in this method lead to exit, i.e. it will never return.
If exit somehow spawns an event loop again (exit handlers?), that's broken and will cause many "fun" issues due to accidental reentrancy.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reasoning is kinda stupid imho…
The reasoning is:
The fcntl can only fail if the file descriptor is invalid. That means QProcess passed a bad file descriptor, so the bug would be in QProcess.
i.e. if this actually happens, it's a bug in QProcess and you can report it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we might as well take precautions to prevent such cases.
IMO the root cause needs to be found and it's not in QProcessPrivate::waitForDeadChild.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, I can't reproduce the "crash with 0" bug anymore, but I'm still not confident since I still get double sessionFinished calls despite exits…
You can try to trace why each of those calls happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I just realized the exit we've been calling, is not the exit(2) syscall, but QCoreApplication::exit which does in fact returns, and I was able to reproduce the "crash with 0" bug. I concluded this had nothing to do with QProcess.
Sorry for this nothingburger, here is what I understood what happened:
- When
UserSessionQProcessed::finished, we catch it inHelperApp::sessionFinished - If it's a
QProcess::NormalExit, callQCoreApplication::exit:- This only signals the event loop that we requested an exit, and the return code we exit with
- It actually returns in order to process more events
- This means we accidentally fall through our second case of
QProcess::CrashExit, believing we stumbled into one:- By calling this
exit, this sets the event loop return code to 3
- By calling this
- Our
HelperAppthen process all the remaining events - Finally, the event loop returns to the main thread
- When
main()reaches out of scope, it calls our destructor~HelperApp() - Which in turns calls
UserSession::stop(). - However, the
UserSessionis alreadyQProcess::NotRunningby this stage, so it emits anotherQProcess::finished(Auth::HELPER_SESSION_ERROR, QProcess::NormalExit).HelperApp::sessionFinshedgets called again…
Sequence diagram
sequenceDiagram
participant main as ::main
participant HelperApp as SDDM::HelperApp
participant Backend as SDDM::Backend
participant UserSession as SDDM::UserSession
main->>HelperApp: exec()
Note over HelperApp: setUp() and doAuth()
HelperApp->>Backend: openSession()
Backend->>UserSession: start()
Note over UserSession: Session goes on…
UserSession->>HelperApp: QProcess::finished()
Note over HelperApp: sessionFinished()
opt QProcess::NormalExit
alt exitCode not 0
HelperApp->>HelperApp: exit(Auth::HELPER_SESSION_ERROR)
else
HelperApp->>HelperApp: exit(Auth::HELPER_SUCCES)
end
end
Note right of HelperApp: Those RETURN and fall through (not exit syscall)
HelperApp->>HelperApp: exit(Auth::HELPER_SESSION_ERROR)
HelperApp-->HelperApp: Finish processing events
HelperApp-->>main: Event loop returns with 3
main->>main: Return 3
Note over main: At SCOPE FINISH
main->>HelperApp: ~HelperApp() (call destructor)
HelperApp->>UserSession: stop()
Note over UserSession: state() is QProcess::NotRunning
UserSession->>HelperApp: QProcess::finished(3, 0)
Note over HelperApp: Repeat sessionFinished() a second time
Should be pretty simple to fix:
- We should either explicitly
returninHelperApp::sessionFinished, or wrap the second case in anelsebranch - In the destructor, call
m_session->stop()only ifm_session->state() != QProcess::NotRunning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I just realized the exit we've been calling, is not the exit(2) syscall, but QCoreApplication::exit which does in fact return
In that case it should probably be made a bit more explicit by calling QCoreApplication::exit() instead...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, the UserSession is already QProcess::NotRunning by this stage, so it emits another QProcess::finished(Auth::HELPER_SESSION_ERROR, QProcess::NormalExit).
That always seemed odd to me, maybe we can just remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case it should probably be made a bit more explicit by calling
QCoreApplication::exit()instead...
Done
That always seemed odd to me, maybe we can just remove it?
Replaced by a qInfo() log
…ctor This avoids calling exit an unnecessary number of times, which can potentially falsify the real return code, and cause restrat loops Signed-off-by: tsrk. <tsrk@tsrk.me>
7522be5 to
f886081
Compare
Signed-off-by: tsrk. <tsrk@tsrk.me>
Signed-off-by: tsrk. <tsrk@tsrk.me>
Calling `UserSession::stop` results in a call to `QProcess::terminate`, which sends a SIGTERM to the session. `QProcess` will catch that and emit an excess `QProcess::finished(15, 1)` which we don't want to handle since we are in the process of exiting. Signed-off-by: tsrk. <tsrk@tsrk.me>
This PR fixes a logic bug that prevents SDDM from restarting the greeter when either the session exits with code 1, or the SDDM helper gets signal-killed.
Helper side
If the helper gets killed by a signal (currently handled are
SIGTERMandSIGHUP), exit withAuth::HELPER_OTHER_ERROR. This will prevent the helper from exiting with codeAuth::HELPER_AUTH_ERROR, which is wrong.If the session exits with a bad code (!= 0), exit with
Auth::HELPER_SESSION_ERROR. This will prevent the helper from exitingAuth::HELPER_AUTH_ERRORif the session deliberately ends with code 1.Daemon side
DO NOT cast the
exitCodepassed byQProcess::finishedas aAuth::HelperStatusif theexitStatusis notQProcess::NormalExit. On LinuxQProcess::finishedis like a wrapper aroundwaitid(2)which will put the signal number inexitCodeif it got killed by one.The Qt Docs even advises against doing it:
For issues like #2003, this will result in a "restart loop" (which is essentially the same original symptom if the greeter fails to start in the first place).
Yes for the other session related issues, these are not SDDM related issues per se, but I think it's better UX to restart the greeter rather than leaving the user on a blank tty if a session fails.