QProcess/Unix: update the close-file-descriptors feature with a minimum
So that one can pass a few extra file descriptors to the child while still closing all the rest. strace -f of this test showed on Linux: [pid 117952] dup3(4, 0, 0) = 0 [pid 117952] dup3(9, 1, 0) = 1 [pid 117952] dup3(11, 2, 0) = 2 [pid 117952] close(12) = 0 [pid 117952] dup2(100, 3) = 3 [pid 117952] close_range(4, 2147483647, 0) = 0 [pid 117952] execve("testUnixProcessParameters/testUnixProcessParameters", ["testUnixProcessParameters/testUn"..., "file-descriptors2", "3", "100"], 0x561793dc87d0 /* 120 vars */ <unfinished ...> Pick-to: 6.6 Change-Id: I3e3bfef633af4130a03afffd175e984bf50b558d Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
e71c226d6f
commit
abd2ffc149
@ -816,8 +816,16 @@ void QProcessPrivate::Channel::clear()
|
|||||||
Its members are:
|
Its members are:
|
||||||
\list
|
\list
|
||||||
\li UnixProcessParameters::flags Flags, see QProcess::UnixProcessFlags
|
\li UnixProcessParameters::flags Flags, see QProcess::UnixProcessFlags
|
||||||
|
\li UnixProcessParameters::lowestFileDescriptorToClose The lowest file
|
||||||
|
descriptor to close.
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
|
When the QProcess::UnixProcessFlags::CloseFileDescriptors flag is set in
|
||||||
|
the \c flags field, QProcess closes the application's open file descriptors
|
||||||
|
before executing the child process. The descriptors 0, 1, and 2 (that is,
|
||||||
|
\c stdin, \c stdout, and \c stderr) are left alone, along with the ones
|
||||||
|
numbered lower than the value of the \c lowestFileDescriptorToClose field.
|
||||||
|
|
||||||
All of the settings above can also be manually achieved by calling the
|
All of the settings above can also be manually achieved by calling the
|
||||||
respective POSIX function from a handler set with
|
respective POSIX function from a handler set with
|
||||||
QProcess::setChildProcessModifier(). This structure allows QProcess to deal
|
QProcess::setChildProcessModifier(). This structure allows QProcess to deal
|
||||||
@ -835,10 +843,11 @@ void QProcessPrivate::Channel::clear()
|
|||||||
|
|
||||||
These flags can be used in the \c flags field of \l UnixProcessParameters.
|
These flags can be used in the \c flags field of \l UnixProcessParameters.
|
||||||
|
|
||||||
\value CloseNonStandardFileDescriptors Close all file descriptors besides
|
\value CloseFileDescriptors Close all file descriptors above the threshold
|
||||||
\c stdin, \c stdout, and \c stderr, preventing any currently open
|
defined by \c lowestFileDescriptorToClose, preventing any currently
|
||||||
descriptor in the parent process from accidentally leaking to the
|
open descriptor in the parent process from accidentally leaking to the
|
||||||
child.
|
child. The \c stdin, \c stdout, and \c stderr file descriptors are
|
||||||
|
never closed.
|
||||||
|
|
||||||
\value IgnoreSigPipe Always sets the \c SIGPIPE signal to ignored
|
\value IgnoreSigPipe Always sets the \c SIGPIPE signal to ignored
|
||||||
(\c SIG_IGN), even if the \c ResetSignalHandlers flag was set. By
|
(\c SIG_IGN), even if the \c ResetSignalHandlers flag was set. By
|
||||||
|
@ -179,15 +179,16 @@ public:
|
|||||||
ResetSignalHandlers = 0x0001, // like POSIX_SPAWN_SETSIGDEF
|
ResetSignalHandlers = 0x0001, // like POSIX_SPAWN_SETSIGDEF
|
||||||
IgnoreSigPipe = 0x0002,
|
IgnoreSigPipe = 0x0002,
|
||||||
// some room if we want to add IgnoreSigHup or so
|
// some room if we want to add IgnoreSigHup or so
|
||||||
CloseNonStandardFileDescriptors = 0x0010,
|
CloseFileDescriptors = 0x0010,
|
||||||
UseVFork = 0x0020, // like POSIX_SPAWN_USEVFORK
|
UseVFork = 0x0020, // like POSIX_SPAWN_USEVFORK
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(UnixProcessFlags, UnixProcessFlag)
|
Q_DECLARE_FLAGS(UnixProcessFlags, UnixProcessFlag)
|
||||||
struct UnixProcessParameters
|
struct UnixProcessParameters
|
||||||
{
|
{
|
||||||
UnixProcessFlags flags = {};
|
UnixProcessFlags flags = {};
|
||||||
|
int lowestFileDescriptorToClose = 0;
|
||||||
|
|
||||||
quint32 _reserved[7] {};
|
quint32 _reserved[6] {};
|
||||||
};
|
};
|
||||||
UnixProcessParameters unixProcessParameters() const noexcept;
|
UnixProcessParameters unixProcessParameters() const noexcept;
|
||||||
void setUnixProcessParameters(const UnixProcessParameters ¶ms);
|
void setUnixProcessParameters(const UnixProcessParameters ¶ms);
|
||||||
|
@ -669,9 +669,9 @@ static void applyProcessParameters(const QProcess::UnixProcessParameters ¶ms
|
|||||||
|
|
||||||
// Close all file descriptors above stderr.
|
// Close all file descriptors above stderr.
|
||||||
// This isn't expected to fail, so we ignore close()'s return value.
|
// This isn't expected to fail, so we ignore close()'s return value.
|
||||||
if (params.flags.testFlag(QProcess::UnixProcessFlag::CloseNonStandardFileDescriptors)) {
|
if (params.flags.testFlag(QProcess::UnixProcessFlag::CloseFileDescriptors)) {
|
||||||
int r = -1;
|
int r = -1;
|
||||||
int fd = STDERR_FILENO + 1;
|
int fd = qMax(STDERR_FILENO + 1, params.lowestFileDescriptorToClose);
|
||||||
#if QT_CONFIG(close_range)
|
#if QT_CONFIG(close_range)
|
||||||
// On FreeBSD, this probably won't fail.
|
// On FreeBSD, this probably won't fail.
|
||||||
// On Linux, this will fail with ENOSYS before kernel 5.9.
|
// On Linux, this will fail with ENOSYS before kernel 5.9.
|
||||||
|
@ -62,7 +62,7 @@ int main(int argc, char **argv)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == "std-file-descriptors") {
|
if (cmd == "file-descriptors") {
|
||||||
int fd = atoi(argv[2]);
|
int fd = atoi(argv[2]);
|
||||||
if (close(fd) < 0 && errno == EBADF)
|
if (close(fd) < 0 && errno == EBADF)
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@ -70,6 +70,16 @@ int main(int argc, char **argv)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmd == "file-descriptors2") {
|
||||||
|
int fd1 = atoi(argv[2]); // should be open
|
||||||
|
int fd2 = atoi(argv[3]); // should be closed
|
||||||
|
if (close(fd1) < 0)
|
||||||
|
fprintf(stderr, "%d was not a valid file descriptor\n", fd1);
|
||||||
|
if (close(fd2) == 0 || errno != EBADF)
|
||||||
|
fprintf(stderr, "%d is a valid file descriptor\n", fd2);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Unknown command \"%s\"", cmd.data());
|
fprintf(stderr, "Unknown command \"%s\"", cmd.data());
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,7 @@ private slots:
|
|||||||
void unixProcessParameters_data();
|
void unixProcessParameters_data();
|
||||||
void unixProcessParameters();
|
void unixProcessParameters();
|
||||||
void unixProcessParametersAndChildModifier();
|
void unixProcessParametersAndChildModifier();
|
||||||
|
void unixProcessParametersOtherFileDescriptors();
|
||||||
#endif
|
#endif
|
||||||
void exitCodeTest();
|
void exitCodeTest();
|
||||||
void systemEnvironment();
|
void systemEnvironment();
|
||||||
@ -1537,7 +1538,7 @@ void tst_QProcess::unixProcessParameters_data()
|
|||||||
using P = QProcess::UnixProcessFlag;
|
using P = QProcess::UnixProcessFlag;
|
||||||
addRow("reset-sighand", P::ResetSignalHandlers);
|
addRow("reset-sighand", P::ResetSignalHandlers);
|
||||||
addRow("ignore-sigpipe", P::IgnoreSigPipe);
|
addRow("ignore-sigpipe", P::IgnoreSigPipe);
|
||||||
addRow("std-file-descriptors", P::CloseNonStandardFileDescriptors);
|
addRow("file-descriptors", P::CloseFileDescriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QProcess::unixProcessParameters()
|
void tst_QProcess::unixProcessParameters()
|
||||||
@ -1618,11 +1619,11 @@ void tst_QProcess::unixProcessParametersAndChildModifier()
|
|||||||
write(pipes[1], message, strlen(message));
|
write(pipes[1], message, strlen(message));
|
||||||
vforkControl.storeRelaxed(1);
|
vforkControl.storeRelaxed(1);
|
||||||
});
|
});
|
||||||
auto flags = QProcess::UnixProcessFlag::CloseNonStandardFileDescriptors |
|
auto flags = QProcess::UnixProcessFlag::CloseFileDescriptors |
|
||||||
QProcess::UnixProcessFlag::UseVFork;
|
QProcess::UnixProcessFlag::UseVFork;
|
||||||
process.setUnixProcessParameters({ flags });
|
process.setUnixProcessParameters({ flags });
|
||||||
process.setProgram("testUnixProcessParameters/testUnixProcessParameters");
|
process.setProgram("testUnixProcessParameters/testUnixProcessParameters");
|
||||||
process.setArguments({ "std-file-descriptors", QString::number(pipes[1]) });
|
process.setArguments({ "file-descriptors", QString::number(pipes[1]) });
|
||||||
process.start();
|
process.start();
|
||||||
QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString()));
|
QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString()));
|
||||||
} // closes the writing end of the pipe
|
} // closes the writing end of the pipe
|
||||||
@ -1639,6 +1640,50 @@ void tst_QProcess::unixProcessParametersAndChildModifier()
|
|||||||
if (haveWorkingVFork)
|
if (haveWorkingVFork)
|
||||||
QVERIFY2(vforkControl.loadRelaxed(), "QProcess doesn't appear to have used vfork()");
|
QVERIFY2(vforkControl.loadRelaxed(), "QProcess doesn't appear to have used vfork()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QProcess::unixProcessParametersOtherFileDescriptors()
|
||||||
|
{
|
||||||
|
constexpr int TargetFileDescriptor = 3;
|
||||||
|
int pipes[2];
|
||||||
|
int fd1 = open("/dev/null", O_RDONLY);
|
||||||
|
int devnull = fcntl(fd1, F_DUPFD, 100); // instead of F_DUPFD_CLOEXEC
|
||||||
|
pipe2(pipes, O_CLOEXEC);
|
||||||
|
close(fd1);
|
||||||
|
|
||||||
|
auto closeFds = qScopeGuard([&] {
|
||||||
|
close(devnull);
|
||||||
|
close(pipes[0]);
|
||||||
|
// we'll close pipe[1] before any QCOMPARE
|
||||||
|
});
|
||||||
|
|
||||||
|
QProcess process;
|
||||||
|
QProcess::UnixProcessParameters params;
|
||||||
|
params.flags = QProcess::UnixProcessFlag::CloseFileDescriptors
|
||||||
|
| QProcess::UnixProcessFlag::UseVFork;
|
||||||
|
params.lowestFileDescriptorToClose = 4;
|
||||||
|
process.setUnixProcessParameters(params);
|
||||||
|
process.setChildProcessModifier([devnull, pipes]() {
|
||||||
|
if (dup2(devnull, TargetFileDescriptor) == TargetFileDescriptor)
|
||||||
|
return;
|
||||||
|
write(pipes[1], &errno, sizeof(errno));
|
||||||
|
_exit(255);
|
||||||
|
});
|
||||||
|
process.setProgram("testUnixProcessParameters/testUnixProcessParameters");
|
||||||
|
process.setArguments({ "file-descriptors2", QString::number(TargetFileDescriptor),
|
||||||
|
QString::number(devnull) });
|
||||||
|
process.start();
|
||||||
|
close(pipes[1]);
|
||||||
|
|
||||||
|
if (int duperror; read(pipes[0], &duperror, sizeof(duperror)) == sizeof(duperror))
|
||||||
|
QFAIL(QByteArray("dup2 failed: ") + strerror(duperror));
|
||||||
|
|
||||||
|
QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString()));
|
||||||
|
QVERIFY(process.waitForFinished(5000));
|
||||||
|
QCOMPARE(process.readAllStandardError(), QString());
|
||||||
|
QCOMPARE(process.readAll(), QString());
|
||||||
|
QCOMPARE(process.exitCode(), 0);
|
||||||
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void tst_QProcess::exitCodeTest()
|
void tst_QProcess::exitCodeTest()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user