Skip to content

posix_spawn() - #140

Draft
dscho wants to merge 28 commits into
git-for-windows:mainfrom
dscho:posix_spawn-gfw
Draft

posix_spawn()#140
dscho wants to merge 28 commits into
git-for-windows:mainfrom
dscho:posix_spawn-gfw

Conversation

@dscho

@dscho dscho commented Aug 14, 2026

Copy link
Copy Markdown
Member

No description provided.

jeremyd2019 and others added 23 commits August 14, 2026 10:04
This will allow Cygwin to examine and handle the posix_spawnattr_t and
posix_spawn_file_actions_t arguments itself.

Cherry-picked-from: 756669312c (posix_spawn: factor out structs to an internal header, 2025-06-17)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Currently just a couple of tests of error conditions, but I have more
tests to add.

Cherry-picked-from: 6df7cdc54b (Cygwin: testsuite: add posix_spawn tests, 2025-06-17)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Given the limited binaries available (sh, ls, sleep), use sh -c true

Cherry-picked-from: 0139b7ae69 (Cygwin: testsuite: test posix_spawnp, 2025-06-18)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Test both that SIG_IGN and sigprocmask are inherited by default, and
that posix_spawnattr options can prevent this.

Cherry-picked-from: d33a13e5c1 (Cygwin: testsuite: test signal mask and ignore options., 2025-06-19)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
There are still more that could be tested, such as (f)chdir.

Cherry-picked-from: 908e644b7d (Cygwin: testsuite: test posix_spawn_file_actions., 2025-06-19)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Also test their interaction with addopen, as opens added subsequent to a
chdir need to be relative to that new cwd.

In order for the tests to compile on Linux, define O_SEARCH to O_PATH if
O_SEARCH is not defined, and use the *chdir_np names instead of the
now-standardized *chdir.

Cherry-picked-from: 922719ba36 (Cygwin: testsuite: test posix_file_actions_add(f)chdir, 2025-06-24)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This is required on Linux.

Cherry-picked-from: 1b60f4861b (Cygwin: testsuite: include sys/stat.h for chmod in posix_spawn/errors.c, 2025-06-26)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This program is currently meant to test standard file handles and
current working directory (since these are settable via posix_spawn),
but could be extended to add additional checks if other cygwin-to-win32
process properties need to be tested.

Cherry-picked-from: 2af1914b6a (Cygwin: testsuite: add a mingw test program to spawn, 2025-06-25)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Test CWD and redirection of standard handles.

Cherry-picked-from: 9deb0bc3b5 (Cygwin: testsuite: test posix_spawn of a non-Cygwin executable., 2025-06-25)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This is a legal (if non-obvious) thing to do, so test it.

Cherry-picked-from: a4c2540d0f (Cygwin: testsuite: test passing directory fd to child, 2025-06-26)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This is apparently relative to the new cwd, but my implementation is
currently treating it as relative to the parent's cwd, so it's worth
testing.

Cherry-picked-from: b4e4cb4c86 (Cygwin: testsuite: test relative path to exe after addchdir., 2025-06-26)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
According to the POSIX documentation, "when the new process image is
executed, any file descriptor (from this new set) which has its
FD_CLOEXEC flag set shall be closed (see posix_spawn())."  The "new set"
is after processing the file actions, so if addopen had the O_CLOEXEC
flag set the descriptor should be closed after the exec.

The adddup2 docs, by contrast, specify that the flag should be cleared,
even if dup2 wouldn't have done so due to the specified file descriptors
being equal.  Add a comment to that effect.

Addresses: https://sourceware.org/pipermail/newlib/2025/021968.html
Fixes: c7c1a1c ("2013-10-01  Petr Hosek  <phosek@chromium.org>")
Cherry-picked-from: fb092671a7 (posix_spawn: preserve FD flags when processing FAE_OPEN, 2025-07-08)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This was previously not handled correctly by newlib.

Cherry-picked-from: 1abf10e225 (Cygwin: testsuite: test posix_spawn_file_actions_addopen with O_CLOEXEC, 2025-07-08)
Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
stdin and stdout were alreadly allowed for popen, but implementing
posix_spawn in terms of spawn would require stderr as well.

Replace the conveniently-located 4 filler bytes with int __stderr so
that child_info_spawn doesn't have to grow.

Introduce a struct for passing additional args to ch_worker.spawn, since
there are getting to be quite a lot of additional args.

Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This will be used by posix_spawn_fileaction_add_(f)chdir.

The int cwdfd is placed such that it fits into space previously unused
due to alignment in the cygheap_exec_info class.

This uses a file descriptor rather than a path both because it is easier
to marshal to the child and because this should protect against races
where the directory might be renamed or removed between addfchdir and
the actual setting of the cwd in the child.

Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This will allow checking for and optimizing cases that can easily be
implemented using ch_spawn instead of using a full fork/exec.

Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Currently just file actions are supported in the fast path, open/dup2
with a target fd of stdin/out/err, close of any fd, and chdir and
fchdir.  These were chosen as the least-common-denominator of
functionality supported for any child process, as they are allowed to be
specified via CreateProcess.  They also happen to be the most common
file operations, performed by the likes of GNU make, ninja, LLVM, and rust.

For other attributes or actions, fall back to the newlib implementation
of posix_spawn (which uses fork/exec to set everything up in the child
process).

Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
the sigmask was already a member of the child_info, so this just needed
an arg to allow overriding the value copied from cygheap.  The signal
handlers are referenced by two pointers, global_sigs which is used by
the signal routines, and cygheap->sigs which is only used during process
launch and startup.  Temporarily replace cygheap->sigs with a copy that
has the requested signals reset to default while spawning the child.

Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Tweak proc_subproc PROC_ADD_CHILD to only initialize vchild->pgid if
it's not already set.

The error checking of setpgid is lacking with respect to the POSIX
standard, but this code replicates what setpgid does.

This attribute is used by ninja, so is worth adding to the fast path.

Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Commit 8860524 (Cygwin: fix child getting another pid after
spawnve, 2019-02-08) uses INVALID_HANDLE_VALUE to tell
pinfo::thisproc() that a spawned process already received its Cygwin
PID from its parent.  Unlike a genuinely exec'ed process, that process
still has to create and maintain its own winpid symlink.

Commit 1b86dd7 (Cygwin: make sure exec'ed process exists early in
process list, 2022-05-19) taught the exec'ing process to maintain that
symlink across exec.  As part of that change, it used the truthiness of
the handle argument to identify exec'ed processes and stopped them from
creating the symlink themselves.

INVALID_HANDLE_VALUE is truthy, too, so this also unintentionally
stopped spawned Cygwin processes from creating their winpid symlink.
Such a process has a valid internal PID and process group, but is
missing from winpids enumeration.  Consequently, ps does not list it
as a Cygwin process and kill(-pgid, ...) returns ESRCH.  In particular,
this prevents terminal-generated signals such as Ctrl+C from reaching
a foreground process started via posix_spawn().

Distinguish the spawn sentinel from a real exec handle and create the
winpid symlink for spawned processes.  Actual exec'ed processes keep
the behavior introduced in 1b86dd7: their exec'ing process
creates and preserves the symlink while the new image starts.

Fixes: 1b86dd7 ("Cygwin: make sure exec'ed process exists early in process list")
Assisted-by: Claude Sonnet 5 and GPT 5.6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
cygwin: testsuite: link test DLLs against libmsys-2.0
posix_spawn() assigns the Cygwin PID and process group before starting
the child.  The spawned process must also create its winpid symlink so
that winpids enumeration can map the Windows PID back to that Cygwin
process-table entry.

Without that mapping, getpgid() in the child can report the requested
process group while group operations such as kill(-pgid, ...) fail with
ESRCH.  This also prevents terminal-generated signals from reaching a
foreground process group.

Spawn a copy of the test in a new process group, verify both the stored
pgid and group enumeration via kill(-pgid, 0), then terminate the child
through the process group.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
POSIX spawn file actions transform a logical copy of the child's
descriptor table in insertion order.  Later actions must observe every
open, dup2, and close performed by earlier actions, rather than the
parent's unchanged descriptor table.

Exercise interactions that the fast path's separate stdfds array and
deferred-close handling do not currently represent: a same-descriptor
dup2 after addopen, dup2 from a descriptor closed by an earlier action,
fchdir from a closed descriptor, and fchdir through a directory opened
into a standard descriptor by an earlier action.

These cases also verify that same-descriptor dup2 preserves the logical
open file while applying dup2's descriptor-flag semantics, and that
action failures are returned by posix_spawn() without executing the
child image.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dscho dscho self-assigned this Aug 14, 2026
The POSIX-spawn error test expects a file created by mkstemp() to be
non-executable until chmod() changes its mode, and therefore expects
posix_spawn() to return EACCES.

MSYS2 mounts filesystems with noacl by default.  POSIX execute bits are
not enforced by Windows ACLs in that configuration, so process creation
gets as far as examining the file format and returns ENOEXEC instead.
This is the same deliberate platform distinction behind the existing
MSYS2 permission-test XFAILs.

Skip only the pre-chmod EACCES assertion when building the test for
MSYS2.  Cygwin and other platforms retain the original check, and the
subsequent ENOEXEC assertion still exercises spawning the executable
file on MSYS2.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dscho and others added 4 commits August 14, 2026 10:26
The fast path intentionally handles open and dup2 targets only for
stdin, stdout, and stderr.  Targets above stderr must therefore remain
correct through the newlib fork/exec fallback.

Cover addopen and adddup2 for a higher descriptor, including addopen
with O_CLOEXEC.  Also verify that closing an already closed descriptor
is harmless, as required for spawn file actions.

Strengthen the ordered same-descriptor dup2 test by opening the logical
descriptor with O_CLOEXEC first.  The later dup2 must leave the opened
file in place while clearing FD_CLOEXEC.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The posix_spawn fast path processes file actions before it knows whether
the child is a Cygwin or native executable.  Ordered-action coverage
must therefore exercise both child types.

For the native helper, verify that same-descriptor dup2 keeps a file
opened by an earlier action and clears O_CLOEXEC.  Also verify that
fchdir resolves a directory descriptor opened into stdin by an earlier
action.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The posix_spawn fast path represents replacement standard descriptors
in spawn_worker_args::stdfds and implements close actions by temporarily
setting FD_CLOEXEC in the parent.  It did not, however, preserve a
logical child descriptor table while processing later actions.

As a consequence, later actions could resolve a descriptor in the
unchanged parent table after an earlier action had closed it.  A
same-descriptor dup2 after addopen discarded the opened file, and
fchdir could not use a directory opened into a standard descriptor by
an earlier action.  These behaviors violate the requirement that file
actions transform the child's descriptor table in insertion order.

Track logical closes separately from the parent descriptor table and
resolve dup2 and fchdir sources through both that state and the mapped
standard descriptors.  Opens and dup2 operations replace the logical
target, while close makes later references fail with EBADF.

Move temporary standard and cwd descriptors above stderr immediately
so subsequent actions cannot overwrite them accidentally.  Preserve
the flags created by addopen, clear FD_CLOEXEC for adddup2 including
the same-descriptor case, and continue restoring every temporary parent
FD_CLOEXEC change on exit.

Pre-scan the action list and select the newlib fork/exec fallback before
changing descriptor flags when an open or dup2 target above stderr is
requested.  Allocate the closed-descriptor set with checked heap
storage and release it on every fast-path and fallback exit.

Fixes: 9dd807043b28 ("Cygwin: add fast-path for posix_spawn(p)")
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The posix_spawn process-group support adds pgid to spawn_worker_args,
which changes the child-info layout shared between the spawning process
and a Cygwin child.

CURR_CHILD_INFO_MAGIC still described the preceding layout.  The build
therefore warned that the generated magic was 0x99ae8af0 rather than
0xd0e19b15.

Update the constant to the generated value so incompatible parent and
child layouts are detected correctly.

Fixes: 0802d9184bc8 ("Cygwin: add pgroup support to posix_spawn fast path")
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants