nuttx/include/nuttx
Marco Casaroli a229e8b87e sched/arch/libc: give fork(), vfork() and task_fork() separate semantics
NuttX implemented fork() and vfork() as the same function.  Both were libc
wrappers around a single up_fork() syscall; vfork() differed only by a
trailing waitpid().  Underneath, the child joined the parent's address
environment -- the same addrenv_join() that pthread_create() uses -- and got
a private copy of the stack.  So the child shared .data, .bss and the heap
with its parent and ran concurrently with it.

That is not fork().  It is vfork()-with-a-private-stack under fork()'s name,
and the history says so: today's fork() is NuttX's old vfork(), renamed in
c33d1c9c97 (2023) without any change of behaviour.  The failure was silent --
a program written against POSIX fork() compiled, ran, and had its child's
writes land in the parent's variables.

Separate them into three primitives, chosen by which function the caller
called rather than by what the hardware happens to be:

  fork()       child gets its own copy of the parent's memory at the same
               virtual addresses; runs concurrently.  Only where an address
               environment can be duplicated -- elsewhere it is not declared
               at all, so calling it is a build error naming the function.
  vfork()      child shares the parent's memory; parent suspended until the
               child _exit()s or exec()s.  Implementable everywhere.
  task_fork()  the historical behaviour under an honest name: shares memory,
               private stack copy, both running.  Non-POSIX, in sched.h.

Below libc there are now three syscalls -- up_task_fork(), up_vfork() and
up_fork().  The per-arch register snapshot is common to all three; each
architecture's entry points share one sequence and differ only in a
FORK_TYPE_* selector (include/nuttx/fork.h) handed to nxtask_setup_fork(),
which is the single place the memory semantics are decided.

The vfork() parent suspension moves out of libc into nxtask_start_vfork(),
released from nxsched_release_tcb().  Two things follow: the parent is
resumed at exec(), since exec_swap() has already handed the child's pid to
the loaded program by the time the vfork stub exits, and vfork() no longer
depends on CONFIG_SCHED_WAITPID.

Releasing there requires one fix in nxtask_exit().  It raises rtcb->lockcount
directly rather than through sched_lock() while it tears the TCB down, so the
nxsem_post() that wakes the vfork() parent queues it on g_pendingtasks -- and
the matching raw lockcount-- does not merge that list the way sched_unlock()
would, leaving the parent stranded with nothing left to move it off.  A
nxsched_merge_pending() after the decrement publishes it.  The call is a
no-op while pre-emption is still disabled, and up_exit() re-reads this_task()
afterwards, so a change of the ready-to-run head is honoured.  Without it
vfork() deadlocks on any configuration where no other task happens to call
sched_unlock() afterwards -- rv-virt:nsh64 and rv-virt:pnsh64, for instance,
where NSH is blocked in waitpid() holding the lock.

fork() is built on a new addrenv_fork(), backed by an up_addrenv_fork() hook
that duplicates an address environment into freshly allocated pages mapped at
the same virtual addresses -- unlike up_addrenv_clone(), which copies only
the representation and leaves both pointing at the same page tables.  The
child then adopts the parent's stack geometry rather than being given a
relocated copy: a pointer to a stack local taken before fork() must name the
same object in the child that it named in the parent, and the parent's stack
is already in the duplicate, with its contents, at the parent's address.

No architecture implements up_addrenv_fork() yet, so this commit leaves
fork() unavailable everywhere.  That is the intended state.  It withdraws
fork() from ARCH_ARM, flat ARCH_ARM64, ARCH_RISCV, ARCH_SIM and ARCH_X86_64,
where until now it named the sharing primitive; per-architecture patches
restore it, with POSIX semantics, as up_addrenv_fork() lands.  Nothing is
lost in the meantime: task_fork() is that same sharing primitive under its
own name, and CONFIG_FORK_IS_TASK_FORK (default n) aliases fork() back to it
for legacy code, on exactly the configurations that had fork() before.

Kconfig: ARCH_HAVE_TASK_FORK and ARCH_HAVE_VFORK inherit ARCH_HAVE_FORK's
select lines, conditions included, so no configuration gains machinery;
ARCH_HAVE_FORK is redefined to mean "can provide POSIX fork() semantics" and
derives from the new ARCH_HAVE_ADDRENV_FORK.

There is one deliberate departure from "verbatim".  ARCH_ARM selected the
fork family unconditionally, BUILD_KERNEL included, and that has never
worked:  on a kernel build the architecture's fork entry point sees the
kernel's return address and stack pointer rather than the caller's, so the
child resumes at a kernel address.  On qemu-armv7a:knsh master faults in
ostest's task_fork case with "Child did not run" and then a data abort;
without the condition this change faults the same way through vfork().
ARCH_ARM64 and ARCH_X86_64 already carried "if !BUILD_KERNEL" for exactly
this reason -- ARM was the outlier.  Conditioning it turns a runtime fault
into an honest absence, which is the whole point of the change; arch/arm
takes the condition off again in the patch that adds its saved-syscall-frame
path.  Only the MMU-capable ARM ports are affected, since Cortex-M cannot
build BUILD_KERNEL at all.

Also fixes two latent syntax errors found on the way: a missing comma in
riscv_fork.c and mips_fork.c, both in *_FRAMEPOINTER && !SAVE_GP branches
that are never compiled today.

Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 16:39:07 +02:00
..
1wire 1wire: Move onewire_valid_rom to 1wire_crc.h 2026-06-26 22:50:43 +08:00
aie
analog stm32h7/dac: add DMA stream mode with ioctl-driven double-buffering 2026-07-26 14:26:47 -03:00
audio drivers/audio/i2s: Fix unsigned integers in function signatures 2026-07-05 15:06:04 +08:00
binfmt binfmt: Add a configuration flag to store the module filename 2025-11-01 22:59:47 +08:00
can sja1000: replace enter_critical_section with spinlock 2026-01-08 09:15:04 -03:00
clk drivers/clk: use uintptr_t for register addresses 2026-06-18 21:52:33 +08:00
contactless
coresight style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
crypto drivers/crypto: add Microchip RNG90 driver 2026-06-14 18:41:09 +08:00
dma
drivers serial/uart_rpmsg: add _raw version of driver 2026-02-23 09:19:57 -03:00
eeprom mtd/at25ee: Use eeprom/spi_xx25xx internally 2025-12-17 19:03:54 +01:00
efuse
fs fs/vfs: Add ioctldir for volume ioctls via the mountpoint directory. 2026-07-25 07:28:22 -03:00
himem
hwspinlock
i2c drivers/i2c: add ioexpander-based lower-half implementation for I2C bit-bang 2026-01-01 17:08:47 +08:00
i3c style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
input drivers/mpr121: Add support to MPR121 Capacitive Keypad 2026-04-23 15:56:32 -03:00
ioexpander drives/ioexpander: add support to PI4IOE IO Expander 2026-07-28 10:18:01 +08:00
lcd style: Fix "the the" typo across the codebase. 2026-03-23 11:07:49 +01:00
leds !drivers/pwm: remove PWM_MULTICHAN option 2026-05-18 11:35:25 -04:00
lib lib/math32: Avoid __uint128_t casts for LDC ImportC 2026-07-21 17:11:03 -03:00
math drivers/math: use small lock to replace enter_critical_section 2026-01-08 11:17:17 +08:00
mbox
mm protect: move us_heap to userspace_data_s 2026-02-02 11:06:53 +08:00
modem style: fix checkpatch issues after debug.h move 2026-04-07 07:50:06 -03:00
motor style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
mtd drivers/mtd/gd25: add QSPI support 2026-06-21 09:43:29 -03:00
net boards/mips: Add networking support to CI20 board 2026-07-17 16:14:14 -03:00
note note/ram: support multiple noterams to dump data when panic occurs 2026-01-24 19:33:17 +08:00
nx nuttx/nx: compilation error occurs 2026-04-14 13:35:26 +08:00
pci drivers/pci:write legacy num to config space when enable legacy irq 2026-01-30 12:50:42 +08:00
pinctrl
power style: Fix "the the" typo across the codebase. 2026-03-23 11:07:49 +01:00
rc style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
regmap
reset style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
rf
rpmsg drivers/rpmsg: use NuttX atomic_t API instead of C11 atomics 2026-05-21 06:28:36 +08:00
rptun rptun: add configuring the stack of rptun as static 2026-01-19 14:18:27 +08:00
segger
sensors drivers/sensors: add LIS3DSH accelerometer uORB driver 2026-07-25 14:45:40 +02:00
serial drivers/serial: Modify serial/pty to allow NSH/Telnet line edit 2026-07-22 22:07:18 +08:00
spi stm32h5/qspi: add QSPIMEM_QUADDATA flag for 1-1-4 transfers 2026-06-21 09:43:29 -03:00
syslog driver/ramlog: Implement the rate limiting function for ramlog driver. 2026-01-27 03:17:05 +08:00
timers drivers/watchdog: fix capture automonitor notifier context 2026-07-17 14:59:47 +08:00
usb drivers/usbhost/usbhost_cdcecm.c: Added support for Host CDC-ECM 2026-05-06 06:20:03 +08:00
usrsock
vhost drivers/vhost: add vhost_get_vq_buffers() to collect scatter-gather buffers 2026-02-04 02:32:02 +08:00
video video/fb: fix compilation errors 2026-04-13 19:55:44 +08:00
virtio include/nuttx/virtio: allow common virtio helpers to be used by vhost 2026-02-04 02:32:02 +08:00
wireless wireless/cc1101: Add MSK/4-FSK, dynamic PATABLE ramping, and fix IOCTL safety 2026-03-11 16:05:19 +01:00
.gitignore
addrenv.h sched/arch/libc: give fork(), vfork() and task_fork() separate semantics 2026-07-29 16:39:07 +02:00
allsyms.h
arch.h sched/arch/libc: give fork(), vfork() and task_fork() separate semantics 2026-07-29 16:39:07 +02:00
ascii.h
atexit.h
atomic.h include/nuttx/atomic.h: fix C++ definition conflicts 2025-12-22 15:27:39 +08:00
bits.h
board.h drivers/usbhost/usbhost_enumerate.c: Allow selecting USB configuration 2026-05-06 06:20:03 +08:00
cache.h nuttx/cache.h: fix the compile warning in sim when enable OpenAMP 2025-12-31 02:36:29 +08:00
can.h drivers/can: move CAN utils to CAN common files 2025-05-14 10:30:25 -03:00
cancelpt.h sched/cancelpt: Fix MISRA C 2012 Rule 10.4 violations 2026-02-02 21:09:40 +08:00
circbuf.h
clock.h !sys/types.h: change time_t and clock_t to int64_t to align with other OSes 2026-05-19 16:21:28 +08:00
clock_notifier.h fs/timerfd: implement TFD_TIMER_CANCEL_ON_SET to detect clock changes 2026-01-30 17:20:24 +08:00
compiler.h float.h: improve long double related definitions 2026-07-02 09:02:21 -03:00
coredump.h
crc8.h libs/crc: implement AUTOSAR-compatible CRC algorithm 2026-01-30 17:32:15 +08:00
crc16.h libs/crc: implement AUTOSAR-compatible CRC algorithm 2026-01-30 17:32:15 +08:00
crc32.h libc/crc32: add IEEE-compatible crc32_ieee for Linux/zlib interop 2026-07-03 10:18:28 +08:00
crc64.h !compiler: drop CONFIG_HAVE_LONG_LONG and require long long support 2026-05-19 16:21:28 +08:00
debug.h include/debug.h: Move to include/nuttx/debug.h 2026-04-07 07:50:06 -03:00
elf.h
environ.h
envpath.h
ethtool.h
event.h [!] sched/event: Remove wait object dependency from event implementation 2025-10-31 19:56:32 -03:00
fdcheck.h
fdt.h
fork.h sched/arch/libc: give fork(), vfork() and task_fork() separate semantics 2026-07-29 16:39:07 +02:00
gdbstub.h
hashtable.h
hrtimer.h sched/hrtimer: Update the comments. 2026-02-02 13:26:22 +08:00
idr.h
init.h sched: add trace points during system startup and board initialization 2026-01-27 03:18:11 +08:00
instrument.h style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
ipcc.h style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
irq.h sched/irq: Consolidate IRQ bounds checking into IRQ_TO_NDX macro 2026-01-28 13:35:30 +08:00
kmalloc.h
kthread.h
lin.h LIN:adjust the LIN flag 2026-01-08 23:15:31 +08:00
lirc.h
list.h list: Fix the list conflicts. 2026-01-19 14:12:09 +08:00
list_type.h list: Fix the list conflicts. 2026-01-19 14:12:09 +08:00
macro.h macro: use portable variadic macros 2026-01-22 22:14:00 +08:00
memoryregion.h
mmcsd.h
module.h libc/elf: rename modlib to libelf 2025-04-11 09:43:22 +08:00
mqueue.h !sys/types.h: change time_t and clock_t to int64_t to align with other OSes 2026-05-19 16:21:28 +08:00
mutex.h sched/pthread: move pthread mutex from syscall to user-space 2026-01-22 12:40:49 -03:00
notifier.h include/debug.h: Move to include/nuttx/debug.h 2026-04-07 07:50:06 -03:00
nuttx.h
page.h style: Fix "is is" typo across the codebase. 2026-03-24 09:39:26 +08:00
panic_notifier.h
pgalloc.h mm/gran: add gran_alloc_align API 2025-05-12 15:01:37 +08:00
progmem.h
pthread.h pthread: move pthread_cond to userspace 2026-01-26 16:26:39 +08:00
queue.h
random.h
reboot_notifier.h
rwsem.h sched/sem_rw.c: Add downgrade_write API for sem_rw 2026-01-07 22:47:09 +08:00
sched.h sched/arch/libc: give fork(), vfork() and task_fork() separate semantics 2026-07-29 16:39:07 +02:00
sched_note.h note: add NOTE_DUMP_BINARY support for binary log dumping 2026-01-27 21:56:03 +08:00
scsi.h style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
sdio.h arch/arm/src/stm32{h7,f7,l4}: add 4-bit wide bus support for MMC/eMMC cards 2026-07-21 08:51:38 -03:00
sdio_slave.h style: fix spelling in code comments and strings 2025-05-23 10:48:41 +08:00
semaphore.h include/nuttx/semaphore.h: parenthesize NXSEM helper args 2026-06-05 10:35:46 -04:00
seqlock.h seqlock: Fix struct name and constants. 2025-12-22 10:22:06 -03:00
signal.h sched/signal: Remove shadow definitions to reduce unnecessary API 2025-10-14 17:40:18 +08:00
spawn.h
spinlock.h sched/spinlock: Add time statistics in func enter_critical_section(). 2026-01-27 21:59:14 +08:00
spinlock_type.h seqlock: Fix struct name and constants. 2025-12-22 10:22:06 -03:00
streams.h libc/stream: Add support for lib_scanf 2025-06-20 09:48:39 +08:00
symtab.h
tee.h drivers/misc/optee: Expanded RPC support. 2025-08-06 02:29:33 +08:00
thermal.h drivers/thermal: Add support for passive trip point 2025-02-16 11:22:41 -03:00
tls.h pthread: remove tl_lock 2026-01-26 20:56:12 +08:00
trace.h trace: fix macro line continuation formatting 2026-01-25 10:45:26 -03:00
uorb.h drivers/sensors: add initial support for fixed-point data for sensors 2026-05-02 00:56:42 +08:00
userspace.h protect: move us_heap to userspace_data_s 2026-02-02 11:06:53 +08:00
vt100.h Documentation: nsh: document the top command 2026-07-11 09:15:15 -03:00
wdog.h drivers: Fix comment typos — 'Pubic' → 'Public' across drivers and headers. 2026-07-02 13:29:48 +08:00
wqueue.h !sys/types.h: change time_t and clock_t to int64_t to align with other OSes 2026-05-19 16:21:28 +08:00
zoneinfo.h