Commit graph

2434 commits

Author SHA1 Message Date
Justin Hammond
0790b52e64 libs/libc/machine/risc-v: Compare a register at a time on equal offsets.
memcmp, strncmp and strcmp reach their word loops only when both pointers
are already on a register boundary:

  or    t0, a0, a1
  andi  t0, t0, SZREG-1

That asks more than the loops need.  They load from the two pointers at
the same boundary, so what matters is that the two agree about where a
boundary falls, not that either is already on one.  A pair offset by the
same amount can be walked up to the boundary a byte at a time and
compared a register at a time from there.

The union also holds far less often than the difference.  For arbitrary
pointers on RV64 it is true about one time in 64 against one in eight,
and the case it rejects, two strings carved out of the same buffer, is
the common one.

Test the difference of the pointers, and walk to the boundary first.
arch_strcpy.S and arch_memcpy.S already do this.  Keeping every access
aligned is not only faster here: the base ISA does not require misaligned
loads and stores to be supported at all, so a routine in a machine
directory cannot assume one will work, whatever it costs.

Measured on a 1.4 GHz rv64, source and destination misaligned by one:

                    before   after
  memcmp 32K          34.4   458.0 MB/s
  strncmp 32K         32.4   253.0 MB/s
  strcmp 32K          41.0   280.0 MB/s

Each of those was the rate of the byte loop the word loop was meant to
replace.  Pointers that genuinely disagree still take the byte loop, and
the aligned rates are unchanged.

The measurements come from the benchmark in apache/nuttx-apps#3706.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-17 12:24:06 +02:00
Justin Hammond
1948a225f8 libs/libc/machine/risc-v: Align strlcpy's destination too.
The word loop walks src to a register boundary and then stores a whole
register at a time to dst, but nothing establishes that dst is on a
boundary too.  Where the two pointers disagree about where a boundary
falls, every store in that loop is misaligned.

The base ISA does not require misaligned stores to be supported.  Where
firmware emulates them each store traps into machine mode, and where
nothing emulates them the store faults, so this is not only a question of
speed.  Measured on a 1.4 GHz rv64 that emulates them, with a 32 KB
string whose src and dst are misaligned by different amounts:

  generic C     410.4 MB/s
  this file       7.5 MB/s

which is around 178 cycles per byte, flat from 512 bytes to 32 KB.

Test the two pointers against each other before going wide, as
arch_strcpy.S already does.  Pointers that agree still reach the word
loop, since walking src to a boundary walks dst to one as well; pointers
that disagree take the byte path, where no single boundary serves both.
After the change the misaligned case runs at 490 MB/s and the aligned
rates are unchanged.

The measurements come from the benchmark in apache/nuttx-apps#3706.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-17 12:24:06 +02:00
Xiang Xiao
d1dc791d11 libs/libc/string: Add 4-byte alignment middle path for BSD functions.
libc_data_t is 8 bytes wide, so a buffer which is 4-byte but not
8-byte aligned falls back to the byte at a time loop.  Add a 32-bit
middle path so such buffers still handle four bytes per iteration.

* Add DETECTNULL32/DETECTCHAR32, UNALIGNED4/UNALIGNED4_X,
  LITTLEBLOCKSIZE4/BIGBLOCKSIZE4 and TOO_SMALL4 to libs/libc/libc.h.
* Take the new path in memccpy, memcmp, memcpy, memset, stpcpy,
  stpncpy, strcmp, strcpy, strncmp and strncpy when both pointers are
  4-byte aligned but the 8-byte path can't be used.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-08-17 10:47:33 +02:00
Bowen Wang
c119a0c0fd libs/libc/string: Fix sign extension in memset word fill pattern.
When the 'c' parameter has bit 7 set (e.g. 0x80), the int value gets
sign extended (to 0xffffff80 on the signed char platforms).  The word
sized fill pattern was built without truncating to unsigned char
first, so the fast word aligned path wrote the wrong bytes.

Fix both lib_memset.c and lib_bsdmemset.c by casting 'c' to unsigned
char before building the fill pattern, as required by C11 7.24.6.1
which states that memset converts 'c' to unsigned char.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Bowen Wang <wangbowen6@xiaomi.com>
2026-08-17 10:47:33 +02:00
anjiahao
7f5f0f5e53 libs/libc/string: Fix asrc alignment for unaligned access in memrchr.
memrchr scans backward, so the original implementation aligned
(x + 1) rather than x:

  #define UNALIGNED(x) ((long)(uintptr_t)((x) + 1) & (sizeof(long) - 1))

while the common UNALIGNED_X() macro checks the pointer itself.  Pass
src0 + 1 to UNALIGNED_X() to restore the original behavior, otherwise
asrc is off by one byte and the word loop reads the wrong data.

Assisted-by: Claude:claude-opus-5
Signed-off-by: anjiahao <anjiahao@xiaomi.com>
2026-08-17 10:47:33 +02:00
fangpeina
319a9f9724 libs/libc/string: Fix the address calculation in memrchr fast path.
Remove the incorrect address restoration logic in the memrchr fast
path.  The UNALIGNED_X loop already ensures the proper alignment, so
the subsequent address recalculation is unnecessary and makes memrchr
return the wrong position.

This fixes the syslog message corruption where memrchr reports the
incorrect newline position.

Assisted-by: Claude:claude-opus-5
Signed-off-by: fangpeina <fangpeina@xiaomi.com>
2026-08-17 10:47:33 +02:00
anjiahao
bd6b226546 libs/libc/string: Use long long to speed up the BSD string functions.
Most hardware accesses the memory through a 64-bit bus, so handle the
data in 64-bit chunks instead of "long" chunks which are only 32-bit
wide on the 32-bit platforms.

* Add the libc_data_t type (unsigned long long) and move the shared
  UNALIGNED/UNALIGNED_X/ALIGNED, LITTLEBLOCKSIZE, TOO_SMALL and
  DETECTNULL helpers from the individual C files to libs/libc/libc.h.
* Convert all lib_bsd*.c implementations to the new type and macros,
  which also drops the duplicated LONG_MAX conditionals.

Assisted-by: Claude:claude-opus-5
Signed-off-by: anjiahao <anjiahao@xiaomi.com>
2026-08-17 10:47:33 +02:00
Alan Carvalho de Assis
3cbf6f2168 libc: add wait4()
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>
2026-08-14 10:20:52 +08:00
Alan Carvalho de Assis
3635004dcd libs/libc/grp: fix getgrbuf_r() pointer-alignment padding
padlen = sizeof(void *) - (addr % sizeof(void *)) never returns 0, even
when addr is already pointer-aligned -- it returns a full alignment unit
instead. Since callers size buflen for zero padding, the subsequent
"buflen < padlen + reqdlen" check then always fails, so getgrgid()/
getgrnam() and their _r variants always return ERANGE.

Found via `id` on sim:toybox, which resolves gid 0 to "root" through
this path.

Signed-off-by: Alan C. Assis <acassis@gmail.com>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 10:20:52 +08:00
Abhishek Mishra
cf3781b7f0 sched: add setresuid and setresgid
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>
2026-08-12 16:06:03 -03:00
Abhishek Mishra
1014c49881 sched: add supplementary group IDs (setgroups/getgroups/initgroups)
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>
2026-08-12 16:06:03 -03:00
ganjing
a0fcbb7957 libs/libc/risc-v: Refresh memcpy and memset with XLEN-adaptive loops.
Rewrite arch_memcpy.S and arch_memset.S to be register-width aware on
both RV32 and RV64 using REG_L/REG_S/SZREG macros from asm.h.

memcpy gains:
 - 16xSZREG unrolled main loop (128B/iter on RV64, 64B on RV32).
 - Shift-merge path for misaligned src: reads two aligned words
   straddling each output word and shifts them together, so no load
   or store is ever misaligned.
 - Single SZREG and byte loops for remainder and small copies.

memset gains:
 - 32xSZREG unrolled main loop (256B/iter on RV64, 128B on RV32)
   using Duff's device for non-power-of-two remainders.
 - .option norvc ensures fixed 4-byte instruction width for correct
   jump offset calculation in the Duff's device entry.
 - Zero-length input handled correctly (branch to guarded tail).

The old memcpy always used lw/sw even on RV64, wasting half the
memory bandwidth. The old memset unrolled only 16 bytes per iteration.

Signed-off-by: ganjing <ganjing@xiaomi.com>
2026-08-12 10:24:01 +08:00
ganjing
931d5f50d4 libs/libc/risc-v: Add optimized strlcpy.
Add word-at-a-time strlcpy using DETECTNULL for both the copy phase
and the strlen tail when truncated.  The copy loop aligns src and
processes a register at a time, falling to bytewise for the last word
containing the terminator.  When truncated, the remaining src length
is measured with a second word-at-a-time loop.

strlcpy has 46 call sites in a typical kernel image (more than strcpy)
and is not covered by newlib OPTSPEED, making it a high-value target.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: ganjing <ganjing@xiaomi.com>
2026-08-12 10:23:47 +08:00
ganjing
ad9c9d41b0 libs/libc/risc-v: Unroll memcmp with 4-word XOR|OR folding.
Reduce branch overhead in the memcmp main loop by comparing four
words per iteration: XOR each pair, OR the four differences together,
and branch once.  On a mismatch the single-word loop locates the
exact differing word within four words of the fault.

Add a beqz guard at .Lbyte_cmp entry to handle the case where the
4-word loop consumes all remaining bytes exactly.

Measured on QEMU RV32: memcmp(128) 313 -> 271 cycles (13% faster).

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: ganjing <ganjing@xiaomi.com>
2026-08-12 10:23:47 +08:00
ganjing
9b20c629fe libs/libc/risc-v: Add optimized string and memory functions.
Add assembly-optimized implementations for 14 string/memory functions
using word-at-a-time techniques (DETECTNULL, broadcast+XOR) and
XLEN-adaptive macros for both RV32 and RV64:

 - memmove: direction check + forward tail to memcpy, reverse path
   with 16xSZREG unroll and shift-merge for misaligned src.
 - memcmp: word-granularity compare when both pointers share alignment,
   bytewise fallback for mismatched pointers.
 - memchr: broadcast target byte, XOR with each word, DETECTNULL to
   find matches. Counter-based bounds (no pointer overflow).
 - strlen: DETECTNULL word loop, constants loaded from .srodata.
 - strnlen: strlen with counter-based length limit.
 - strcpy/strncpy: word loop with DETECTNULL, zero-fill remainder
   for strncpy. strncpy reuses strcpy via #define USE_AS_STRNCPY.
 - stpcpy/stpncpy: reuse strcpy/strncpy via #define USE_AS_STPCPY.
 - strchr/strchrnul: broadcast+XOR detecting both target char and
   null simultaneously. strchrnul reuses strchr via #define.
 - strrchr: forward scan recording last match position.
 - strncmp: word-at-a-time compare with null detection and counter.
 - strcat: strlen(dst) then strcpy(dst_end, src) word-at-a-time.

Each function is independently selectable via CONFIG_RISCV_<FUNC>,
or all enabled together with CONFIG_RISCV_STRING_FUNCTION=y.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: ganjing <ganjing@xiaomi.com>
2026-08-12 10:23:47 +08:00
Lwazi Dube
30a3769057 arch/mips: Add support for loadable ELF modules
Implement architecture-specific ELF header definitions and relocation handling
for the MIPS architecture to enable loadable modules.

Fixes #19178.

Changes include:
- Add `arch/mips/include/elf.h` with MIPS ELF relocation types and
  architecture-specific ELF data structures (`arch_elfdata_s`).
- Implement `libs/libc/machine/mips/arch_elf.c` containing `up_checkarch`,
  `up_relocate`, and `up_relocateadd` functions handling `R_MIPS_NONE`,
  `R_MIPS_32`, `R_MIPS_26`, `R_MIPS_HI16`, and `R_MIPS_LO16` relocations.
- Integrate MIPS machine-specific C library support in
  `libs/libc/machine/mips/Make.defs`.
- Update `LDMODULEFLAGS` in `arch/mips/src/mips32/Toolchain.defs` to include the
  little-endian (`-EL`) flag.
- Update `up_coherent_dcache` for proper cache synchronization on JZ4780.

Signed-off-by: Lwazi Dube <lwazeh@gmail.com>
2026-08-11 09:46:46 -03:00
Marco Casaroli
70c2ef5911 !sched/arch/libc: Give fork() and vfork() their real, 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 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>
2026-08-10 08:57:30 -03:00
Alan Carvalho de Assis
87260499e1 cmake: Use NUTTX(_DIR/_BIN_DIR) instead CMAKE(_SRC_DIR/_BIN_DIR)
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
This change fixes NuttX’s CMake support when NuttX is embedded
in another project via add_subdirectory(). CMake’s CMAKE_SOURCE_DIR
and CMAKE_BINARY_DIR refer to the outermost project, causing NuttX
to access its .config, generated files, host tools, and build artifacts
in the parent project’s directories. The fix introduces NUTTX_DIR and
NUTTX_BINARY_DIR, based on CMAKE_CURRENT_SOURCE_DIR and
CMAKE_CURRENT_BINARY_DIR, and consistently uses them for NuttX
self-references while preserving existing standalone builds. It fixes
the Kconfig initialization failure reported in #19697 and allows an
embedded sim:nsh build to configure, build, and boot successfully.
The change affects only the CMake build system (not Make or Kconfig
defaults), requires the corresponding nuttx-apps change, and does not
extend add_subdirectory() support to cross-compiled non-sim boards due
to CMake’s toolchain-file limitation.

Fixes #19697.

Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Alan Carvalho de Assis <acassis@gmail.com>
2026-08-09 11:13:08 -03:00
Justin Hammond
31fbb99218 sched/semaphore: Keep a negative task id out of the mutex holder.
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
A mutex records its holder as a task id in the low 31 bits of a word
whose top bit means "someone is blocked on this".  The id was stored
without masking, so an id with its top bit set became a holder with the
blocking bit raised.

Task ids are normally small and positive, but not always.
nxsched_gettid() reports -ESRCH for a context that no longer maps to a
running task, and there is a window where that is exactly what the
running context is: nxtask_exit() marks the next task ready to run
while the dying task is still executing on its own stack, and only then
releases the TCB.  Freeing the group inside that release takes and
drops the group's mutexes, so the lock stores 0xfffffffd and the unlock
compares 0x7ffffffd, which are not equal.

With assertions enabled the unlock trips its holder check, and every
exit of a process that frees memory panics.  In a kernel build that is
every exit, so no program could be run twice, and running one at all
took the shell down with it.  Without assertions the failure is silent:
the accidental blocking bit sends the unlock looking for a waiter that
never existed.

Encode the id the same way everywhere it is stored or compared, so that
a lock and an unlock from one context agree whatever the id's sign.
The masked forms of -1 and -2 would alias the "no holder" and "reset"
values, but nxsched_gettid() yields only valid ids and -ESRCH.

mm_lock() already sidesteps this window with a note that gettid() may
return -ESRCH during a context switch; this gives the generic mutex the
same footing rather than a second special case.

Test case, on the EIC7700 EVB, which is a kernel build with assertions:

  nsh> hello
  Hello, World!!

Before, that printed and then panicked in sem_post, taking the shell
with it, every time.  After, five runs in a row complete and the shell
survives.  ps over telnet still completes.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-09 10:05:30 +08:00
Justin Hammond
cda33ae4fc libs/libc/netdb: Size an answer header by its header, not by its union.
dns_recv_response() checked for room using sizeof(struct dns_answer_s),
but that structure is the 10-byte header plus a union holding the largest
address it can carry.  With IPv6 built the union is 16 bytes, so the check
demanded 26 bytes where 10 were needed, and any answer sitting at the end
of a response was rejected as truncated.

An A record answer supplies 14 bytes, so whether a lookup worked depended
on how much padding the server happened to send after it:

  $ dig +noedns @10.1.1.2 github.com A      # ANSWER 1, AUTHORITY 0, ADDITIONAL 0
  -> answer is last in the packet, 14 bytes remain, rejected

  $ dig +noedns @10.11.5.254 github.com A   # ANSWER 1, AUTHORITY 13, ADDITIONAL 7
  -> 26+ bytes remain, accepted

On the board, before and after, against the first of those servers:

  nsh> nslookup apache.org
  [CPU1] dns_recv_response: DNS answer header truncated
  Host: apache.org Addr: 2a04:4e42::644                 <- A record lost

  nsh> nslookup apache.org
  Host: apache.org Addr: 2a04:4e42::644
  Host: apache.org Addr: 151.101.2.132                  <- both returned

The address that follows the header is already bounds checked separately,
where its real length is known, so only the header check was wrong.  The
size is now a named constant next to the structure, since the rest of this
function already used the literal 10 for the same quantity.

Only IPv4-only builds escaped it, where sizeof happens to equal 14 and an
A record fits exactly.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 15:02:15 -03:00
liang.huang
6dea4d41ba libs/libc/symtab: fix bogus symbol size under SYMTAB_ORDEREDBYNAME
allsyms_lookup() derived a symbol's size from the physically next table
entry, assuming address order. Under CONFIG_SYMTAB_ORDEREDBYNAME the
table is sorted by name instead, producing a huge bogus size in
%pS/backtrace output.

Scan for the closest larger address instead of relying on table order.

Signed-off-by: liang.huang <liang.huang@houmo.ai>
Assisted-by: Claude Code:claude-sonnet-5
2026-08-05 21:51:25 +08:00
liang.huang
7d3d1c5567 libs/libc/symtab: fix bogus symbol names for out-of-range addresses
allsyms_findbyvalue()/%pS printed a bogus name/offset for addresses
outside the real symbol table's coverage, due to the boundary sentinels
being matchable as real symbols.

Compute the high sentinel from the actual symbol range and treat a
sentinel match as "not found".

Signed-off-by: liang.huang <liang.huang@houmo.ai>
Assisted-by: Claude Code:claude-sonnet-5
2026-08-05 21:51:25 +08:00
liang.huang
2d323de0b8 libs/libc/symtab: fix g_allsyms link failure in BUILD_PROTECTED/KERNEL
g_allsyms/g_nallsyms only exist in the kernel image, but symtab_allsyms.c
is unconditionally built into libc.a, so user-mode code under
CONFIG_BUILD_PROTECTED/CONFIG_BUILD_KERNEL fails to link.

Guard the affected code so user-mode libc.a no longer references these
kernel-only symbols.

Signed-off-by: liang.huang <liang.huang@houmo.ai>
Assisted-by: Claude Code:claude-sonnet-5
2026-08-05 21:51:25 +08:00
Marco Casaroli
e47be608d5 libc/dlfcn: Count opens so a library can be shared.
dlopen() of a library that is already loaded fails.  libelf_insert()
rejects a name that is already in the module registry with EEXIST, and
dlinsert() passes that straight out, so the second caller gets NULL.
POSIX says dlopen() shall return a handle to the object, and there is no
way today for two modules to hold the same library at once -- which is
what a shared library is for.

So dlopen() now takes another reference on a library that is already
there, and dlclose() only tears it down when the last handle goes.  The
count lives in the dlfcn layer rather than in libelf_insert() so that
insmod keeps its own behaviour: a second insmod of the same name still
fails with EEXIST, which is right for a kernel module.

The module name is what makes any of this possible, and a PROTECTED build
did not have one.  Names were defined for CONFIG_BUILD_FLAT or the kernel
side of a split build, on the reasoning that only the kernel needed them,
which predates dlopen() being usable from user space.  Without a name the
user-space copy of libelf cannot recognise a second open of a library,
cannot count opens, and cannot make dlclose() mean anything -- two
dlopen()s there produce two independent copies of the library and lose
track of the first.  Names are therefore defined wherever CONFIG_LIBC_DLFCN
is, which costs NAME_MAX per loaded module in that configuration.

The path no longer has to be copied either.  The module name is the
basename of the file and libelf_insert() takes it as a const string, so
dlinsert() finds it with strrchr() instead of handing a writable
duplicate of the whole path to basename().

BUILD_KERNEL is deliberately untouched.  dlopen() returns NULL there
unconditionally: dlinsert() is a stub, because sharing a library between
processes with separate address spaces needs the text in a shared region
and the data per process at a matching virtual address, which is a
different problem from this one.

Built for mps3-an547:picostest with and without CONFIG_LIBC_DLFCN, and
for stm32f4discovery:kostest, a PROTECTED configuration, with it enabled.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-04 11:12:49 -03:00
Abhishek Mishra
db6ab892a2 fs/binfmt: close symlink TOCTOU and harden setuid/setgid exec hygiene
Some checks are pending
Build Documentation / build-html (push) Waiting to run
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
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>
2026-08-01 15:32:03 -03:00
hanzhijian
ddf350f1a3 libs/libc: support explicit bases up to 36
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
The strto* interfaces document explicit bases in the range 2 through 36, and lib_isbasedigit() already supports alphabetic digits through base 36.  However, lib_checkbase() rejects every explicit base above 26 with EINVAL.

Raise the validation limit to 36 so the conversion interfaces accept the full documented range while continuing to reject base 37 and above.

Signed-off-by: hanzhijian <hanzhijian@zepp.com>
2026-08-01 20:08:32 +08:00
Udit Jain
6f0da60bd7 libs/libbuiltin/compiler-rt: skip unsupported Arm VFP builtins asm
Some checks are pending
Build Documentation / build-html (push) Waiting to run
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
The compiler-rt builtins build globs every arm/*.S source, but several of
those hand-written assembly files require FPU features the target may not
have.  Upstream compiler-rt selects them conditionally; NuttX did not, so
BUILTIN_COMPILER_RT builds for single-precision-FPU Arm targets (e.g.
Cortex-M33, -mfpu=fpv5-sp-d16) failed to assemble with errors such as
"selected FPU does not support instruction -- vadd.f64".

Filter the source list to match the configured FPU, in both the Makefile
and CMake builds:
  - chkstk.S / chkstk2.S are Windows/MinGW-only stack probes, always dropped;
  - with no hardware FPU (!CONFIG_ARCH_FPU) all arm/*vfp.S are dropped;
  - with a single-precision FPU (!CONFIG_ARCH_DPFPU) the double-precision
    *df*vfp.S routines are dropped.

Reproduced against compiler-rt 17.0.1 with arm-none-eabi-gcc 14.2 using the
Cortex-M33 single-precision flags: 18 of 86 arm/*.S files failed to assemble
(17 double-precision *df*vfp.S plus chkstk.S); after the filter all remaining
68 files assemble cleanly.

Fixes: https://github.com/apache/nuttx/issues/17386
Generated-by: Claude (Anthropic)
Signed-off-by: Udit Jain <uditjainstjis@gmail.com>
2026-07-31 15:37:33 -03:00
Abhishek Mishra
5c9c236954 libc/pwd: default ostest multiuser passwd path to /tmp/ostest_passwd
When CONFIG_TESTING_OSTEST_MULTIUSER is enabled, default
CONFIG_LIBC_PASSWD_FILEPATH to a writable tmpfs path used by the
ostest multiuser passwd sub-test.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-07-30 09:48:10 +08:00
Abhishek Mishra
282d4cb0e9 libc/pwd: parse NuttX 5-field passwd records
Accept user#️⃣uid:gid:home lines produced by mkpasswd in addition
to the six-field gecos form, and raise the default line buffer to 256.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-07-30 09:48:10 +08:00
Abhishek Mishra
4c62271654 sched/group: add getresuid, getresgid, setreuid, and setregid
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>
2026-07-30 09:48:10 +08:00
Alan Carvalho de Assis
c027e7c3e4 tools: fix stale archive members surviving a Kconfig-driven CSRCS change
During the Toybox port to NuttX, Claude noticed that changes in the
menuconfig weren't taking affect. This issue exists for a long time on
NuttX, in fact BayLibre's presentation from 2017 make jokes about our
building system not been reliable:
https://www.youtube.com/watch?v=XUJK2htXxKw&t=320s

Stale archive members from $(AR)'s additive-only behavior can linger
after Kconfig toggles change which files provide a symbol, causing dead
weight or "multiple definition" link errors on incremental builds.
Fixed by splitting ARCHIVE into two macros: ARCHIVE keeps the original
additive behavior for apps/libapps.a, which many independent
subdirectories contribute to across a build, while the new
ARCHIVE_REBUILD deletes then archives for the far more common case
of a single Makefile building its own self-contained $(OBJS)
- all 39 such call sites now use it.

Assisted-By: Claude Sonnet 5
Signed-off-by: Alan C. Assis <acassis@gmail.com>
2026-07-28 21:26:03 -03:00
hanzhijian
5f0c1c8d10 libc/time: Fix POSIX timezone string parsing.
When loading a zoneinfo file fails, parse a non-colon-prefixed TZ value
as a POSIX timezone string. Treat a successful tzparse() result as
success while preserving the leading-colon file-only behavior.

Assisted-by: Codex:gpt-5.6 Sol
Signed-off-by: hanzhijian <hanzhijian@zepp.com>
2026-07-25 19:54:18 +08:00
Lingao Meng
b5d3d30378 netdb: Add http and https service entries
getaddrinfo() and getservbyname() use the built-in service table when
resolving service names. The table only contained ntp, so common
service names such as http and https could not be resolved without a
numeric port.

Add http and https entries for both TCP and UDP to match the existing
service table style.

Testing:

  - Host: Ubuntu 22.04 x86_64.

  - Board/config: sim:nsh with CONFIG_LIBC_NETDB=y and
    CONFIG_EXAMPLES_HELLO=y.

  - make clean && make -j16.

  - Ran a temporary hello example that called getservbyname("http",
    "tcp") and getservbyname("https", "tcp"). The app verified ports
    80 and 443 and printed "getservbyname http/https test passed".

Assisted-by: Claude:Claude-Fable-5
Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
2026-07-23 15:34:02 +08:00
anjiahao
745a6e53a4 spawn: allow zero stacksize/priority to use binary loader defaults
posix_spawnattr_init() no longer pre-fills attr->stacksize and
attr->priority (it leaves them zero, as memset already does).  When a
caller does not set them, the binary loader supplies them from the
loaded ELF (binp->stacksize / binp->priority, parsed from the nx_*
symbols), and nxtask_spawn_exec() falls back to the parent priority and
CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE for the posix_spawn() function-task
path.

This lets an application's stack size and priority embedded as ELF
symbols actually drive the spawned task, instead of being overridden by
the spawnattr defaults.

Signed-off-by: anjiahao <anjiahao@xiaomi.com>
2026-07-15 12:24:44 -03:00
anjiahao
c8b71df614 libelf:support find symbol by symbol name
Add libelf_findsymbol() to locate a symbol in the ELF symbol table by
name, reusing the existing libelf_findsymtab/libelf_readsym/libelf_symname
helpers, and expose its prototype in include/nuttx/lib/elf.h.

Signed-off-by: anjiahao <anjiahao@xiaomi.com>
2026-07-15 12:24:44 -03:00
liang.huang
170a989ccb libc/builtin: support per-application priority/stacksize under KERNEL build.
nsh_fileapp() could not apply an application's Kconfig-configured
priority/stacksize via posix_spawn() under CONFIG_BUILD_KERNEL, because
the registry table (struct builtin_s / g_builtins[]) was gated on
CONFIG_BUILTIN, which depends on !BUILD_KERNEL. Those settings were
silently ignored in KERNEL builds.

CONFIG_BUILTIN conflates the table with main_t-based dispatch, which is
meaningless under CONFIG_BUILD_KERNEL. Add a hidden derived symbol,
APP_REGISTRY, that tracks table availability independently of dispatch:

  config APP_REGISTRY
          bool
          default y if BUILTIN || BUILD_KERNEL

Switch the guards on the table itself (Make.defs, builtin.h) from
CONFIG_BUILTIN to CONFIG_APP_REGISTRY. Call sites that dereference
builtin->main stay gated on CONFIG_BUILTIN and remain unreachable
under CONFIG_BUILD_KERNEL.

Signed-off-by: liang.huang <liang.huang@houmo.ai>
2026-07-13 15:23:39 -03:00
Alan Carvalho de Assis
d8d77c249c libs/libdsp: Add Matrix operations
This commit adds support for matrix operation on libdsp. The code
came from: https://github.com/DjVul/Extended-Kalman-Filter---STM32/blob/main/Core/Src/matrix_utils.c

Signed-off-by: Alan C. Assis <acassis@gmail.com>
2026-07-11 14:55:59 -03:00
Old-Ding
8ff9dfdea3 libs: netdb: Advance stream buffers by transfer count
Update the DNS stream send and receive helpers to advance their buffers
by the number of bytes actually transferred. Advancing by the original
remaining length skips data when send() or recv() completes partially.

Signed-off-by: Old-Ding <ai.neo.ae86@gmail.com>
2026-07-09 08:45:42 +08:00
Old-Ding
bab2b4a5f2 libc/stream: Check lowout bounds before access
Check the provided length before reading the current lowoutstream byte. This avoids reading past zero-length or fully consumed buffers before the loop condition stops.

Generated-by: OpenAI Codex
Signed-off-by: Old-Ding <35417409+Old-Ding@users.noreply.github.com>
2026-07-06 09:02:22 +02:00
hanzhijian
9e467388de stdlib/rand: replace weak LCG with xorshift32 PRNG
The existing first-order LCG (seed = 470001 * seed % 999563) has several
quality problems:
- Output range limited to [1, 999562] (~20 bits) instead of full 32-bit
- Lower bits have very short periods (8-bit period = 1, 16-bit = 105)
- Overall period only ~1M, far too short for many applications
- Causes mbedtls_rsa_gen_key to loop forever when rand() consumption
  aligns with the cycle length (issue #16760)

Replace the entire order-based LCG implementation (CONFIG_LIBC_RAND_ORDER
0-3) with Marsaglia's xorshift32:
- Full 32-bit output range
- Period 2^32 - 1 (~4.29 billion)
- Fast: just three XOR/shift operations
- No floating-point math needed
- No CONFIG_LIBC_RAND_ORDER configuration required

Remove the CONFIG_LIBC_RAND_ORDER Kconfig option and clean up all
defconfig references (12 boards) and related comments.

Signed-off-by: hanzhijian <hanzhijian@zepp.com>
2026-07-03 15:08:12 +08:00
hanzhijian
87d84e437a libc/crc32: add IEEE-compatible crc32_ieee for Linux/zlib interop
Add crc32_ieee() and crc32_ieeepart() that produce CRC32 values
compatible with Linux/zlib. The difference from the existing crc32():

  crc32():        init=0, no final XOR (NuttX native)
  crc32_ieee():   init=0xFFFFFFFF, final XOR 0xFFFFFFFF (Linux/zlib)

The existing crc32() and crc32part() are unchanged to avoid breaking
existing callers (bbsram, sbram, etc.).

New functions:
  crc32_ieee(src, len)              - full CRC, Linux-compatible
  crc32_ieeepart(src, len, crcval)  - incremental CRC, Linux-compatible

Verified on sim:nsh against known Linux zlib test vectors:
  crc32_ieee("hello")     = 0x3610a686 (Linux: 0x3610a686) PASS
  crc32_ieee("123456789") = 0xcbf43926 (Linux: 0xcbf43926) PASS
  crc32_ieee("")           = 0x00000000 (Linux: 0x00000000) PASS
  crc32_ieeepart incremental  = 0xcbf43926               PASS
  crc32("hello")          = 0xf032519b (unchanged)       PASS

Signed-off-by: hanzhijian <hanzhijian@zepp.com>
2026-07-03 10:18:28 +08:00
Karel Kočí
d61242c319 libs/libm: correct implementation of truncl if long double is double
This reuses implementation of trunc in case long double has same size as
double.

The previous implementation is used only in case x87 80-bit float point
is the long double. In other cases the logic is intentionally replaced
with panic to not provide a wrong result.

Signed-off-by: Karel Kočí <kkoci@elektroline.cz>
2026-07-02 09:02:21 -03:00
Karel Kočí
9f4b4faf47 libs/libc: add limits checks
NuttX defines constants for the sizes of types just as constants
directly in the code. The real values depend on compiler and target
platform. It is possible that value declared by NuttX header is wrong
compared to the compiler configuration. This is more likely in case of
floating points.

Not all compilers supported by NuttX provide required info through
defines and thus it is more generic to specify limits as constants, but
in case compiler provides them then it is a good idea to compare. This
commit adds such set of comparisons for limits.h and float.h constants.

The comparisons were tested with GCC and Clang. They will be ignored in
case compiler doesn't provide them.

Signed-off-by: Karel Kočí <kkoci@elektroline.cz>
2026-07-02 09:02:21 -03:00
Xiang Xiao
9e141acab3 !include/fcntl.h: align open flags with Linux values
Align the NuttX open(2) flag constants with the Linux asm-generic
values so that the FUSE wire protocol and other cross-platform
interfaces work without conversion.

All code that used '(flags & O_RDONLY)' as a bitmask check (always 0
now that O_RDONLY=0) has been updated to use '(flags & O_ACCMODE)'
comparisons.

The NUTTX_O_* constants in include/nuttx/fs/hostfs.h are updated to
match, and the sim hostfs open flag mapping is fixed.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-06-30 13:43:44 +08:00
Xiang Xiao
6161c73639 include/fcntl.h: remove O_RDOK/O_WROK aliases
O_RDOK and O_WROK are non-standard aliases for O_RDONLY and O_WRONLY
respectively.  Having two names for the same flag creates confusion,
especially when aligning the flag values with Linux.  Remove the
aliases and replace all uses with the standard O_RDONLY/O_WRONLY.

No functional change — O_RDOK was defined as O_RDONLY and O_WROK as
O_WRONLY, so the replacement is a pure text substitution.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-06-28 09:10:11 -03:00
Xiang Xiao
9704481224 libc/unistd: add getgroups()
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>
2026-06-24 14:55:11 -03:00
Xiang Xiao
95063bde15 drivers/serial: add job-control TTY ioctls and libc wrappers
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>
2026-06-23 16:26:53 -03:00
Xiang Xiao
a9b72ed796 libc/grp: add getgrouplist()
Add getgrouplist() to the C library.  It scans the group database for
a user's supplementary groups and always reports the primary group
first.  Without CONFIG_LIBC_GROUP_FILE only the primary group is
returned, since no membership information is available.

The group file is read through lib_get_tempbuffer()/lib_put_tempbuffer()
to avoid a heap allocation on every lookup.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-06-23 23:09:22 +08:00
Michal Lenc
1685e8ff7b syslog: avoid an infinite loop if one channel fails
The current implementation exits syslog_write_foreach function
if write to one channel fails, causing other channels not being written
and returning negated errno. libc syslog functions then stay in
an infinite loop, because error is returned and the same bytes
are still passed to syslograwstream_flush and syslog_write_foreach.

The channel write may fail for many reasons - disconnected USB if
CDC ACM syslog is enabled, lost networking if telnet syslog is enabled,
error on NOR flash etc. This shouldn't lead to an ininite loop in the
code though.

The solution ensures all channels in syslog_write_foreach are tried,
therefore the user get the output to the working channels even if
the first one is broken. It also updates syslograwstream_addchar and
syslograwstream_addstring to skip the bytes if all channels fails. This
ensures syslog call won't result in an infinite loop, but the user may
lost the debugging output.

Co-authored-by: Martin Krasula <mkrasula@elektroline.cz>
Signed-off-by: Michal Lenc <michallenc@seznam.cz>
2026-06-23 22:45:01 +08:00
Jukka Laitinen
7f8f800e63 arch, sched/signal: Fix compilation with ENABLE_PARTIAL_SIGNALS=y
Correct build errors when CONFIG_ENABLE_ALL_SIGNALS is not defined

- sched makefiles: Move pending-signal helpers from the ENABLE_ALL_SIGNALS-only
  list to the !DISABLE_ALL_SIGNALS list so signal dispatch is available in
  PARTIAL builds sched: make SIG_PREALLOC_ACTIONS, SIG_ALLOC_ACTIONS and
  SIG_DEFAULT depend on ENABLE_ALL_SIGNALS
- sched: fix ifdefs around pending-signal queue access and signal-mask for
  PARTIAL/DISABLE modes
- arch: gate SYS_signal_handler / _return calls and SYSCALL_LOOKUP(signal)
  with ENABLE_ALL_SIGNALS

Signed-off-by: Jukka Laitinen <jukka.laitinen@tii.ae>
2026-06-16 17:07:32 +08:00