QProcess/Unix: open the working directory before changing the thread state

This operation can fail and, in handling the failure, we call functions
that may allocate memory and thus throw. If that happens, our destructor
won't be called, so this commit avoids leaving the thread in an
incorrect state by not changing it in the first place.

In case there was a failure but no exception happened (a much more
likely scenario), the destructor will be called by startProcess() or
startDetached(). Therefore, we need to ensure it won't cause problems.

Change-Id: I091193bb945a2637f463fffdd93694e555401452
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
(cherry picked from commit 34edfe134403e331f0fa4550a074507a393bdc52)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Thiago Macieira 2024-08-26 18:37:16 -07:00 committed by Qt Cherry-pick Bot
parent fee6283b85
commit 87fbcf438c

View File

@ -257,6 +257,20 @@ struct QChildProcess
: d(d), argv(resolveExecutable(d->program), d->arguments),
envp(d->environmentPrivate())
{
// Open the working directory first, because this operation can fail.
// That way, if it does, we don't have anything to clean up.
if (!d->workingDirectory.isEmpty()) {
workingDirectory = opendirfd(QFile::encodeName(d->workingDirectory));
if (workingDirectory < 0) {
d->setErrorAndEmit(QProcess::FailedToStart, "chdir: "_L1 + qt_error_string());
d->cleanup();
// make sure our destructor does nothing
isUsingVfork = false;
return;
}
}
// Block Unix signals, to ensure the user's handlers aren't run in the
// child side and do something weird, especially if the handler and the
// user of QProcess are completely different codebases.
@ -268,15 +282,6 @@ struct QChildProcess
// would be bad enough with regular fork(), but it's likely fatal with
// vfork().
disableThreadCancellations();
if (!d->workingDirectory.isEmpty()) {
workingDirectory = opendirfd(QFile::encodeName(d->workingDirectory));
if (workingDirectory < 0) {
d->setErrorAndEmit(QProcess::FailedToStart, "chdir: "_L1 + qt_error_string());
d->cleanup();
}
}
}
~QChildProcess() noexcept(false)
{
@ -335,7 +340,7 @@ private:
}
#if defined(PTHREAD_CANCEL_DISABLE)
int oldstate;
int oldstate = PTHREAD_CANCEL_DISABLE;
void disableThreadCancellations() noexcept
{
// the following is *not* noexcept, but it won't throw while disabling
@ -343,9 +348,11 @@ private:
}
void restoreThreadCancellations() noexcept(false)
{
if (oldstate != PTHREAD_CANCEL_DISABLE) {
// this doesn't touch errno
pthread_setcancelstate(oldstate, nullptr);
}
}
#else
void disableThreadCancellations() noexcept {}
void restoreThreadCancellations() {}