Skip to content

testing, system, netutils: do not depend on fork() where it is absent or unneeded - #3673

Open
casaroli wants to merge 2 commits into
apache:masterfrom
casaroli:fork-semantics-ostest
Open

testing, system, netutils: do not depend on fork() where it is absent or unneeded#3673
casaroli wants to merge 2 commits into
apache:masterfrom
casaroli:fork-semantics-ostest

Conversation

@casaroli

@casaroli casaroli commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Four places in apps call fork() from code that is compiled unconditionally. That is fine only for as long as every architecture provides it, and NuttX is about to stop doing so: apache/nuttx#19562 splits fork() into three primitives, after which CONFIG_ARCH_HAVE_FORK announces POSIX fork() specifically and is off until an architecture implements it. Those four then fail to build, and the NuttX PR's CI cannot go green, because NuttX PRs build against apps master.

This PR removes that blockage and nothing else. It introduces no new configuration symbols, no compatibility layer, and no test changes.

It splits along a real line, one commit each. Two of the four are tests that genuinely exercise fork() and so must stop being built where it is absent. The other two never wanted fork() at all — they reached for it only to put work in the background, and each has a NuttX-native way to do that, so they need no fork primitive and no new dependency.

Tests that really do need fork()

system/libuvtest-fork.c and test-pipe-close-stdout-read-stdin.c are filtered out of the test-*.c glob where CONFIG_ARCH_HAVE_FORK is unset, in both the Makefile and the CMake build. Nothing is lost even then, because every test the two files define is already excluded from the task list on NuttX: 0001-libuv-port-for-nuttx.patch widens the two #ifndef _WIN32 guards in test/test-list.h to #if !defined(_WIN32) && !defined(__NuttX__), and those guards cover all nine fork_* entries and pipe_close_stdout_read_stdin — the complete set both files define. They are compiled today but never run.

testing/ltp — the open_posix_testsuite is filtered through LTP's existing BLACKWORDS mechanism, which already drops tests for absent features and is already conditioned on configuration symbols. The pattern [^v_]fork( spares vfork() and task_fork(). Where fork() is not provided this drops 278 of 1943 test files; where it is provided — everywhere, today — nothing is dropped. That loss is the honest price of the split: those tests exercise fork() and cannot link without it. They return per architecture as real fork() lands.

Callers that only wanted to run in the background

netutils/dropbear — the port already routes every fork-then-exec through vfork(), and has done since it was written: sysoptions.h selects DROPBEAR_VFORK when HAVE_FORK is undefined, and the port leaves it undefined, so spawn_command() in dbutil.c and both call sites in scp.c take the vfork() branch. The single exception is the daemon() fallback that compat.c compiles under #ifndef HAVE_DAEMON, which calls fork() directly and bypasses that switch. NuttX has daemon() in libs/libc/unistd/lib_daemon.c and declares it in unistd.h, so the fallback is redundant — this defines HAVE_DAEMON alongside the HAVE_STRLCAT and HAVE_STRLCPY entries that are in nuttx_config.h for exactly the same reason. The code was unreachable in any case: the port hands svr_getopts() an argv containing -F, so svr_opts.forkbg is always zero and dropbear never calls daemon() at all.

testing/drivers/nand_sim — forked so the parent could return to the shell while the child registered the MTD device and slept forever. Nothing from before the fork() is used after it, so the child is a self-contained entry point and task_create() expresses that directly; the emulator body moves into nand_sim_daemon() unchanged. TESTING_NAND_SIM therefore needs no fork dependency and its Kconfig is untouched, so sim:nand and sim:mnemofs keep working whatever ARCH_HAVE_FORK is set to — no defconfig edits are needed on either side, and no coverage is lost at any point in the merge order.

Two other fork() mentions need nothing: games/NXDoom's is inside #if 0 /* UNUSED */, and system/syslogd already uses posix_spawn().

Impact

Against today's master this is a no-op, with nothing left over. CONFIG_ARCH_HAVE_FORK is set on every architecture, so neither the LTP filter nor the libuv filter drops anything; nand_sim and dropbear behave identically either way, since task_create() and daemon() do not depend on the symbol at all. Every configuration builds exactly the same set of files it does today.

After apache/nuttx#19562, ARCH_HAVE_FORK goes off until a per-architecture PR turns it back on. The two test suites stop being built rather than failing to link; nand_sim and dropbear are unaffected.

interpreters/bas is deliberately left alone. Its SHELL and EDIT statements want the same treatment, but checkpatch.sh checks the whole of any file a patch touches, and bas_statement.c produces 1681 pre-existing findings before this patch is applied at all — a one-newline commit against master fails CI identically. Migrating BAS has to follow a style cleanup of that file, and neither belongs here. The consequence is small: CONFIG_EXAMPLES_BAS_SHELL is EXPERIMENTAL and already depends on ARCH_HAVE_FORK, so it becomes unselectable rather than misbehaving.

The companion test work — splitting ostest's fork test into task_fork, vfork and fork — is #3685, which depends on apache/nuttx#19562 and must merge after it.

Testing

Host: macOS 15 (Darwin 25.5.0) on Apple Silicon. QEMU 11.0.3, xPack riscv-none-elf-gcc 14.2.0-3, Arm GNU arm-none-eabi-gcc 14.2.Rel1.

Style

../nuttx/tools/checkpatch.sh -c -u -m -g <base>..HEAD, the exact command .github/workflows/check.yml runs — ✔️ All checks pass, with codespell, cvt2utf, cmake-format and nxstyle all installed.

Verification

This PR was restructured after review: the test work it originally carried has moved to #3685, leaving only the changes needed to unblock apache/nuttx#19562, and the branch has been rebased onto current master. The functional matrix is being re-run against the restructured branch and will be posted here; the previous revision's results are in this PR's history.

The three configurations CI reported failing on apache/nuttx#19562sim:nand, sim:mnemofs and sim:dropbear — were rebuilt against that PR's branch, where CONFIG_ARCH_HAVE_FORK is genuinely off, under Linux GCC rather than the development host's compiler.

At this PR's base the two reported diagnostics reproduce at exactly the locations CI gives:

dropbear/src/compat.c:162:17: implicit declaration of function 'fork'
nand_sim_main.c:145:9:        implicit declaration of function 'fork'

With this PR applied both are gone — zero occurrences across all three configurations — and sim:dropbear builds and links clean with no warnings at all. So the change is measured against the real failure rather than an approximation of it.

By inspection the change is a no-op against today's master: CONFIG_ARCH_HAVE_FORK is set on every architecture, so the LTP filter drops nothing, and the two libuv files removed from the glob are already absent from the task list on NuttX.

@jerpelea jerpelea left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please replace
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
with
Assisted-by: Claude Opus 5 (1M context) noreply@anthropic.com

@casaroli
casaroli force-pushed the fork-semantics-ostest branch 2 times, most recently from 536349e to 2a694fe Compare July 30, 2026 09:40
@casaroli
casaroli requested a review from jerpelea July 30, 2026 09:59
@casaroli
casaroli marked this pull request as ready for review July 30, 2026 09:59
jerpelea
jerpelea previously approved these changes Jul 30, 2026
Comment thread testing/ltp/CMakeLists.txt Outdated
Comment thread testing/ostest/ostest.h Outdated
Comment thread testing/ostest/ostest.h Outdated
Comment thread testing/ostest/Makefile Outdated
Comment thread testing/ostest/Makefile Outdated
Comment thread testing/ostest/fork.c Outdated
Comment thread testing/ostest/vfork.c Outdated
acassis
acassis previously approved these changes Jul 31, 2026
@casaroli

Copy link
Copy Markdown
Contributor Author

Follow up pr #3685

@casaroli casaroli closed this Aug 1, 2026
@casaroli casaroli reopened this Aug 1, 2026
acassis
acassis previously approved these changes Aug 1, 2026
Comment thread interpreters/python/Makefile Outdated
Comment thread netutils/libwebsockets/lws_config_private.h Outdated
Comment thread testing/drivers/nand_sim/Kconfig Outdated
Comment thread testing/fs/fdsantest/fdsantest_simple.c Outdated
Comment thread testing/fs/fdsantest/fdsantest_simple.c Outdated
Comment thread testing/ostest/CMakeLists.txt Outdated
Comment thread testing/ostest/Makefile Outdated
Comment thread testing/ostest/Makefile Outdated
Comment thread testing/ostest/ostest.h Outdated
Comment thread testing/ostest/ostest.h Outdated
@casaroli
casaroli force-pushed the fork-semantics-ostest branch from 7aac6c5 to e295115 Compare August 2, 2026 10:35
@casaroli casaroli changed the title testing/ostest: split the fork test into task_fork, vfork and fork testing, system: do not build tests that call fork() where it is absent Aug 2, 2026
@github-actions github-actions Bot removed the Size: L label Aug 2, 2026
@casaroli

casaroli commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

@xiaoxiang781216 I rewrote the PR so most of the complexity is moved to #3685

acassis
acassis previously approved these changes Aug 2, 2026
Comment thread system/libuv/Makefile Outdated
CSRCS += $(wildcard libuv/test/test-*.c)
# test-fork.c and test-pipe-close-stdout-read-stdin.c call fork(). Every
# test they define is already excluded from the task list on NuttX by
# 0001-libuv-port-for-nuttx.patch, so they are dead code here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we keep fork related code if the arch support the fork

@casaroli casaroli Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should try to enable this after we have real fork() working. We would need to fix the patch.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if so, why not check CONFIG_ARCH_HAVE_FORK before removing them from build.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — both the Makefile and CMakeLists.txt now drop the two files only when CONFIG_ARCH_HAVE_FORK is unset, so on every architecture today they are built exactly as before.

On the first question, though: keeping them where the arch supports fork() does not actually recover any coverage, because the exclusion is not keyed on fork availability. 0001-libuv-port-for-nuttx.patch widens the two #ifndef _WIN32 guards in test/test-list.h to #if !defined(_WIN32) && !defined(__NuttX__), and those guards cover the complete set of tests the two files define — all nine fork_* entries (fork_timer, fork_socketpair, fork_socketpair_started, fork_signal_to_child, fork_signal_to_child_closed, fork_fs_events_child, fork_fs_events_child_dir, fork_fs_events_file_parent_child, fork_threadpool_queue_work_simple) plus pipe_close_stdout_read_stdin. On NuttX they compile and link but are never entered.

I have taken the suggestion anyway, because gating is the better shape for two reasons that do not depend on that argument: it keeps this PR a strict no-op on master rather than nearly one, and it matches what testing/ltp does a few lines away instead of asking the reader to accept a claim about a vendored patch.

Verified on sim:citest under Linux GCC, building the same tree twice against a NuttX with and without the symbol: with CONFIG_ARCH_HAVE_FORK=y 170 libuv test files are compiled including both of these, and with it unset 168 are compiled and these two are the only ones missing.

@casaroli
casaroli force-pushed the fork-semantics-ostest branch 2 times, most recently from 3cab7a1 to 065682a Compare August 2, 2026 16:51
@casaroli casaroli changed the title testing, system: do not build tests that call fork() where it is absent testing, system, netutils: do not depend on fork() where it is absent or unneeded Aug 2, 2026
Two places call fork() from code that is compiled unconditionally, which is
fine only for as long as every architecture provides it.  NuttX is splitting
fork() into three primitives -- see apache/nuttx#19562 -- after which
ARCH_HAVE_FORK announces POSIX fork() specifically, and is off until an
architecture implements it.  Both then fail to link.  Each is dropped only
where ARCH_HAVE_FORK is unset, so builds that have fork() are unaffected.

system/libuv: test-fork.c and test-pipe-close-stdout-read-stdin.c are
filtered out of the test-*.c glob.  Nothing is lost even where they are
dropped: every test they define is already excluded from the task list on
NuttX by 0001-libuv-port-for-nuttx.patch, which extends the _WIN32 guards
around them to __NuttX__ -- all nine fork_* entries and
pipe_close_stdout_read_stdin.  They are compiled today but never run.

testing/ltp: the open_posix_testsuite is filtered through LTP's existing
BLACKWORDS mechanism, which already drops tests for absent features and is
already conditioned on configuration symbols.  The pattern spares vfork()
and task_fork().  Where fork() is absent this drops 278 of 1943 test files;
those tests exercise fork() and cannot link without it, and they return per
architecture as fork() lands.

Against today's master this is a no-op: ARCH_HAVE_FORK is set everywhere, so
neither filter drops anything.  It is part of what lets the NuttX side build
against apps master.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
…ground.

Neither of these wants fork() semantics.  Both reach for fork() only to put
work in the background, and each has a NuttX-native way to do that, so
neither needs a fork primitive at all -- which matters once apache/nuttx#19562
makes ARCH_HAVE_FORK conditional on the architecture implementing POSIX
fork().

netutils/dropbear: the port already routes every fork-then-exec through
vfork(), because sysoptions.h selects DROPBEAR_VFORK when HAVE_FORK is
undefined and the port leaves it undefined.  spawn_command() in dbutil.c and
both call sites in scp.c follow that switch.  The one exception is the
daemon() fallback that compat.c compiles under #ifndef HAVE_DAEMON, which
calls fork() directly and bypasses it.  NuttX provides daemon() in
libs/libc/unistd/lib_daemon.c and declares it in unistd.h, so the fallback is
redundant; define HAVE_DAEMON alongside the HAVE_STRLCAT and HAVE_STRLCPY
entries that are there for exactly the same reason.  The code was unreachable
in any case -- the port hands svr_getopts() an argv containing -F, so
svr_opts.forkbg is always zero and dropbear never calls daemon() at all.

testing/drivers/nand_sim: forked so that the parent could return to the shell
while the child registered the MTD device and slept forever.  Nothing from
before the fork is used after it, so the child is a self-contained entry
point, and task_create() expresses that directly.  The emulator body moves
into nand_sim_daemon() unchanged.  TESTING_NAND_SIM therefore needs no fork
dependency, and the two sim configurations that enable it keep working
whatever ARCH_HAVE_FORK is set to.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
@casaroli
casaroli force-pushed the fork-semantics-ostest branch from 065682a to 63b94b4 Compare August 2, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants