1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// Copyright (C) 2021 Alex Trotsenko.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5// Qt-Security score:critical reason:execute-external-code
6
7//#define QPROCESS_DEBUG
8#include "qdebug.h"
9#include <private/qdebug_p.h>
10#include "qplatformdefs.h"
11
12#include "qprocess.h"
13#include "qprocess_p.h"
14#include "qstandardpaths.h"
15#include "private/qcore_unix_p.h"
16#include "private/qlocking_p.h"
17
18#ifdef Q_OS_DARWIN
19#include <private/qcore_mac_p.h>
20#endif
21
22#include <private/qcoreapplication_p.h>
23#include <private/qthread_p.h>
24#include <qfile.h>
25#include <qfileinfo.h>
26#include <qdir.h>
27#include <qlist.h>
28#include <qmutex.h>
29#include <qsocketnotifier.h>
30#include <qthread.h>
31
32#ifdef Q_OS_QNX
33# include <sys/neutrino.h>
34#endif
35
36#include <errno.h>
37#include <limits.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/resource.h>
41#include <termios.h>
42#include <unistd.h>
43
44#if __has_include(<paths.h>)
45# include <paths.h>
46#endif
47
48#if QT_CONFIG(process)
49#include <forkfd.h>
50#endif
51
52#ifndef O_PATH
53# define O_PATH 0
54#endif
55#ifndef _PATH_DEV
56# define _PATH_DEV "/dev/"
57#endif
58#ifndef _PATH_TTY
59# define _PATH_TTY _PATH_DEV "tty"
60#endif
61
62#ifdef Q_OS_FREEBSD
63__attribute__((weak))
64#endif
65extern char **environ;
66
67QT_BEGIN_NAMESPACE
68
69using namespace Qt::StringLiterals;
70
71#if !defined(Q_OS_DARWIN)
72
73QProcessEnvironment QProcessEnvironment::systemEnvironment()
74{
75 QProcessEnvironment env;
76 const char *entry;
77 for (int count = 0; (entry = environ[count]); ++count) {
78 const char *equal = strchr(s: entry, c: '=');
79 if (!equal)
80 continue;
81
82 QByteArray name(entry, equal - entry);
83 QByteArray value(equal + 1);
84 env.d->vars.insert(key: QProcessEnvironmentPrivate::Key(name),
85 value: QProcessEnvironmentPrivate::Value(value));
86 }
87 return env;
88}
89
90#endif // !defined(Q_OS_DARWIN)
91
92#if QT_CONFIG(process)
93
94namespace QtVforkSafe {
95// Certain libc functions we need to call in the child process scenario aren't
96// safe under vfork() because they do more than just place the system call to
97// the kernel and set errno on return. For those, we'll create a function
98// pointer like:
99// static constexpr auto foobar = __libc_foobar;
100// while for all other OSes, it'll be
101// using ::foobar;
102// allowing the code for the child side of the vfork to simply use
103// QtVforkSafe::foobar(args);
104//
105// Currently known issues are:
106//
107// - FreeBSD's libthr sigaction() wrapper locks a rwlock
108// https://github.com/freebsd/freebsd-src/blob/8dad5ece49479ba6cdcd5bb4c2799bbd61add3e6/lib/libthr/thread/thr_sig.c#L575-L641
109// - MUSL's sigaction() locks a mutex if the signal is SIGABR
110// https://github.com/bminor/musl/blob/718f363bc2067b6487900eddc9180c84e7739f80/src/signal/sigaction.c#L63-L85
111//
112// All other functions called in the child side are vfork-safe, provided that
113// PThread cancellation is disabled and Unix signals are blocked.
114#if defined(__MUSL__)
115# define LIBC_PREFIX __libc_
116#elif defined(Q_OS_FREEBSD)
117// will cause QtCore to link to ELF version "FBSDprivate_1.0"
118# define LIBC_PREFIX _
119#endif
120
121#ifdef LIBC_PREFIX
122# define CONCAT(x, y) CONCAT2(x, y)
123# define CONCAT2(x, y) x ## y
124# define DECLARE_FUNCTIONS(NAME) \
125 extern decltype(::NAME) CONCAT(LIBC_PREFIX, NAME); \
126 static constexpr auto NAME = std::addressof(CONCAT(LIBC_PREFIX, NAME));
127#else // LIBC_PREFIX
128# define DECLARE_FUNCTIONS(NAME) using ::NAME;
129#endif // LIBC_PREFIX
130
131extern "C" {
132DECLARE_FUNCTIONS(sigaction)
133}
134
135#undef LIBC_PREFIX
136#undef DECLARE_FUNCTIONS
137
138// similar to qt_ignore_sigpipe() in qcore_unix_p.h, but vfork-safe
139static void change_sigpipe(decltype(SIG_DFL) new_handler)
140{
141 struct sigaction sa;
142 memset(s: &sa, c: 0, n: sizeof(sa));
143 sa.sa_handler = new_handler;
144 sigaction(SIGPIPE, act: &sa, oact: nullptr);
145}
146} // namespace QtVforkSafe
147
148static int opendirfd(QByteArray encodedName)
149{
150 // We append "/." to the name to ensure that the directory is actually
151 // traversable (i.e., has the +x bit set). This avoids later problems
152 // with fchdir().
153 if (encodedName != "/" && !encodedName.endsWith(bv: "/."))
154 encodedName += "/.";
155 return qt_safe_open(pathname: encodedName, QT_OPEN_RDONLY | O_DIRECTORY | O_PATH);
156}
157
158namespace {
159struct AutoPipe
160{
161 int pipe[2] = { -1, -1 };
162 AutoPipe(int flags = 0)
163 {
164 qt_safe_pipe(pipefd: pipe, flags);
165 }
166 ~AutoPipe()
167 {
168 for (int fd : pipe) {
169 if (fd >= 0)
170 qt_safe_close(fd);
171 }
172 }
173
174 explicit operator bool() const { return pipe[0] >= 0; }
175 int &operator[](int idx) { return pipe[idx]; }
176 int operator[](int idx) const { return pipe[idx]; }
177};
178
179struct ChildError
180{
181 int code;
182 char function[_POSIX_PIPE_BUF - sizeof(code)];
183};
184static_assert(std::is_trivially_copy_constructible_v<ChildError>);
185#ifdef PIPE_BUF
186static_assert(PIPE_BUF >= sizeof(ChildError)); // PIPE_BUF may be bigger
187#endif
188
189// we need an errno value to use to indicate the child process modifier threw,
190// something the regular operations shouldn't set.
191static constexpr int FakeErrnoForThrow = std::numeric_limits<int>::max();
192
193static QString errorMessageForSyscall(QUtf8StringView fnName, int errnoCode = -1)
194{
195 QString msg = qt_error_string(errorCode: errnoCode);
196 return QProcess::tr(s: "Child process set up failed: %1: %2").arg(args&: fnName, args: std::move(msg));
197}
198
199static QString startFailureErrorMessage(ChildError &err, [[maybe_unused]] ssize_t bytesRead)
200{
201 // ChildError is less than the POSIX pipe buffer atomic size, so the read
202 // must not have been truncated
203 Q_ASSERT(bytesRead == sizeof(err));
204
205 qsizetype len = qstrnlen(str: err.function, maxlen: sizeof(err.function));
206 const QUtf8StringView complement(err.function, len);
207 if (err.code == FakeErrnoForThrow)
208 return QProcess::tr(s: "Child process modifier threw an exception: %1")
209 .arg(a: complement);
210 if (err.code == 0)
211 return QProcess::tr(s: "Child process modifier reported error: %1")
212 .arg(a: complement);
213 if (err.code < 0)
214 return QProcess::tr(s: "Child process modifier reported error: %1: %2")
215 .arg(args: complement, args: qt_error_string(errorCode: -err.code));
216 return errorMessageForSyscall(fnName: complement, errnoCode: err.code);
217}
218
219struct QProcessPoller
220{
221 QProcessPoller(const QProcessPrivate &proc);
222
223 int poll(const QDeadlineTimer &deadline);
224
225 pollfd &stdinPipe() { return pfds[0]; }
226 pollfd &stdoutPipe() { return pfds[1]; }
227 pollfd &stderrPipe() { return pfds[2]; }
228 pollfd &forkfd() { return pfds[3]; }
229
230 enum { n_pfds = 4 };
231 pollfd pfds[n_pfds];
232};
233
234QProcessPoller::QProcessPoller(const QProcessPrivate &proc)
235{
236 for (int i = 0; i < n_pfds; i++)
237 pfds[i] = qt_make_pollfd(fd: -1, POLLIN);
238
239 stdoutPipe().fd = proc.stdoutChannel.pipe[0];
240 stderrPipe().fd = proc.stderrChannel.pipe[0];
241
242 if (!proc.writeBuffer.isEmpty()) {
243 stdinPipe().fd = proc.stdinChannel.pipe[1];
244 stdinPipe().events = POLLOUT;
245 }
246
247 forkfd().fd = proc.forkfd;
248}
249
250int QProcessPoller::poll(const QDeadlineTimer &deadline)
251{
252 return qt_safe_poll(fds: pfds, nfds: n_pfds, deadline);
253}
254
255struct QChildProcess
256{
257 // Used for argv and envp arguments to execve()
258 struct CharPointerList
259 {
260 std::unique_ptr<char *[]> pointers;
261
262 CharPointerList(const QString &argv0, const QStringList &args);
263 explicit CharPointerList(const QProcessEnvironmentPrivate *env);
264 /*implicit*/ operator char **() const { return pointers.get(); }
265
266 private:
267 QByteArray data;
268 void updatePointers(qsizetype count);
269 };
270
271 const QProcessPrivate *d;
272 CharPointerList argv;
273 CharPointerList envp;
274 sigset_t oldsigset;
275 int workingDirectory = -2;
276 bool isUsingVfork = usingVfork();
277
278 bool ok() const
279 {
280 return workingDirectory != -1;
281 }
282
283 QChildProcess(QProcessPrivate *d)
284 : d(d), argv(resolveExecutable(program: d->program), d->arguments),
285 envp(d->environmentPrivate())
286 {
287 // Open the working directory first, because this operation can fail.
288 // That way, if it does, we don't have anything to clean up.
289 if (!d->workingDirectory.isEmpty()) {
290 workingDirectory = opendirfd(encodedName: QFile::encodeName(fileName: d->workingDirectory));
291 if (workingDirectory < 0) {
292 d->setErrorAndEmit(error: QProcess::FailedToStart, description: errorMessageForSyscall(fnName: "chdir"));
293 d->cleanup();
294
295 // make sure our destructor does nothing
296 isUsingVfork = false;
297 return;
298 }
299 }
300
301 // Block Unix signals, to ensure the user's handlers aren't run in the
302 // child side and do something weird, especially if the handler and the
303 // user of QProcess are completely different codebases.
304 maybeBlockSignals();
305
306 // Disable PThread cancellation until the child has successfully been
307 // executed. We make a number of POSIX calls in the child that are thread
308 // cancellation points and could cause an unexpected stack unwind. That
309 // would be bad enough with regular fork(), but it's likely fatal with
310 // vfork().
311 disableThreadCancellations();
312 }
313 ~QChildProcess() noexcept(false)
314 {
315 cleanup();
316 }
317
318 void cleanup() noexcept(false)
319 {
320 if (workingDirectory >= 0)
321 close(fd: workingDirectory);
322 workingDirectory = -1;
323
324 restoreSignalMask();
325 restoreThreadCancellations();
326 }
327
328 void maybeBlockSignals() noexcept
329 {
330 // We only block Unix signals if we're using vfork(), to avoid a
331 // changing behavior to the user's modifier and because in some OSes
332 // this action would block crashing signals too.
333 if (isUsingVfork) {
334 sigset_t emptyset;
335 sigfillset(set: &emptyset);
336 pthread_sigmask(SIG_SETMASK, newmask: &emptyset, oldmask: &oldsigset);
337 }
338 }
339
340 void restoreSignalMask() const noexcept
341 {
342 // this function may be called more than once
343 if (isUsingVfork)
344 pthread_sigmask(SIG_SETMASK, newmask: &oldsigset, oldmask: nullptr);
345 }
346
347 bool usingVfork() const noexcept;
348
349 template <typename Lambda> int doFork(Lambda &&childLambda)
350 {
351 pid_t pid;
352 if (isUsingVfork) {
353 QT_IGNORE_DEPRECATIONS(pid = vfork();)
354 } else {
355 pid = fork();
356 }
357 if (pid == 0)
358 _exit(childLambda());
359 return pid;
360 }
361
362 int startChild(pid_t *pid)
363 {
364 int ffdflags = FFD_CLOEXEC | (isUsingVfork ? 0 : FFD_USE_FORK);
365 return ::vforkfd(flags: ffdflags, ppid: pid, childFn: &QChildProcess::startProcess, token: this);
366 }
367
368private:
369 Q_NORETURN void startProcess() const noexcept;
370 static int startProcess(void *self) noexcept
371 {
372 static_cast<QChildProcess *>(self)->startProcess();
373 Q_UNREACHABLE_RETURN(-1);
374 }
375
376#if defined(PTHREAD_CANCEL_DISABLE)
377 int oldstate = PTHREAD_CANCEL_DISABLE;
378 void disableThreadCancellations() noexcept
379 {
380 // the following is *not* noexcept, but it won't throw while disabling
381 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, oldstate: &oldstate);
382 }
383 void restoreThreadCancellations() noexcept(false)
384 {
385 // ensure we don't call pthread_setcancelstate() again
386 int oldoldstate = std::exchange(obj&: oldstate, PTHREAD_CANCEL_DISABLE);
387 if (oldoldstate != PTHREAD_CANCEL_DISABLE) {
388 // this doesn't touch errno
389 pthread_setcancelstate(state: oldoldstate, oldstate: nullptr);
390 }
391 }
392#else
393 void disableThreadCancellations() noexcept {}
394 void restoreThreadCancellations() {}
395#endif
396
397 static QString resolveExecutable(const QString &program);
398};
399
400QChildProcess::CharPointerList::CharPointerList(const QString &program, const QStringList &args)
401{
402 qsizetype count = 1 + args.size();
403 pointers.reset(p: new char *[count + 1]);
404 pointers[count] = nullptr;
405
406 // we abuse the pointer array to store offsets first (QByteArray will
407 // reallocate, after all)
408 pointers[0] = reinterpret_cast<char *>(0);
409 data = QFile::encodeName(fileName: program);
410 data += '\0';
411
412 const auto end = args.end();
413 auto it = args.begin();
414 for (qsizetype i = 1; it != end; ++it, ++i) {
415 pointers[i] = reinterpret_cast<char *>(data.size());
416 data += QFile::encodeName(fileName: *it);
417 data += '\0';
418 }
419
420 updatePointers(count);
421}
422
423QChildProcess::CharPointerList::CharPointerList(const QProcessEnvironmentPrivate *environment)
424{
425 if (!environment)
426 return;
427
428 const QProcessEnvironmentPrivate::Map &env = environment->vars;
429 qsizetype count = env.size();
430 pointers.reset(p: new char *[count + 1]);
431 pointers[count] = nullptr;
432
433 const auto end = env.end();
434 auto it = env.begin();
435 for (qsizetype i = 0; it != end; ++it, ++i) {
436 // we abuse the pointer array to store offsets first (QByteArray will
437 // reallocate, after all)
438 pointers[i] = reinterpret_cast<char *>(data.size());
439
440 data += it.key();
441 data += '=';
442 data += it->bytes();
443 data += '\0';
444 }
445
446 updatePointers(count);
447}
448
449void QChildProcess::CharPointerList::updatePointers(qsizetype count)
450{
451 char *const base = const_cast<char *>(data.constBegin());
452 for (qsizetype i = 0; i < count; ++i)
453 pointers[i] = base + qptrdiff(pointers[i]);
454}
455} // anonymous namespace
456
457static bool qt_pollfd_check(const pollfd &pfd, short revents)
458{
459 return pfd.fd >= 0 && (pfd.revents & (revents | POLLHUP | POLLERR | POLLNVAL)) != 0;
460}
461
462static int qt_create_pipe(int *pipe)
463{
464 if (pipe[0] != -1)
465 qt_safe_close(fd: pipe[0]);
466 if (pipe[1] != -1)
467 qt_safe_close(fd: pipe[1]);
468 int pipe_ret = qt_safe_pipe(pipefd: pipe);
469 if (pipe_ret != 0) {
470 QScopedValueRollback rollback(errno);
471 qErrnoWarning(msg: "QProcess: Cannot create pipe");
472 }
473 return pipe_ret;
474}
475
476void QProcessPrivate::destroyPipe(int *pipe)
477{
478 if (pipe[1] != -1) {
479 qt_safe_close(fd: pipe[1]);
480 pipe[1] = -1;
481 }
482 if (pipe[0] != -1) {
483 qt_safe_close(fd: pipe[0]);
484 pipe[0] = -1;
485 }
486}
487
488void QProcessPrivate::closeChannel(Channel *channel)
489{
490 delete channel->notifier;
491 channel->notifier = nullptr;
492
493 destroyPipe(pipe: channel->pipe);
494}
495
496void QProcessPrivate::cleanup()
497{
498 q_func()->setProcessState(QProcess::NotRunning);
499
500 closeChannels();
501 delete stateNotifier;
502 stateNotifier = nullptr;
503 destroyPipe(pipe: childStartedPipe);
504 pid = 0;
505 if (forkfd != -1) {
506 qt_safe_close(fd: forkfd);
507 forkfd = -1;
508 }
509}
510
511/*
512 Create the pipes to a QProcessPrivate::Channel.
513*/
514bool QProcessPrivate::openChannel(Channel &channel)
515{
516 Q_Q(QProcess);
517
518 if (channel.type == Channel::Normal) {
519 // we're piping this channel to our own process
520 if (qt_create_pipe(pipe: channel.pipe) != 0) {
521 setErrorAndEmit(error: QProcess::FailedToStart, description: errorMessageForSyscall(fnName: "pipe"));
522 return false;
523 }
524
525 // create the socket notifiers
526 if (threadData.loadRelaxed()->hasEventDispatcher()) {
527 if (&channel == &stdinChannel) {
528 channel.notifier = new QSocketNotifier(QSocketNotifier::Write, q);
529 channel.notifier->setSocket(channel.pipe[1]);
530 QObject::connect(sender: channel.notifier, SIGNAL(activated(QSocketDescriptor)),
531 receiver: q, SLOT(_q_canWrite()));
532 } else {
533 channel.notifier = new QSocketNotifier(QSocketNotifier::Read, q);
534 channel.notifier->setSocket(channel.pipe[0]);
535 const char *receiver;
536 if (&channel == &stdoutChannel)
537 receiver = SLOT(_q_canReadStandardOutput());
538 else
539 receiver = SLOT(_q_canReadStandardError());
540 QObject::connect(sender: channel.notifier, SIGNAL(activated(QSocketDescriptor)),
541 receiver: q, member: receiver);
542 }
543 }
544
545 return true;
546 } else if (channel.type == Channel::Redirect) {
547 // we're redirecting the channel to/from a file
548 QByteArray fname = QFile::encodeName(fileName: channel.file);
549
550 if (&channel == &stdinChannel) {
551 // try to open in read-only mode
552 channel.pipe[1] = -1;
553 if ( (channel.pipe[0] = qt_safe_open(pathname: fname, O_RDONLY)) != -1)
554 return true; // success
555 setErrorAndEmit(error: QProcess::FailedToStart,
556 description: QProcess::tr(s: "Could not open input redirection for reading"));
557 } else {
558 int mode = O_WRONLY | O_CREAT;
559 if (channel.append)
560 mode |= O_APPEND;
561 else
562 mode |= O_TRUNC;
563
564 channel.pipe[0] = -1;
565 if ( (channel.pipe[1] = qt_safe_open(pathname: fname, flags: mode, mode: 0666)) != -1)
566 return true; // success
567
568 setErrorAndEmit(error: QProcess::FailedToStart,
569 description: QProcess::tr(s: "Could not open input redirection for reading"));
570 }
571 return false;
572 } else {
573 Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
574
575 Channel *source;
576 Channel *sink;
577
578 if (channel.type == Channel::PipeSource) {
579 // we are the source
580 source = &channel;
581 sink = &channel.process->stdinChannel;
582
583 Q_ASSERT(source == &stdoutChannel);
584 Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
585 } else {
586 // we are the sink;
587 source = &channel.process->stdoutChannel;
588 sink = &channel;
589
590 Q_ASSERT(sink == &stdinChannel);
591 Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
592 }
593
594 if (source->pipe[1] != INVALID_Q_PIPE || sink->pipe[0] != INVALID_Q_PIPE) {
595 // already created, do nothing
596 return true;
597 } else {
598 Q_ASSERT(source->pipe[0] == INVALID_Q_PIPE && source->pipe[1] == INVALID_Q_PIPE);
599 Q_ASSERT(sink->pipe[0] == INVALID_Q_PIPE && sink->pipe[1] == INVALID_Q_PIPE);
600
601 Q_PIPE pipe[2] = { -1, -1 };
602 if (qt_create_pipe(pipe) != 0) {
603 setErrorAndEmit(error: QProcess::FailedToStart, description: errorMessageForSyscall(fnName: "pipe"));
604 return false;
605 }
606 sink->pipe[0] = pipe[0];
607 source->pipe[1] = pipe[1];
608
609 return true;
610 }
611 }
612}
613
614void QProcessPrivate::commitChannels() const
615{
616 // copy the stdin socket if asked to (without closing on exec)
617 if (stdinChannel.pipe[0] != INVALID_Q_PIPE)
618 qt_safe_dup2(oldfd: stdinChannel.pipe[0], STDIN_FILENO, flags: 0);
619
620 // copy the stdout and stderr if asked to
621 if (stdoutChannel.pipe[1] != INVALID_Q_PIPE)
622 qt_safe_dup2(oldfd: stdoutChannel.pipe[1], STDOUT_FILENO, flags: 0);
623 if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
624 qt_safe_dup2(oldfd: stderrChannel.pipe[1], STDERR_FILENO, flags: 0);
625 } else {
626 // merge stdout and stderr if asked to
627 if (processChannelMode == QProcess::MergedChannels)
628 qt_safe_dup2(STDOUT_FILENO, STDERR_FILENO, flags: 0);
629 }
630}
631
632inline QString QChildProcess::resolveExecutable(const QString &program)
633{
634#ifdef Q_OS_DARWIN
635 // allow invoking of .app bundles on the Mac.
636 QFileInfo fileInfo(program);
637 if (program.endsWith(".app"_L1) && fileInfo.isDir()) {
638 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0,
639 QCFString(fileInfo.absoluteFilePath()),
640 kCFURLPOSIXPathStyle, true);
641 {
642 // CFBundle is not reentrant, since CFBundleCreate might return a reference
643 // to a cached bundle object. Protect the bundle calls with a mutex lock.
644 Q_CONSTINIT static QBasicMutex cfbundleMutex;
645 const auto locker = qt_scoped_lock(cfbundleMutex);
646 QCFType<CFBundleRef> bundle = CFBundleCreate(0, url);
647 // 'executableURL' can be either relative or absolute ...
648 QCFType<CFURLRef> executableURL = CFBundleCopyExecutableURL(bundle);
649 // not to depend on caching - make sure it's always absolute.
650 url = CFURLCopyAbsoluteURL(executableURL);
651 }
652 if (url) {
653 const QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
654 return QString::fromCFString(str);
655 }
656 }
657#endif
658
659 if (!program.contains(c: u'/')) {
660 // findExecutable() returns its argument if it's an absolute path,
661 // otherwise it searches $PATH; returns empty if not found (we handle
662 // that case much later)
663 return QStandardPaths::findExecutable(executableName: program);
664 }
665 return program;
666}
667
668extern "C" {
669__attribute__((weak)) pid_t __interceptor_vfork();
670}
671
672inline bool globalUsingVfork() noexcept
673{
674#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
675 // ASan writes to global memory, so we mustn't use vfork().
676 return false;
677#endif
678#if defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer)
679 // Ditto, apparently
680 return false;
681#endif
682#if defined(Q_OS_LINUX) && !QT_CONFIG(forkfd_pidfd)
683 // some broken environments are known to have problems with the new Linux
684 // API, so we have a way for users to opt-out during configure time (see
685 // QTBUG-86285)
686 return false;
687#endif
688#if defined(Q_OS_DARWIN)
689 // Using vfork() for startDetached() is causing problems. We don't know
690 // why: without the tools to investigate why it happens, we didn't bother.
691 return false;
692#endif
693#if defined(Q_OS_CYGWIN)
694 // Fails to link Qt6Core, so we avoid that..
695 return false;
696#endif
697
698 // Dynamically detect whether libasan or libtsan are loaded into the
699 // process' memory. We need this because the user's code may be compiled
700 // with ASan or TSan, but not Qt.
701 return __interceptor_vfork == nullptr;
702}
703
704inline bool QChildProcess::usingVfork() const noexcept
705{
706 if (!globalUsingVfork())
707 return false;
708
709 if (!d->unixExtras || !d->unixExtras->childProcessModifier)
710 return true; // no modifier was supplied
711
712 // if a modifier was supplied, use fork() unless the user opts in to
713 // vfork()
714 auto flags = d->unixExtras->processParameters.flags;
715 return flags.testFlag(flag: QProcess::UnixProcessFlag::UseVFork);
716}
717
718#ifdef QT_BUILD_INTERNAL
719Q_AUTOTEST_EXPORT bool _qprocessUsingVfork() noexcept
720{
721 return globalUsingVfork();
722}
723#endif
724
725void QProcessPrivate::startProcess()
726{
727 Q_Q(QProcess);
728 q->setProcessState(QProcess::Starting);
729
730#if defined (QPROCESS_DEBUG)
731 qDebug("QProcessPrivate::startProcess()");
732#endif
733
734 // Initialize pipes
735 if (!openChannels()) {
736 // openChannel sets the error string
737 Q_ASSERT(!errorString.isEmpty());
738 cleanup();
739 return;
740 }
741 if (qt_create_pipe(pipe: childStartedPipe) != 0) {
742 setErrorAndEmit(error: QProcess::FailedToStart, description: errorMessageForSyscall(fnName: "pipe"));
743 cleanup();
744 return;
745 }
746
747 if (threadData.loadRelaxed()->hasEventDispatcher()) {
748 // Set up to notify about startup completion (and premature death).
749 // Once the process has started successfully, we reconfigure the
750 // notifier to watch the fork_fd for expected death.
751 stateNotifier = new QSocketNotifier(childStartedPipe[0],
752 QSocketNotifier::Read, q);
753 QObject::connect(sender: stateNotifier, SIGNAL(activated(QSocketDescriptor)),
754 receiver: q, SLOT(_q_startupNotification()));
755 }
756
757 // Prepare the arguments and the environment
758 QChildProcess childProcess(this);
759 if (!childProcess.ok()) {
760 Q_ASSERT(processError != QProcess::UnknownError);
761 return;
762 }
763
764 // Start the child.
765 forkfd = childProcess.startChild(pid: &pid);
766 int lastForkErrno = errno;
767
768 if (forkfd == -1) {
769 // Cleanup, report error and return
770 childProcess.cleanup();
771#if defined (QPROCESS_DEBUG)
772 qDebug("fork failed: %ls", qUtf16Printable(qt_error_string(lastForkErrno)));
773#endif
774 q->setProcessState(QProcess::NotRunning);
775 setErrorAndEmit(error: QProcess::FailedToStart,
776 description: QProcess::tr(s: "Resource error (fork failure): %1").arg(a: qt_error_string(errorCode: lastForkErrno)));
777 cleanup();
778 return;
779 }
780
781 Q_ASSERT(pid > 0);
782
783 // parent
784 // close the ends we don't use and make all pipes non-blocking
785 qt_safe_close(fd: childStartedPipe[1]);
786 childStartedPipe[1] = -1;
787
788 if (stdinChannel.pipe[0] != -1) {
789 qt_safe_close(fd: stdinChannel.pipe[0]);
790 stdinChannel.pipe[0] = -1;
791 }
792
793 if (stdinChannel.pipe[1] != -1)
794 ::fcntl(fd: stdinChannel.pipe[1], F_SETFL, ::fcntl(fd: stdinChannel.pipe[1], F_GETFL) | O_NONBLOCK);
795
796 if (stdoutChannel.pipe[1] != -1) {
797 qt_safe_close(fd: stdoutChannel.pipe[1]);
798 stdoutChannel.pipe[1] = -1;
799 }
800
801 if (stdoutChannel.pipe[0] != -1)
802 ::fcntl(fd: stdoutChannel.pipe[0], F_SETFL, ::fcntl(fd: stdoutChannel.pipe[0], F_GETFL) | O_NONBLOCK);
803
804 if (stderrChannel.pipe[1] != -1) {
805 qt_safe_close(fd: stderrChannel.pipe[1]);
806 stderrChannel.pipe[1] = -1;
807 }
808 if (stderrChannel.pipe[0] != -1)
809 ::fcntl(fd: stderrChannel.pipe[0], F_SETFL, ::fcntl(fd: stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
810}
811
812Q_NORETURN void
813failChildProcess(const QProcessPrivate *d, const char *description, int code) noexcept
814{
815 ChildError error = {};
816 error.code = code;
817 qstrncpy(dst: error.function, src: description, len: sizeof(error.function));
818 qt_safe_write(fd: d->childStartedPipe[1], data: &error, len: sizeof(error));
819 _exit(status: -1);
820}
821
822void QProcess::failChildProcessModifier(const char *description, int error) noexcept
823{
824 // We signal user errors with negative errnos
825 failChildProcess(d: d_func(), description, code: -error);
826}
827
828// See IMPORTANT notice below
829static const char *applyProcessParameters(const QProcess::UnixProcessParameters &params)
830{
831 // Apply Unix signal handler parameters.
832 // We don't expect signal() to fail, so we ignore its return value
833 bool ignore_sigpipe = params.flags.testFlag(flag: QProcess::UnixProcessFlag::IgnoreSigPipe);
834 if (ignore_sigpipe)
835 QtVforkSafe::change_sigpipe(SIG_IGN);
836 if (params.flags.testFlag(flag: QProcess::UnixProcessFlag::ResetSignalHandlers)) {
837 struct sigaction sa = {};
838 sa.sa_handler = SIG_DFL;
839 for (int sig = 1; sig < NSIG; ++sig) {
840 if (!ignore_sigpipe || sig != SIGPIPE)
841 QtVforkSafe::sigaction(sig: sig, act: &sa, oact: nullptr);
842 }
843
844 // and unmask all signals
845 sigset_t set;
846 sigemptyset(set: &set);
847 sigprocmask(SIG_SETMASK, set: &set, oset: nullptr);
848 }
849
850 // Close all file descriptors above stderr.
851 // This isn't expected to fail, so we ignore close()'s return value.
852 if (params.flags.testFlag(flag: QProcess::UnixProcessFlag::CloseFileDescriptors)) {
853 int r = -1;
854 int fd = qMax(STDERR_FILENO + 1, b: params.lowestFileDescriptorToClose);
855#ifdef CLOSE_RANGE_CLOEXEC
856 // Mark the file descriptors for closing upon execve() - we delay
857 // closing so we don't close the ones QProcess needs for itself.
858 // On FreeBSD, this probably won't fail.
859 // On Linux, this will fail with ENOSYS before kernel 5.9 and EINVAL
860 // before 5.11.
861 r = close_range(fd: fd, INT_MAX, CLOSE_RANGE_CLOEXEC);
862#endif
863 if (r == -1) {
864 // We *could* read /dev/fd to find out what file descriptors are
865 // open, but we won't. We CANNOT use opendir() here because it
866 // allocates memory. Using getdents(2) plus either strtoul() or
867 // std::from_chars() would be acceptable.
868 int max_fd = INT_MAX;
869 if (struct rlimit limit; getrlimit(RLIMIT_NOFILE, rlimits: &limit) == 0)
870 max_fd = limit.rlim_cur;
871 for ( ; fd < max_fd; ++fd)
872 fcntl(fd: fd, F_SETFD, FD_CLOEXEC);
873 }
874 }
875
876 // Apply session and process group settings. This may fail.
877 if (params.flags.testFlag(flag: QProcess::UnixProcessFlag::CreateNewSession)) {
878 if (setsid() < 0)
879 return "setsid";
880 }
881
882 // Disconnect from the controlling TTY. This probably won't fail. Must be
883 // done after the session settings from above.
884 if (params.flags.testFlag(flag: QProcess::UnixProcessFlag::DisconnectControllingTerminal)) {
885#ifdef TIOCNOTTY
886 if (int fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY); fd >= 0) {
887 // we still have a controlling TTY; give it up
888 int r = ioctl(fd: fd, TIOCNOTTY);
889 int savedErrno = errno;
890 close(fd: fd);
891 if (r != 0) {
892 errno = savedErrno;
893 return "ioctl";
894 }
895 }
896#endif
897 }
898
899 // Disable core dumps near the end. This isn't expected to fail.
900 if (params.flags.testFlag(flag: QProcess::UnixProcessFlag::DisableCoreDumps)) {
901 if (struct rlimit lim; getrlimit(RLIMIT_CORE, rlimits: &lim) == 0 && lim.rlim_cur) {
902 // We'll leave rlim_max untouched, so the child can set it back if it
903 // wants to. We don't expect setrlimit() to fail, so we ignore its
904 // return value.
905 lim.rlim_cur = 0;
906 (void) setrlimit(RLIMIT_CORE, rlimits: &lim);
907 }
908 }
909
910 // Apply UID and GID parameters last. This isn't expected to fail either:
911 // either we're trying to impersonate what we already are, or we're EUID or
912 // EGID root, in which case we are allowed to do this.
913 if (params.flags.testFlag(flag: QProcess::UnixProcessFlag::ResetIds)) {
914 int r = setgid(getgid());
915 r = setuid(getuid());
916 (void) r;
917 }
918
919 return nullptr;
920}
921
922// the noexcept here adds an extra layer of protection
923static void callChildProcessModifier(const QProcessPrivate *d) noexcept
924{
925 QT_TRY {
926 if (d->unixExtras->childProcessModifier)
927 d->unixExtras->childProcessModifier();
928 } QT_CATCH (std::exception &e) {
929 failChildProcess(d, description: e.what(), code: FakeErrnoForThrow);
930 } QT_CATCH (...) {
931 failChildProcess(d, description: "throw", code: FakeErrnoForThrow);
932 }
933}
934
935// IMPORTANT:
936//
937// This function is called in a vfork() context on some OSes (notably, Linux
938// with forkfd), so it MUST NOT modify any non-local variable because it's
939// still sharing memory with the parent process.
940void QChildProcess::startProcess() const noexcept
941{
942 // Render channels configuration.
943 d->commitChannels();
944
945 // make sure this fd is closed if execv() succeeds
946 qt_safe_close(fd: d->childStartedPipe[0]);
947
948 // enter the working directory
949 if (workingDirectory >= 0 && fchdir(fd: workingDirectory) == -1)
950 failChildProcess(d, description: "fchdir", errno);
951
952 bool sigpipeHandled = false;
953 bool sigmaskHandled = false;
954 if (d->unixExtras) {
955 // FIRST we call the user modifier function, before we dropping
956 // privileges or closing non-standard file descriptors
957 callChildProcessModifier(d);
958
959 // then we apply our other user-provided parameters
960 if (const char *what = applyProcessParameters(params: d->unixExtras->processParameters))
961 failChildProcess(d, description: what, errno);
962
963 auto flags = d->unixExtras->processParameters.flags;
964 using P = QProcess::UnixProcessFlag;
965 sigpipeHandled = flags.testAnyFlags(flags: P::ResetSignalHandlers | P::IgnoreSigPipe);
966 sigmaskHandled = flags.testFlag(flag: P::ResetSignalHandlers);
967 }
968 if (!sigpipeHandled) {
969 // reset the signal that we ignored
970 QtVforkSafe::change_sigpipe(SIG_DFL); // reset the signal that we ignored
971 }
972 if (!sigmaskHandled) {
973 // restore the signal mask from the parent, if applyProcessParameters()
974 // hasn't completely reset it
975 restoreSignalMask();
976 }
977
978 // execute the process
979 if (!envp.pointers)
980 qt_safe_execv(path: argv[0], argv);
981 else
982 qt_safe_execve(filename: argv[0], argv, envp);
983 failChildProcess(d, description: "execve", errno);
984}
985
986bool QProcessPrivate::processStarted(QString *errorMessage)
987{
988 Q_Q(QProcess);
989
990 ChildError buf;
991 ssize_t ret = qt_safe_read(fd: childStartedPipe[0], data: &buf, maxlen: sizeof(buf));
992
993 if (stateNotifier) {
994 stateNotifier->setEnabled(false);
995 stateNotifier->disconnect(receiver: q);
996 }
997 qt_safe_close(fd: childStartedPipe[0]);
998 childStartedPipe[0] = -1;
999
1000#if defined (QPROCESS_DEBUG)
1001 qDebug("QProcessPrivate::processStarted() == %s", ret <= 0 ? "true" : "false");
1002#endif
1003
1004 if (ret <= 0) { // process successfully started
1005 if (stateNotifier) {
1006 QObject::connect(sender: stateNotifier, SIGNAL(activated(QSocketDescriptor)),
1007 receiver: q, SLOT(_q_processDied()));
1008 stateNotifier->setSocket(forkfd);
1009 stateNotifier->setEnabled(true);
1010 }
1011 if (stdoutChannel.notifier)
1012 stdoutChannel.notifier->setEnabled(true);
1013 if (stderrChannel.notifier)
1014 stderrChannel.notifier->setEnabled(true);
1015
1016 return true;
1017 }
1018
1019 // did we read an error message?
1020 if (errorMessage)
1021 *errorMessage = startFailureErrorMessage(err&: buf, bytesRead: ret);
1022
1023 return false;
1024}
1025
1026qint64 QProcessPrivate::bytesAvailableInChannel(const Channel *channel) const
1027{
1028 Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
1029 int nbytes = 0;
1030 qint64 available = 0;
1031 if (::ioctl(fd: channel->pipe[0], FIONREAD, (char *) &nbytes) >= 0)
1032 available = (qint64) nbytes;
1033#if defined (QPROCESS_DEBUG)
1034 qDebug("QProcessPrivate::bytesAvailableInChannel(%d) == %lld", int(channel - &stdinChannel), available);
1035#endif
1036 return available;
1037}
1038
1039qint64 QProcessPrivate::readFromChannel(const Channel *channel, char *data, qint64 maxlen)
1040{
1041 Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
1042 qint64 bytesRead = qt_safe_read(fd: channel->pipe[0], data, maxlen);
1043#if defined QPROCESS_DEBUG
1044 int save_errno = errno;
1045 qDebug("QProcessPrivate::readFromChannel(%d, %p \"%s\", %lld) == %lld",
1046 int(channel - &stdinChannel),
1047 data, QtDebugUtils::toPrintable(data, bytesRead, 16).constData(), maxlen, bytesRead);
1048 errno = save_errno;
1049#endif
1050 if (bytesRead == -1 && errno == EWOULDBLOCK)
1051 return -2;
1052 return bytesRead;
1053}
1054
1055/*! \reimp
1056*/
1057qint64 QProcess::writeData(const char *data, qint64 len)
1058{
1059 Q_D(QProcess);
1060
1061 if (d->stdinChannel.closed) {
1062#if defined QPROCESS_DEBUG
1063 qDebug("QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)",
1064 data, QtDebugUtils::toPrintable(data, len, 16).constData(), len);
1065#endif
1066 return 0;
1067 }
1068
1069 d->write(data, size: len);
1070 if (d->stdinChannel.notifier)
1071 d->stdinChannel.notifier->setEnabled(true);
1072
1073#if defined QPROCESS_DEBUG
1074 qDebug("QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)",
1075 data, QtDebugUtils::toPrintable(data, len, 16).constData(), len, len);
1076#endif
1077 return len;
1078}
1079
1080bool QProcessPrivate::_q_canWrite()
1081{
1082 if (writeBuffer.isEmpty()) {
1083 if (stdinChannel.notifier)
1084 stdinChannel.notifier->setEnabled(false);
1085#if defined QPROCESS_DEBUG
1086 qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer).");
1087#endif
1088 return false;
1089 }
1090
1091 const bool writeSucceeded = writeToStdin();
1092
1093 if (writeBuffer.isEmpty() && stdinChannel.closed)
1094 closeWriteChannel();
1095 else if (stdinChannel.notifier)
1096 stdinChannel.notifier->setEnabled(!writeBuffer.isEmpty());
1097
1098 return writeSucceeded;
1099}
1100
1101bool QProcessPrivate::writeToStdin()
1102{
1103 const char *data = writeBuffer.readPointer();
1104 const qint64 bytesToWrite = writeBuffer.nextDataBlockSize();
1105
1106 qint64 written = qt_safe_write_nosignal(fd: stdinChannel.pipe[1], data, len: bytesToWrite);
1107#if defined QPROCESS_DEBUG
1108 qDebug("QProcessPrivate::writeToStdin(), write(%p \"%s\", %lld) == %lld", data,
1109 QtDebugUtils::toPrintable(data, bytesToWrite, 16).constData(), bytesToWrite, written);
1110 if (written == -1)
1111 qDebug("QProcessPrivate::writeToStdin(), failed to write (%ls)", qUtf16Printable(qt_error_string(errno)));
1112#endif
1113 if (written == -1) {
1114 // If the O_NONBLOCK flag is set and If some data can be written without blocking
1115 // the process, write() will transfer what it can and return the number of bytes written.
1116 // Otherwise, it will return -1 and set errno to EAGAIN
1117 if (errno == EAGAIN)
1118 return true;
1119
1120 closeChannel(channel: &stdinChannel);
1121 setErrorAndEmit(error: QProcess::WriteError);
1122 return false;
1123 }
1124 writeBuffer.free(bytes: written);
1125 if (!emittedBytesWritten && written != 0) {
1126 emittedBytesWritten = true;
1127 emit q_func()->bytesWritten(bytes: written);
1128 emittedBytesWritten = false;
1129 }
1130 return true;
1131}
1132
1133void QProcessPrivate::terminateProcess()
1134{
1135#if defined (QPROCESS_DEBUG)
1136 qDebug("QProcessPrivate::terminateProcess() pid=%jd", intmax_t(pid));
1137#endif
1138 if (pid > 0)
1139 ::kill(pid: pid, SIGTERM);
1140}
1141
1142void QProcessPrivate::killProcess()
1143{
1144#if defined (QPROCESS_DEBUG)
1145 qDebug("QProcessPrivate::killProcess() pid=%jd", intmax_t(pid));
1146#endif
1147 if (pid > 0)
1148 ::kill(pid: pid, SIGKILL);
1149}
1150
1151bool QProcessPrivate::waitForStarted(const QDeadlineTimer &deadline)
1152{
1153#if defined (QPROCESS_DEBUG)
1154 const qint64 msecs = deadline.remainingTime();
1155 qDebug("QProcessPrivate::waitForStarted(%lld) waiting for child to start (fd = %d)",
1156 msecs, childStartedPipe[0]);
1157#endif
1158
1159 pollfd pfd = qt_make_pollfd(fd: childStartedPipe[0], POLLIN);
1160
1161 if (qt_safe_poll(fds: &pfd, nfds: 1, deadline) == 0) {
1162 setError(error: QProcess::Timedout);
1163#if defined (QPROCESS_DEBUG)
1164 qDebug("QProcessPrivate::waitForStarted(%lld) == false (timed out)", msecs);
1165#endif
1166 return false;
1167 }
1168
1169 bool startedEmitted = _q_startupNotification();
1170#if defined (QPROCESS_DEBUG)
1171 qDebug("QProcessPrivate::waitForStarted() == %s", startedEmitted ? "true" : "false");
1172#endif
1173 return startedEmitted;
1174}
1175
1176bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
1177{
1178#if defined (QPROCESS_DEBUG)
1179 qDebug("QProcessPrivate::waitForReadyRead(%lld)", deadline.remainingTime());
1180#endif
1181
1182 forever {
1183 QProcessPoller poller(*this);
1184
1185 int ret = poller.poll(deadline);
1186
1187 if (ret < 0) {
1188 break;
1189 }
1190 if (ret == 0) {
1191 setError(error: QProcess::Timedout);
1192 return false;
1193 }
1194
1195 // This calls QProcessPrivate::tryReadFromChannel(), which returns true
1196 // if we emitted readyRead() signal on the current read channel.
1197 bool readyReadEmitted = false;
1198 if (qt_pollfd_check(pfd: poller.stdoutPipe(), POLLIN) && _q_canReadStandardOutput())
1199 readyReadEmitted = true;
1200 if (qt_pollfd_check(pfd: poller.stderrPipe(), POLLIN) && _q_canReadStandardError())
1201 readyReadEmitted = true;
1202
1203 if (readyReadEmitted)
1204 return true;
1205
1206 if (qt_pollfd_check(pfd: poller.stdinPipe(), POLLOUT))
1207 _q_canWrite();
1208
1209 // Signals triggered by I/O may have stopped this process:
1210 if (processState == QProcess::NotRunning)
1211 return false;
1212
1213 // We do this after checking the pipes, so we cannot reach it as long
1214 // as there is any data left to be read from an already dead process.
1215 if (qt_pollfd_check(pfd: poller.forkfd(), POLLIN)) {
1216 processFinished();
1217 return false;
1218 }
1219 }
1220 return false;
1221}
1222
1223bool QProcessPrivate::waitForBytesWritten(const QDeadlineTimer &deadline)
1224{
1225#if defined (QPROCESS_DEBUG)
1226 qDebug("QProcessPrivate::waitForBytesWritten(%lld)", deadline.remainingTime());
1227#endif
1228
1229 while (!writeBuffer.isEmpty()) {
1230 QProcessPoller poller(*this);
1231
1232 int ret = poller.poll(deadline);
1233
1234 if (ret < 0) {
1235 break;
1236 }
1237
1238 if (ret == 0) {
1239 setError(error: QProcess::Timedout);
1240 return false;
1241 }
1242
1243 if (qt_pollfd_check(pfd: poller.stdinPipe(), POLLOUT))
1244 return _q_canWrite();
1245
1246 if (qt_pollfd_check(pfd: poller.stdoutPipe(), POLLIN))
1247 _q_canReadStandardOutput();
1248
1249 if (qt_pollfd_check(pfd: poller.stderrPipe(), POLLIN))
1250 _q_canReadStandardError();
1251
1252 // Signals triggered by I/O may have stopped this process:
1253 if (processState == QProcess::NotRunning)
1254 return false;
1255
1256 if (qt_pollfd_check(pfd: poller.forkfd(), POLLIN)) {
1257 processFinished();
1258 return false;
1259 }
1260 }
1261
1262 return false;
1263}
1264
1265bool QProcessPrivate::waitForFinished(const QDeadlineTimer &deadline)
1266{
1267#if defined (QPROCESS_DEBUG)
1268 qDebug("QProcessPrivate::waitForFinished(%lld)", deadline.remainingTime());
1269#endif
1270
1271 forever {
1272 QProcessPoller poller(*this);
1273
1274 int ret = poller.poll(deadline);
1275
1276 if (ret < 0) {
1277 break;
1278 }
1279 if (ret == 0) {
1280 setError(error: QProcess::Timedout);
1281 return false;
1282 }
1283
1284 if (qt_pollfd_check(pfd: poller.stdinPipe(), POLLOUT))
1285 _q_canWrite();
1286
1287 if (qt_pollfd_check(pfd: poller.stdoutPipe(), POLLIN))
1288 _q_canReadStandardOutput();
1289
1290 if (qt_pollfd_check(pfd: poller.stderrPipe(), POLLIN))
1291 _q_canReadStandardError();
1292
1293 // Signals triggered by I/O may have stopped this process:
1294 if (processState == QProcess::NotRunning)
1295 return true;
1296
1297 if (qt_pollfd_check(pfd: poller.forkfd(), POLLIN)) {
1298 processFinished();
1299 return true;
1300 }
1301 }
1302 return false;
1303}
1304
1305void QProcessPrivate::waitForDeadChild()
1306{
1307 Q_ASSERT(forkfd != -1);
1308
1309 // read the process information from our fd
1310 forkfd_info info = {}; // Silence -Wmaybe-uninitialized; Thiago says forkfd_wait cannot fail here
1311 // (QTBUG-119081)
1312 int ret;
1313 QT_EINTR_LOOP(ret, forkfd_wait(forkfd, &info, nullptr));
1314
1315 exitCode = info.status;
1316 exitStatus = info.code == CLD_EXITED ? QProcess::NormalExit : QProcess::CrashExit;
1317
1318 delete stateNotifier;
1319 stateNotifier = nullptr;
1320
1321 QT_EINTR_LOOP(ret, forkfd_close(forkfd));
1322 forkfd = -1; // Child is dead, don't try to kill it anymore
1323
1324#if defined QPROCESS_DEBUG
1325 qDebug() << "QProcessPrivate::waitForDeadChild() dead with exitCode"
1326 << exitCode << ", crashed?" << (info.code != CLD_EXITED);
1327#endif
1328}
1329
1330bool QProcessPrivate::startDetached(qint64 *pid)
1331{
1332 AutoPipe startedPipe, pidPipe;
1333 if (!startedPipe || !pidPipe) {
1334 setErrorAndEmit(error: QProcess::FailedToStart, description: errorMessageForSyscall(fnName: "pipe"));
1335 return false;
1336 }
1337
1338 if (!openChannelsForDetached()) {
1339 // openChannel sets the error string
1340 closeChannels();
1341 return false;
1342 }
1343
1344 // see startProcess() for more information
1345 QChildProcess childProcess(this);
1346 if (!childProcess.ok()) {
1347 Q_ASSERT(processError != QProcess::UnknownError);
1348 return false;
1349 }
1350
1351 childStartedPipe[1] = startedPipe[1]; // for failChildProcess()
1352 pid_t childPid = childProcess.doFork(childLambda: [&] {
1353 ::setsid();
1354
1355 qt_safe_close(fd: startedPipe[0]);
1356 qt_safe_close(fd: pidPipe[0]);
1357
1358 pid_t doubleForkPid;
1359 if (childProcess.startChild(pid: &doubleForkPid) == -1)
1360 failChildProcess(d: this, description: "fork", errno);
1361
1362 // success
1363 qt_safe_write(fd: pidPipe[1], data: &doubleForkPid, len: sizeof(pid_t));
1364 return 0;
1365 });
1366 childStartedPipe[1] = -1;
1367
1368 int savedErrno = errno;
1369 closeChannels();
1370
1371 if (childPid == -1) {
1372 childProcess.cleanup();
1373 setErrorAndEmit(error: QProcess::FailedToStart, description: errorMessageForSyscall(fnName: "fork", errnoCode: savedErrno));
1374 return false;
1375 }
1376
1377 // close the writing ends of the pipes so we can properly get EOFs
1378 qt_safe_close(fd: pidPipe[1]);
1379 qt_safe_close(fd: startedPipe[1]);
1380 pidPipe[1] = startedPipe[1] = -1;
1381
1382 // This read() will block until we're cleared to proceed. If it returns 0
1383 // (EOF), it means the direct child has exited and the grandchild
1384 // successfully execve()'d the target process. If it returns any positive
1385 // result, it means one of the two children wrote an error result. Negative
1386 // values should not happen.
1387 ChildError childStatus;
1388 ssize_t startResult = qt_safe_read(fd: startedPipe[0], data: &childStatus, maxlen: sizeof(childStatus));
1389
1390 // reap the intermediate child
1391 int result;
1392 qt_safe_waitpid(pid: childPid, status: &result, options: 0);
1393
1394 bool success = (startResult == 0); // nothing written -> no error
1395 if (success && pid) {
1396 pid_t actualPid;
1397 if (qt_safe_read(fd: pidPipe[0], data: &actualPid, maxlen: sizeof(pid_t)) != sizeof(pid_t))
1398 actualPid = 0; // this shouldn't happen!
1399 *pid = actualPid;
1400 } else if (!success) {
1401 if (pid)
1402 *pid = -1;
1403 childProcess.cleanup();
1404 setErrorAndEmit(error: QProcess::FailedToStart,
1405 description: startFailureErrorMessage(err&: childStatus, bytesRead: startResult));
1406 }
1407 return success;
1408}
1409
1410#endif // QT_CONFIG(process)
1411
1412QT_END_NAMESPACE
1413

source code of qtbase/src/corelib/io/qprocess_unix.cpp