wait4() is BSD/Linux-standard (used by toybox's "time" applet) but NuttX
only had waitpid()+getrusage() separately. Add it to libs/libc/unistd/
built on top of those two existing primitives, so it needs no syscall
plumbing of its own and works unmodified across flat/protected/kernel
build separation. Prototype added to include/sys/wait.h.
Signed-off-by: Alan C. Assis <acassis@gmail.com>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Complete the POSIX credential setters for real/effective/saved UID and
GID so login and privilege-drop paths can clear saved-root without
relying on setreuid patterns alone.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
Track supplementary GIDs per task group, wire setgroups/getgroups
syscalls when CONFIG_SCHED_NGROUPS > 0, and honor them in DAC checks
via nxsched_has_gid(). When NGROUPS is 0, libc provides getgroups/
setgroups stubs. initgroups() fails instead of silently truncating
when membership exceeds CONFIG_SCHED_NGROUPS.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
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 two 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.
Below libc there is still one syscall. up_fork() gains a bool saying which
primitive the caller used, since the per-architecture register snapshot is
the same for both, and passes it to nxtask_setup_fork(), which is the single
place the memory semantics are decided. The argument arrives in the first
argument register and is never touched: each architecture's snapshot takes
some other call-clobbered register for its scratch, so the flag is simply
still there when the C worker is called.
The vfork() parent suspension moves out of libc into nxtask_start_fork(),
released from nxsched_release_tcb() by nxtask_resume_vfork(). 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 leaves it queued where a blocked
task collects while pre-emption is off -- g_pendingtasks, or g_readytorun on
SMP -- and the matching raw lockcount-- does not publish it the way
sched_unlock() would, leaving the parent stranded with nothing to move it on.
The fix mirrors sched_unlock() for each case: nxsched_merge_pending(), or
nxsched_deliver_task() under CONFIG_SMP. Both are no-ops 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 wherever no
other task happens to call sched_unlock() afterwards -- rv-virt:nsh64 and
rv-virt:pnsh64, where NSH is blocked in waitpid() holding the lock, and
qemu-armv8a:citest_smp, which hangs the moment the vfork() test runs.
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. In the
meantime the sharing primitive is still there under the name that describes
it: vfork() for a child that runs a program, pthread_create() for a second
flow of control that shares memory, posix_spawn() for both at once.
Kconfig: ARCH_HAVE_VFORK inherits 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 now depends on ARCH_ADDRENV.
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 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.
BREAKING CHANGE: fork() is withdrawn from every architecture. It is no
longer declared in unistd.h, so code that calls it fails to build with an error
naming the function, and the sharing behaviour it used to have is gone rather
than renamed. CONFIG_ARCH_HAVE_FORK no longer means "fork() exists"; it means
"this configuration can provide POSIX fork() semantics", and no architecture
selects it yet.
Quick fix, chosen by why the call was made:
to run a program vfork() + exec*(), or better posix_spawn()
a second flow of control that pthread_create()
shares the caller's memory
a genuinely independent copy keep fork(), and wait for the per-arch patch
of the process that implements up_addrenv_fork() and selects
CONFIG_ARCH_HAVE_FORK
Out-of-tree code that tests CONFIG_ARCH_HAVE_FORK to decide whether a
fork-then-exec path is available wants CONFIG_ARCH_HAVE_VFORK instead, which is
selected in exactly the places CONFIG_ARCH_HAVE_FORK used to be. The full
migration guide is Documentation/guides/fork_vfork_migration.rst.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Perform pseudo-filesystem permission checks inside inode_reserve() and
inode_remove() while the inode tree lock is held, and hold that lock across
pseudorename mutations so symlink swaps cannot bypass directory checks.
Hold a read lock around pseudo-fs open permission checks.
On setuid/setgid exec, update saved set-IDs, mark the task group secure,
sanitize dangerous environment variables, clear debug/dumpable flags, and
add issetugid(), secure_getenv(), and PR_SET/GET_DUMPABLE support.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
Implement POSIX real/effective/saved credential getters and paired
setters in the task group layer, with libc stubs and syscalls.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
Add getgroups() to the C library. NuttX has no supplementary group
IDs, so it reports a single group, the effective group ID, and follows
POSIX for the gidsetsize == 0 and short-buffer (EINVAL) cases.
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
NuttX has no real session/process-group abstraction, so the TTY layer
collapses the foreground process group onto the single dev->pid field
(pgrp == pid, one member per group). Extend the controlling-terminal
support so portable software (e.g. dropbear, socat) that relies on
job-control primitives works without losing the existing NuttX-specific
behaviour.
Driver (serial.c, pty.c):
- TIOCSCTTY now accepts a flag: arg > 0 keeps the historical "target
PID in arg" semantics (NSH registers the foreground command it just
spawned), while arg == 0 selects the calling task via
nxsched_getpid(), matching the POSIX flag convention used by
dropbear/socat/apue. This preserves all existing callers and makes
the previously-dead arg==0 path deliver SIGINT correctly.
- Add TIOCGPGRP/TIOCGSID (return dev->pid) and TIOCSPGRP (set it).
- pty.c gains the same handlers against pd_pid and includes
nuttx/sched.h for nxsched_getpid().
ioctl numbers (tioctl.h): TIOCGPGRP/TIOCSPGRP/TIOCGSID at 0x37-0x39.
libc wrappers:
- termios: tcgetpgrp(), tcsetpgrp(), tcgetsid() over the new ioctls.
- unistd: setsid()/getsid()/setpgid() stubs consistent with the
existing getpgrp()/getpgid() single-session model (sid == pgid ==
pid; setpgid only succeeds for pgid == pid).
Declare the new prototypes in unistd.h (tcgetsid was already in
termios.h) and register all sources in the Make.defs/CMakeLists.
Group-broadcast signalling (kill(-pgrp)) remains unsupported, so
tty signals still target the single dev->pid; a real session/process
group model is left as a follow-up.
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
debug.h is a NuttX-specific, non-POSIX header. Placing it in the
top-level include/ directory creates naming conflicts with external
projects that define their own debug.h.
This commit moves the canonical header to include/nuttx/debug.h,
following the NuttX convention for non-POSIX/non-standard headers,
and updates all in-tree references.
A backward-compatibility shim is left at include/debug.h that
emits a deprecation #warning and re-includes <nuttx/debug.h>,
allowing out-of-tree code to continue building while migrating.
Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>
Replace global enter_critical_section() with lightweight spinlock in
gethostname() and sethostname() to reduce interrupt latency while protecting
access to the shared hostname string.
Signed-off-by: hujun5 <hujun5@xiaomi.com>
Add three POSIX‑required pthread‑related constants to limits.h:
- _POSIX_THREAD_DESTRUCTOR_ITERATIONS
- _POSIX_THREAD_KEYS_MAX
- _POSIX_THREAD_THREADS_MAX
These constants are needed for full POSIX pthread compatibility and are
required by certain PSE52 test suites and applications.
Signed-off-by: guoshichao <guoshichao@xiaomi.com>
Initialize the rlim_max field in the rlp structure when handling
UL_SETFSIZE command in ulimit(), resolving an uninitialized variable
warning from Coverity static analysis.
Signed-off-by: guoshichao <guoshichao@xiaomi.com>
implement the ulimit() based on getrlimit/setrlimit function.
the ulimit() function is requiredd by vsx testset
Signed-off-by: guoshichao <guoshichao@xiaomi.com>
Replace the pipe() macro definition with a proper function implementation
to improve POSIX compliance and debugging capabilities. The new pipe()
function serves as a wrapper around pipe2() with flags set to 0.
Changes include:
- Convert pipe() from macro to function declaration in unistd.h
- Add lib_pipe.c implementation file with proper function documentation
- Update build system files (CMakeLists.txt and Make.defs) to include
the new source file when CONFIG_PIPES is enabled
- Add pipe() entry to libc.csv for symbol tracking
This change allows for better debugging, proper symbol resolution,
and follows NuttX coding standards for library function implementations.
Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
These options are unistd-specific and should not be filesystem dependent,
and also not suitable for define in the sched directory.
Signed-off-by: chao an <anchao.archer@bytedance.com>
This PR will still allow basic shell operations such as cd/ls/pwd to be used even when the environment is disabled.
Signed-off-by: chao an <anchao@lixiang.com>
CC: pthread/pthread_mutexattr_getrobust.c artc I800: creating archive libsched.a
CC: unistd/lib_getopterrp.c ctc W505: ["unistd/lib_getoptargp.c" 48/29] implicit declaration of function "getoptvars"
ctc W577: ["unistd/lib_getoptargp.c" 48/39] calling a function without a prototype
ctc W524: ["unistd/lib_getoptargp.c" 48/27] conversion of integer to pointer at assignment
ctc E212: ["unistd/lib_getoptargp.c" 49/13] access to incomplete type
ctc W523: ["unistd/lib_getoptargp.c" 49/13] pointers to different types at return
Signed-off-by: zhangyuan29 <zhangyuan29@xiaomi.com>
Most tools used for compliance and SBOM generation use SPDX identifiers
This change brings us a step closer to an easy SBOM generation.
Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.com>
To solve some compilation issues encountered during the porting of
third-party libraries, this function has been added.
Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
vfork use waitpid hang father process,
but waitpid release child processs information by default.
So when user call wait, it return errno 10.
Signed-off-by: yangyalei <yangyalei@xiaomi.com>