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:
|
||||
\list
|
||||
\li UnixProcessParameters::flags Flags, see QProcess::UnixProcessFlags
|
||||
\li UnixProcessParameters::lowestFileDescriptorToClose The lowest file
|
||||
descriptor to close.
|
||||
\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
|
||||
respective POSIX function from a handler set with
|
||||
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.
|
||||
|
||||
\value CloseNonStandardFileDescriptors Close all file descriptors besides
|
||||
\c stdin, \c stdout, and \c stderr, preventing any currently open
|
||||
descriptor in the parent process from accidentally leaking to the
|
||||
child.
|
||||
\value CloseFileDescriptors Close all file descriptors above the threshold
|
||||
defined by \c lowestFileDescriptorToClose, preventing any currently
|
||||
open descriptor in the parent process from accidentally leaking to the
|
||||
child. The \c stdin, \c stdout, and \c stderr file descriptors are
|
||||
never closed.
|
||||
|
||||
\value IgnoreSigPipe Always sets the \c SIGPIPE signal to ignored
|
||||
(\c SIG_IGN), even if the \c ResetSignalHandlers flag was set. By
|
||||
|
@ -179,15 +179,16 @@ public:
|
||||
ResetSignalHandlers = 0x0001, // like POSIX_SPAWN_SETSIGDEF
|
||||
IgnoreSigPipe = 0x0002,
|
||||
// some room if we want to add IgnoreSigHup or so
|
||||
CloseNonStandardFileDescriptors = 0x0010,
|
||||
CloseFileDescriptors = 0x0010,
|
||||
UseVFork = 0x0020, // like POSIX_SPAWN_USEVFORK
|
||||
};
|
||||
Q_DECLARE_FLAGS(UnixProcessFlags, UnixProcessFlag)
|
||||
struct UnixProcessParameters
|
||||
{
|
||||
UnixProcessFlags flags = {};
|
||||
int lowestFileDescriptorToClose = 0;
|
||||
|
||||
quint32 _reserved[7] {};
|
||||
quint32 _reserved[6] {};
|
||||
};
|
||||
UnixProcessParameters unixProcessParameters() const noexcept;
|
||||
void setUnixProcessParameters(const UnixProcessParameters ¶ms);
|
||||
|
@ -669,9 +669,9 @@ static void applyProcessParameters(const QProcess::UnixProcessParameters ¶ms
|
||||
|
||||
// Close all file descriptors above stderr.
|
||||
// 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 fd = STDERR_FILENO + 1;
|
||||
int fd = qMax(STDERR_FILENO + 1, params.lowestFileDescriptorToClose);
|
||||
#if QT_CONFIG(close_range)
|
||||
// On FreeBSD, this probably won't fail.
|
||||
// On Linux, this will fail with ENOSYS before kernel 5.9.
|
||||
|
@ -62,7 +62,7 @@ int main(int argc, char **argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (cmd == "std-file-descriptors") {
|
||||
if (cmd == "file-descriptors") {
|
||||
int fd = atoi(argv[2]);
|
||||
if (close(fd) < 0 && errno == EBADF)
|
||||
return EXIT_SUCCESS;
|
||||
@ -70,6 +70,16 @@ int main(int argc, char **argv)
|
||||
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());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
@ -118,6 +118,7 @@ private slots:
|
||||
void unixProcessParameters_data();
|
||||
void unixProcessParameters();
|
||||
void unixProcessParametersAndChildModifier();
|
||||
void unixProcessParametersOtherFileDescriptors();
|
||||
#endif
|
||||
void exitCodeTest();
|
||||
void systemEnvironment();
|
||||
@ -1537,7 +1538,7 @@ void tst_QProcess::unixProcessParameters_data()
|
||||
using P = QProcess::UnixProcessFlag;
|
||||
addRow("reset-sighand", P::ResetSignalHandlers);
|
||||
addRow("ignore-sigpipe", P::IgnoreSigPipe);
|
||||
addRow("std-file-descriptors", P::CloseNonStandardFileDescriptors);
|
||||
addRow("file-descriptors", P::CloseFileDescriptors);
|
||||
}
|
||||
|
||||
void tst_QProcess::unixProcessParameters()
|
||||
@ -1618,11 +1619,11 @@ void tst_QProcess::unixProcessParametersAndChildModifier()
|
||||
write(pipes[1], message, strlen(message));
|
||||
vforkControl.storeRelaxed(1);
|
||||
});
|
||||
auto flags = QProcess::UnixProcessFlag::CloseNonStandardFileDescriptors |
|
||||
auto flags = QProcess::UnixProcessFlag::CloseFileDescriptors |
|
||||
QProcess::UnixProcessFlag::UseVFork;
|
||||
process.setUnixProcessParameters({ flags });
|
||||
process.setProgram("testUnixProcessParameters/testUnixProcessParameters");
|
||||
process.setArguments({ "std-file-descriptors", QString::number(pipes[1]) });
|
||||
process.setArguments({ "file-descriptors", QString::number(pipes[1]) });
|
||||
process.start();
|
||||
QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString()));
|
||||
} // closes the writing end of the pipe
|
||||
@ -1639,6 +1640,50 @@ void tst_QProcess::unixProcessParametersAndChildModifier()
|
||||
if (haveWorkingVFork)
|
||||
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
|
||||
|
||||
void tst_QProcess::exitCodeTest()
|
||||
|
Loading…
Reference in New Issue
Block a user