176 Commits

Author SHA1 Message Date
Gabor Karsay
7e64004db0 docs: mark macros, flags, enums with percent sign 2022-03-04 16:21:55 +00:00
Philip Withnall
44f4d55150 gspawn: Clarify that empty argv arrays are not allowed when spawning
While `execve()` might allow (probably malicious) users to execute a
program with an empty `argv` array, gspawn does not. It’s not actually
possible, as the path to the binary to execute is not specified
separately from the argument array.

Explicitly document and encode that in preconditions.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2022-02-11 14:45:43 +00:00
Marc-André Lureau
e05227371d glib/win32: fix passing same fd for stdout & stderr spawn
Delay closing the FDs after all input FDs are duped.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2022-02-07 23:39:36 +04:00
Marc-André Lureau
2d35c57718 glib/win32: implement fd passing with g_spawn_async_with_pipes_and_fds
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2022-02-07 23:39:36 +04:00
Marc-André Lureau
34ce1b1f93 glib/spawn: win32 helper doesn't support same fd for out&err
This is fixed in the following commits of this MR.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2022-02-07 23:31:10 +04:00
Philip Withnall
ce04a12404 gspawn: Report errors with closing file descriptors between fork/exec
If a seccomp policy is set up incorrectly so that it returns `EPERM` for
`close_range()` rather than `ENOSYS` due to it not being recognised, no
error would previously be reported from GLib, but some file descriptors
wouldn’t be closed, and that would cause a hung zombie process. The
zombie process would be waiting for one half of a socket to be closed.

Fix that by correctly propagating errors from `close_range()` back to the
parent process so they can be reported correctly.

Distributions which aren’t yet carrying the Docker fix to correctly
return `ENOSYS` from unrecognised syscalls may want to temporarily carry
an additional patch to fall back to `safe_fdwalk()` if `close_range()`
fails with `EPERM`. This change will not be accepted upstream as `EPERM`
is not the right error for `close_range()` to be returning.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Fixes: #2580
2022-01-19 12:01:08 +00:00
Michael Catanzaro
b251a7cd18 gspawn: add new error message for open() failures
Reporting these as dup2() failures is bogus.
2021-12-14 13:45:39 -06:00
Michael Catanzaro
34de33a9bd gspawn: Check from errors from safe_dup2() and dupfd_cloexec()
Although unlikely, these functions can fail, e.g. if we run out of file
descriptors. Check for errors to improve robustness. This is especially
important now that I changed our use of dupfd_cloexec() to avoid
returning fds smaller than the largest fd in target_fds. An application
that attempts to remap to the highest-allowed fd value deserves at least
some sort of attempt at error reporting, not silent failure.
2021-12-14 13:45:39 -06:00
Michael Catanzaro
7d5bdff6d9 gspawn: Implement fd remapping for posix_spawn codepath
This means that GSubprocess will (sometimes) be able to use the
optimized posix_spawn codepath instead of having to fall back to
fork/exec.
2021-12-14 13:45:39 -06:00
Michael Catanzaro
f9780c6bee gspawn: fix fd remapping conflation issue
We currently dup all source fds to avoid possible conflation with the
target fds, but fail to consider that the result of a dup might itself
conflict with one of the target fds. Solve this the easy way by duping
all source_fds to values that are greater than the largest fd in
target_fds.

Fixes #2503
2021-12-14 13:45:39 -06:00
Michael Catanzaro
e2700c7638 gspawn: fix hangs when duping child_err_report_fd
In case child_err_report_fd conflicts with one of the target_fds, the
code here is careful to dup child_err_report_fd in order to avoid
conflating the two. It was a good idea, but evidently was not tested,
because the newly-created fd is not created with CLOEXEC set. This means
it stays open in the child process, causing the parent to hang forever
waiting to read from the other end of the pipe. Oops!

The fix is simple: just set CLOEXEC. This removes our only usage of the
safe_dup() function, so it can be dropped.

Fixes #2506
2021-12-14 13:36:26 -06:00
Michael Catanzaro
33f15d9dd0 gspawn: Improve error message when dup fails
This error message is no longer accurate now that we allow arbitrary fd
remapping.
2021-12-14 13:36:25 -06:00
Michael Catanzaro
ac8d1aa247 gspawn: use close_and_invalidate more 2021-12-14 13:36:24 -06:00
Philip Withnall
709df8eeb4 Merge branch 'docgen-fixes' into 'main'
Adapt documentation to gi-docgen

See merge request GNOME/glib!2206
2021-08-03 13:53:38 +00:00
Emmanuele Bassi
bed2da6cc2 docs: Break gtk-doc stanzas into paragraphs
Keep the first paragraph short, to act as a summary.
2021-08-02 16:00:12 +01:00
Simon McVittie
37e5dfd3a2 Merge branch 'close-range-cloexec' into 'main'
gspawn: Use CLOSE_RANGE_CLOEXEC if available

See merge request GNOME/glib!2184
2021-08-02 14:49:27 +00:00
Casper Dik
a75ffc5112 gspawn: safe_fdwalk for Solaris 11.4
Use F_NEXTFD/F_PREVFD for fdwalk when they are available.

Closes #2429
2021-08-01 12:52:34 -07:00
Casper Dik
790571a2cd gspawn: safe_closefrom for Solaris 11.3/11.4
When F_CLOSEFROM is defined, we know that closefrom() is signal safe.
2021-08-01 12:43:19 -07:00
Philip Withnall
b5556a2c68 gspawn: Use CLOSE_RANGE_CLOEXEC if available
It’s a new flag added to `close_range()` in kernel 5.11, which will
allow us to speed up setting `CLOEXEC` on ranges of file descriptors.

This currently happens in some situations when executing a new binary
with `GSpawn`.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2021-07-09 12:40:47 +01:00
Khem Raj
b71117d894 correctly use 3 parameters for close_range
libc implementation has 3 parameter e.g.
https://www.freebsd.org/cgi/man.cgi?query=close_range&sektion=2&format=html

Signed-off-by: Khem Raj <raj.khem@gmail.com>
2021-07-08 17:26:43 -07:00
Simon McVittie
b483013d02 spawn: Clarify the most common non-exit reason for process termination
A reader might think "how would a process terminate without an exit
status?", or equivalently, "what harm would it do if I assume every
termination has an exit status?" without this reminder that termination
with a signal is also reasonably common.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2021-06-15 14:33:17 +01:00
Simon McVittie
e0b6b8037d Distinguish more clearly between wait status and exit status
On Unix platforms, wait() and friends yield an integer that encodes
how the process exited. Confusingly, this is usually not the same as
the integer passed to exit() or returned from main(): conceptually it's
an integer encoding of this tagged union:

    enum { EXITED, SIGNALLED, ... } tag;
    union {
        int exit_status;         /* if EXITED */
        struct {
            int terminating_signal;
            bool core_dumped;
        } terminating_signal;    /* if SIGNALLED */
        ...
    } detail;

Meanwhile, on Windows, wait statuses and exit statuses are
interchangeable.

I find that it's clearer what is going on if we are consistent about
referring to the result of wait() as a "wait status", and the value
passed to exit() as an "exit status".

GSubprocess already gets this right: g_subprocess_get_status() returns
the wait status, while g_subprocess_get_exit_status() genuinely returns
the exit status. However, the GSpawn family of APIs has tended to
conflate the two.

Confusingly, g_spawn_check_exit_status() has always checked a wait
status, and it would not be correct to pass an exit status to it; so
let's deprecate it in favour of g_spawn_check_wait_status(), which
does the same thing that g_spawn_check_exit_status() always did.
Code that needs backwards-compatibility with older GLib can use:

    #if !GLIB_CHECK_VERSION(2, 69, 0)
    #define g_spawn_check_wait_status(x) (g_spawn_check_exit_status (x))
    #endif

Signed-off-by: Simon McVittie <smcv@collabora.com>
2021-06-15 14:33:14 +01:00
Simon McVittie
6c5a227bcc gmain: Add g_steal_fd() to API
This is basically glnx_steal_fd() from libglnx. We already had two
private implementations of it in GLib.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2021-03-22 11:48:10 +00:00
Philip Withnall
fd0b20d537 gspawn: Minor improvements to documentation formatting
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2021-02-16 13:44:00 +00:00
Philip Withnall
b31f3f5f80 gspawn: Add new g_spawn_async_with_pipes_and_fds() API
This is a simple wrapper around the new source/target FD mapping
functionality in `fork_exec()`.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Helps: #2097
2021-02-16 13:44:00 +00:00
Philip Withnall
c5f3ba7f01 gspawn: Avoid merged FDs being closed on exec()
If `stdout_fd` was set to (say) 6, and `stderr_fd` was set to 1, the
`set_cloexec()` call for setting up `stderr` would set the new `stdout`
for the forked process to be closed in the pending `exec()`.

This would cause the child process to error when writing to `stdout`.

This situation happens when using `G_SUBPROCESS_FLAGS_STDERR_MERGE`.

Add some conditions to prevent setting `CLOEXEC` in such cases.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Helps: #2097
2021-02-16 13:44:00 +00:00
Philip Withnall
f20f0d385e gspawn: Avoid custom FDs conflicting with the child_err_report_fd
It was previously possible to specify the FD number which
`child_err_report_fd` was assigned, as a target FD in the FD mapping set
up using `g_subprocess_launcher_take_fd()`.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Fixes: #2097
2021-02-16 13:44:00 +00:00
Philip Withnall
7be9767cc4 gspawn: Handle arbitrary FD passing and renumbering between fork/exec
This effectively moves some of the functionality of `GSubprocess`
(`g_subprocess_launcher_take_fd()`) into `g_spawn*()`, which should make
implementation a little simpler.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Helps: #2097
2021-02-16 13:44:00 +00:00
Philip Withnall
cddcd24b52 gspawn: Combine fork_exec() implementations
This is an internal change which won’t affect the public API. It should
introduce no functional changes, but simplifies the code a little.

The arguments from `fork_exec_with_pipes()` have been added to
`fork_exec_with_fds()`. `child_close_fds` has been dropped since it’s
now an implementation detail within the function.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Helps: #2097
2021-02-16 13:44:00 +00:00
Philip Withnall
63467c559e gspawn: Remove spurious blank lines
This introduces no functional changes.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2021-02-15 12:28:56 +00:00
Philip Withnall
12a627be55 gspawn: Reindent some arguments
They were indented incorrectly and I’m about to add some additional
ones.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
2021-02-15 12:28:56 +00:00
Thomas Haller
e864c6577a spawn: prefer allocating buffers on stack for small sizes to avoid valgrind leaks
We preallocate buffers that are used after forked. That is because
malloc()/free() are not async-signal-safe and must not be used between
fork() and exec().

However, for the child process that exits without fork, valgrind wrongly
reports these buffers as leaked.
That can be suppressed with "--child-silent-after-fork=yes", but it is
cumbersome.

Work around by trying to allocate the buffers on the stack. At
least in the common cases where the pointers are small enough
so that we can reasonably do that.

If the buffers happen to be large, we still allocate them on the heap
and the problem still happens. Maybe we could have also allocated them
as thread_local, but currently glib doesn't use that.

[smcv: Cosmetic adjustments to address review comments from pwithnall]
2021-01-31 13:37:13 +00:00
Simon McVittie
7ff0fb3af5 spawn: Don't set a search path if we don't want to search PATH
do_exec() and g_execute() rely on being passed a NULL search path
if we intend to avoid searching the PATH, but since the refactoring
in commit 62ce66d4, this was never done. This resulted in some spawn
calls searching the PATH when it was not intended.

Spawn calls that go through the posix_spawn fast-path were unaffected.

The deprecated gtester utility, as used in GTK 3, relies on the
ability to run an executable from the current working directory by
omitting the G_SPAWN_SEARCH_PATH flag. This *mostly* worked, because
our fallback PATH ends with ".". However, if an executable of the
same name existed in /usr/bin or /bin, it would run that instead of the
intended test: in particular, GTK 3's build-time tests failed if
ImageMagick happens to be installed, because gtester would accidentally
run display(1) instead of testsuite/gdk/display.

Fixes: 62ce66d4 "gspawn: Don’t use getenv() in async-signal-safe context"
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=977961
2021-01-28 16:49:06 +00:00
Philip Withnall
23f1a31923 gspawn: Handle ENOSYS from close_range()
It’s possible that GLib will eventually be compiled against a version of
libc which supports `close_range()` (hence `HAVE_CLOSE_RANGE` will be
defined), but then run against an older kernel which doesn’t support it.
In this case, we want to fall back to `fdwalk()`, which should work on
such systems.

This is what cpython does: 3529718925/Python/fileutils.c (L2227)

Spotted by Allison Karlitskaya in !1688.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2020-12-03 14:30:29 +00:00
Emmanuel Fleury
76426c0158 Rewriting the G_GNUC_NORETURN into G_NORETURN macros everywhere 2020-11-25 11:34:05 +00:00
Matt Rose
b23811a234 add __APPLE__ to the list of operating systems that can use sysconf() to get open file limits 2020-11-17 14:42:06 -05:00
Philip Withnall
9f8ccee65f gspawn: Use close_range() if available to close FDs between fork/exec
It’s landed in kernel 5.9: http://lkml.iu.edu/hypermail/linux/kernel/2008.0/02649.html

Note, this is untested because I currently don’t have kernel 5.9. We can
fix anything up if it breaks once the new syscall is wrapped in glibc.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2020-10-12 18:10:45 +01:00
Philip Withnall
f0e74a97e7 gspawn: Handle error opening /dev/null
This is very unlikely to happen, but add error handling to mirror the
other calls to `safe_open()`, and shut Coverity up.

Coverity CID: #1430611

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2020-10-05 12:31:28 +01:00
Emmanuel Fleury
d49586cfa3 Fix signedness error in glib/gspawn.c
glib/gspawn.c:2252:16: error: comparison of integer expressions of
  different signedness: ‘int’ and ‘gsize’ {aka ‘long unsigned int’}

 2252 |   if (argc + 2 > argv_buffer_len)
      |                ^
2020-09-14 10:11:44 +02:00
Philip Withnall
7cd67c935f gspawn: Add sysprof trace support for spawning
Use this to replace the much-hated `g_debug()` which told people that
`posix_spawn()` (the fast path) wasn’t being used for various reasons.

If people want to make their process spawning faster now, they’ll have
to use a profiling tool like sysprof to check their program’s
performance. Shocking.

I think I was wrong to put this `g_debug()` in there in the first place
— it hasn’t served its purpose of making people speed up their spawn
paths to use `posix_spawn()`, it’s only cluttered up logs and frustrated
people.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
2020-07-07 11:17:10 +01:00
Philip Withnall
dd36248f9e gspawn: Don’t use malloc() when running a binary under /bin/sh
Allocate a working buffer before calling `fork()` to avoid calling
`malloc()` in the async-signal-safe context between `fork()` and
`exec()`, where it’s not safe to use.

In this case, the buffer is used to assemble a wrapper around `argv` so
it can be run under `/bin/sh`.

See `man 7 signal-safety`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Fixes: #2140
2020-06-23 12:03:30 +01:00
Philip Withnall
cf5af28169 gspawn: Don’t use malloc() when searching for a binary
Allocate a working buffer before calling `fork()` to avoid calling
`malloc()` in the async-signal-safe context between `fork()` and
`exec()`, where it’s not safe to use.

In this case, the buffer is used to assemble elements from `PATH` with
the binary from `argv[0]` to try executing them.

See `man 7 signal-safety`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #2140
2020-06-23 12:03:30 +01:00
Philip Withnall
62ce66d4e7 gspawn: Don’t use getenv() in async-signal-safe context
Query the environment before calling `fork()` so that it doesn’t have to
be called in the async-signal-safe context between `fork()` and
`exec()`.

See `man 7 signal-safety`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #2140
2020-06-23 12:03:30 +01:00
Philip Withnall
84f188ae24 gspawn: Don’t use getrlimit() or sysconf() in async-signal-safe context
They’re not safe to call in an async-signal-safe context on Linux.
`sysconf()` is safe to call on FreeBSD and OpenBSD (at least), so
continue doing that.

This will reduce performance in the (already low performance) fallback
case where `/proc` is inaccessible to a forked process on Linux, while
spawning a subprocess.

See `man 7 signal-safety`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #2140
2020-06-23 12:03:30 +01:00
Philip Withnall
1051bfe11e gspawn: Don’t use g_assert() in async-signal-safe context
Use the error handling infrastructure which already exists for other
failures in the async-signal-safe context.

`g_assert()` is unlikely to have caused problems in practice because it
is only async-signal-unsafe when the assertion condition fails.

See `man 7 signal-safety`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #2140
2020-06-23 12:03:30 +01:00
Philip Withnall
33948929df gspawn: Don’t use g_ascii_isdigit() in async-signal-safe context
While `g_ascii_isdigit()` *is* currently async-signal-safe, it’s going
to be hard to remember to keep it that way if the implementation changes
in future.

It seems more robust to just reimplement it here, given that it’s not
much code.

See `man 7 signal-safety`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #2140
2020-06-23 12:03:30 +01:00
Philip Withnall
6f46294227 gspawn: Don’t use g_close() in async-signal-safe context
Use normal `close()` instead, which is guaranteed to be
async-signal-safe.

See `man 7 signal-safety`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #2140
2020-06-23 12:03:30 +01:00
Philip Withnall
0e05ef7750 gspawn: Audit for async-signal-safety
Functions called between `fork()` and `exec()` have to be
async-signal-safe.

Add a comment to each function which is called in that context, and
`FIXME` comments to the non-async-signal-safe functions which end up
being called as leaves of the call graph.

The following commits will fix those `FIXME`s.

See `man 7 signal-safety`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #2140
2020-06-23 12:02:13 +01:00
Philip Withnall
a63efa4291 tree: Fix various ableist language
In almost all cases, rewording the documentation/comments made things
more specific and a little clearer.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

See: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1544#note_846645
2020-06-23 10:49:44 +01:00
Timm Bäder
e5ab441b0d Replace fallthrough comments with G_GNUC_FALLTHROUGH
It's safer to do it this way and since we have G_GNUC_FALLTHROUGH now, w
e might as well replace the fallthrough comments.
2020-03-04 11:21:17 +01:00