The base firmware and an FDPIC module disagree about what a function
pointer is. Firmware is not built FDPIC, so to it a pointer is a code
address and it branches there. A module passes the address of a two word
descriptor instead, because its code and data are placed independently and
a bare code address would leave the callee unable to find its own data. A
firmware routine that takes a callback therefore branches into the
module's data segment and faults.
So the ten entry points that can be handed a callback by a module resolve
the descriptor before storing or branching to it: qsort, bsearch,
pthread_create, signal, sigaction, task_create and task_create_with_stack,
task_spawn, pthread_once, scandir, and mq_notify and timer_create with
SIGEV_THREAD.
Which one resolves matters as much as that one does. Resolving twice would
take an already resolved code address for a descriptor and read two words
from the instruction stream, so each pointer is resolved exactly once, at
the outermost point that sees it. signal() passes its argument through
untouched because sigaction() and then nxsig_action() will resolve it,
which covers a module calling sigaction() directly as well. qsort() is
split so that the public entry resolves and the recursive implementation
does not. scandir() resolves its filter but not its comparison function,
which it hands to qsort().
Whether a caller is a module at all is asked of the PIC base register,
which up_initial_state() sets only for a task that has a D-Space. A plain
kernel task therefore reads zero and is left alone.
SIGEV_THREAD is the case the register cannot answer, because the callback
runs later on a work queue worker that carries no module's base at all.
The base is captured instead when the notification is registered, in the
module's own context, and installed around the call.
All of it is behind CONFIG_FDPIC, which defaults off. Built for
mps3-an547:picostest both ways; with it off the entry points compile to
what they were.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
atexit_call_exitfuncs() cached its loop bound on entry
(for (idx = aehead->nfuncs - 1; idx >= 0; idx--)), while
atexit_register() appends new entries at funcs[nfuncs] and bumps nfuncs.
Any function registered by an exit handler via atexit() / on_exit() /
__cxa_atexit() lands above the cached bound and is never invoked, even
though the registration returns OK.
This contradicts the exit(3) documentation that NuttX mirrors verbatim
in its own exit() docstring (libs/libc/stdlib/lib_exit.c):
It is possible for one of these functions to use atexit(3) or
on_exit(3) to register an additional function to be executed
during exit processing; the new registration is added to the
front of the list of functions that remain to be called.
The same restructure closes a second defect: atexit_call_exitfuncs()
read and cleared the task-group-shared ta_exit list without holding
ta_lock, while atexit_register() takes it ("The following must be
atomic"). Entries are now claimed under the lock and the handler is
invoked with the lock released, so a handler re-entering
atexit_register() cannot deadlock (also safe with the non-recursive
nxmutex used here).
Evidence: exit(3) man page, DESCRIPTION -
https://man7.org/linux/man-pages/man3/exit.3.html
NuttX mirrors this passage verbatim in its own exit() docstring --
5a209a853e/libs/libc/stdlib/lib_exit.c (L65-L70)
Before:
```
A handler that registers another function during exit processing
gets a success return from atexit(), but the new function is never
invoked - it lands above the loop bound cached on entry.
```
After:
```
A registration made during exit processing runs before the older
remaining handlers (order A -> B -> C below), matching the exit(3)
guarantee, and the list is consumed under ta_lock.
```
Testing:
Simulated (sim:nsh, CONFIG_LIBC_MAX_EXITFUNS=8).
Build and run:
```
cmake -B build -DBOARD_CONFIG=sim:nsh -GNinja
cmake -S . -B build # after setting CONFIG_LIBC_MAX_EXITFUNS=8
# in build/.config (sim:nsh default is 1)
cmake --build build -j$(nproc)
(echo hello; echo poweroff) | ./build/nuttx
```
"hello" runs the test at the NSH prompt; poweroff terminates the sim.
The test was carried by apps/examples/hello/hello_main.c (scratch only,
not part of this commit); its diff:
```
--- a/examples/hello/hello_main.c
+++ b/examples/hello/hello_main.c
@@ -24,6 +24,7 @@
#include <nuttx/config.h>
#include <stdio.h>
+#include <stdlib.h>
/****************************************************************************
* Public Functions
@@ -33,8 +34,29 @@
* hello_main
****************************************************************************/
+static void handler_b(void)
+{
+ printf("ATEXIT-TEST: handler B called (registered during exit)\n");
+}
+
+static void handler_a(void)
+{
+ int ret;
+
+ printf("ATEXIT-TEST: handler A called\n");
+ ret = atexit(handler_b);
+ printf("ATEXIT-TEST: atexit(handler_b) inside A returned %d\n", ret);
+}
+
+static void handler_c(void)
+{
+ printf("ATEXIT-TEST: handler C called\n");
+}
+
int main(int argc, FAR char *argv[])
{
printf("Hello, World!!\n");
+ atexit(handler_c); /* older entry, must run LAST */
+ atexit(handler_a); /* registers handler_b during exit */
return 0;
}
```
Before the fix:
```
Hello, World!!
ATEXIT-TEST: handler A called
ATEXIT-TEST: atexit(handler_b) inside A returned 0
ATEXIT-TEST: handler C called
```
(handler B is never invoked although its registration returned 0)
After the fix:
```
Hello, World!!
ATEXIT-TEST: handler A called
ATEXIT-TEST: atexit(handler_b) inside A returned 0
ATEXIT-TEST: handler B called (registered during exit)
ATEXIT-TEST: handler C called
```
Assisted-by: Claude Code (GLM-5.3) <claude@anthropic.com>
Signed-off-by: Junbo Zheng <zhengjunbo1@xiaomi.com>
The link support is no longer limited to the pseudo file system and now
covers both soft (symbolic) links and hard links across the VFS. Rename
the configuration option PSEUDOFS_SOFTLINKS to the more accurate FS_LINKS
and update all references in the source, headers, Kconfig, documentation
and board defconfigs accordingly.
This is a configuration rename; any out-of-tree defconfig that still
selects PSEUDOFS_SOFTLINKS must be updated to FS_LINKS.
Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
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>
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>
Every compiler supported by NuttX provides the "long long" types,
so the CONFIG_HAVE_LONG_LONG indirection is no longer useful.
Remove the option from include/nuttx/compiler.h and treat
"long long" as unconditionally available across the OS.
In addition to deleting the guard itself, this commit unconditionally
enables the long-long flavored helpers that used to be gated behind
it:
- libs/libc/fixedmath: drop the soft-emulated b32/ub32 routines
in lib_fixedmath.c (-261 lines) and trim the matching
prototypes, Make.defs and CMakeLists.txt entries; keep only
the long-long backed implementations.
- include/sys/endian.h, include/strings.h, libs/libc/string
/lib_ffsll.c, lib_flsll.c: always expose the 64-bit byte-swap
and ffsll/flsll variants.
- libs/libm/libm/lib_llround{,f,l}.c: drop the empty stubs.
- libs/libc/stdlib (atoll, llabs, lldiv, strtoll/ull, rand48,
strtold), libs/libc/stream (libvsprintf, libvscanf,
libbsprintf, ultoa_invert), libs/libc/misc (crc64, crc64emac),
libs/libc/inttypes/strtoimax, libs/libc/lzf, libs/libc/libc.csv,
libs/libc/string (memset, vikmemcpy): remove the
#ifdef CONFIG_HAVE_LONG_LONG branches.
- include/{stddef.h,stdlib.h,fixedmath.h,sys/epoll.h,cxx/cstdlib,
nuttx/audio/audio.h,nuttx/crc64.h,nuttx/lib/math.h,
nuttx/lib/math32.h,nuttx/lib/stdbit.h}: same guard cleanup.
- drivers/note/note_driver.c, fs/spiffs/src/spiffs.h,
sched/irq/irq_procfs.c: drop their local guards as well.
- Documentation/applications/netutils/ntpclient/index.rst:
refresh the documentation snippet.
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Introduce CONFIG_LIBC_DISABLE_HEXSTR_TO_FLOAT to remove support for
parsing hexadecimal floating-point constants (C99 %a) in strtod(),
strtof(), strtold(), and sscanf("%a"). This feature is rarely used in
embedded systems and its removal saves significant flash space. Decimal
float parsing remains unaffected.
Signed-off-by: Peter van der Perk <peter.vanderperk@nxp.com>
Currently the code for strtof, strtod and strtold is in the list
of files to compile unconditionally, but when trying to compile
kernel with toolchain without libm support (LIBM_NONE=y) compilation
fails because code refers math.h
Signed-off-by: Petro Karashchenko <petro.karashchenko@gmail.com>
remove reference to non-existent readme in libc.
Pointing to the documentation page doesn't make sense in this case,
because it doesn't explain the use of `#undef XXX` for this case anyway.
Signed-off-by: raiden00pl <raiden00@railab.me>
This reverts commit 8f96e59aee.
The implementation of __dso_handle is independent of the HOST OS but dependent on the specific compiler.
Therefore, this patch will cause compilation failures for other cross-compilers on the Windows platform, such as arm-none-eabi-gcc and aarch64-none-elf-gcc.
Constraints should be applied to compiler target rather than compiler host.
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>
remove the advertising clause
3. All advertising materials mentioning features or use of this software must
display the following acknowledgement: This product includes software
developed by the University of California, Berkeley and its contributors.
permitted by Berkley amendment
https://ipira.berkeley.edu/sites/default/files/amendment_of_4-clause_bsd_software_license.pdf
following example from NETBSD and OPENBSD
eb7c1594f16580fee329
Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.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>
When *pptr/ptr is "0" and base is 0, lib_checkbase() returns the error base 8(should be 10).
e.g. `strtoul("0", &endptr, 0);`
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
This commit adds support for reallocarray function. The functionality
is the same as for standard realloc, but the function also checks for
multiplication overflow and fails safely in case it occurs.
Signed-off-by: Michal Lenc <michallenc@seznam.cz>
The maximum startup parameters have been checked accordingly in nxtask_setup_stackargs(),
let us save argument counter to avoid limit check.
Signed-off-by: chao an <anchao@lixiang.com>
CPP: nuttx-names.in-> nuttx-names.dat
LD: nuttx
/usr/lib/gcc/x86_64-pc-msys/13.2.0/../../../../x86_64-pc-msys/bin/ld: nuttx.rel:/d/a/nuttx_windows/nuttx_windows/nuttxspace/nuttx/libs/libc/stdlib/lib_exit.c:48: multiple definition of `__dso_handle'; /usr/lib/gcc/x86_64-pc-msys/13.2.0/crtbegin.o:cygming-crtbeg:(.data+0x0): first defined here
Prototype:
unsigned long strtoul(FAR const char *nptr, FAR char **endptr, int base);
unsigned long long strtoull(FAR const char *nptr, FAR char **endptr, int base);
If endptr is not NULL, strtoul()/strtoull() should store the address of the first
invalid character in *endptr. And if the correct value is outside the range of
representable values, {ULONG_MAX} or {ULLONG_MAX} shall be returned and errno set
to [ERANGE].
With such code:
strtoul("34592348345343453453455645765736575865767", &endptr, 10);
It indeed returns ULONG_MAX and sets errno to ERANGE. But after strtoul
return, endptr points to "3455645765736575865767", not NULL.
Signed-off-by: Sunny <zxcvbnm37425@gmail.com>
1. Update all CMakeLists.txt to adapt to new layout
2. Fix cmake build break
3. Update all new file license
4. Fully compatible with current compilation environment(use configure.sh or cmake as you choose)
------------------
How to test
From within nuttx/. Configure:
cmake -B build -DBOARD_CONFIG=sim/nsh -GNinja
cmake -B build -DBOARD_CONFIG=sim:nsh -GNinja
cmake -B build -DBOARD_CONFIG=sabre-6quad/smp -GNinja
cmake -B build -DBOARD_CONFIG=lm3s6965-ek/qemu-flat -GNinja
(or full path in custom board) :
cmake -B build -DBOARD_CONFIG=$PWD/boards/sim/sim/sim/configs/nsh -GNinja
This uses ninja generator (install with sudo apt install ninja-build). To build:
$ cmake --build build
menuconfig:
$ cmake --build build -t menuconfig
--------------------------
2. cmake/build: reformat the cmake style by cmake-format
https://github.com/cheshirekow/cmake_format
$ pip install cmakelang
$ for i in `find -name CMakeLists.txt`;do cmake-format $i -o $i;done
$ for i in `find -name *\.cmake`;do cmake-format $i -o $i;done
Co-authored-by: Matias N <matias@protobits.dev>
Signed-off-by: chao an <anchao@xiaomi.com>
use PTHREAD_CLEANUP_STACKSIZE to enable or disable interfaces pthread_cleanup_push() and pthread_cleanup_pop().
reasons:(1)same as TLS_TASK_NELEM (2)it is no need to use two variables
Signed-off-by: yanghuatao <yanghuatao@xiaomi.com>
=================================================================
==2920138==ERROR: AddressSanitizer: invalid-pointer-pair: 0x603000000130 0x000000000000
#0 0x5602d3c6a89d in qsort stdlib/lib_qsort.c:180
#1 0x5602d3c28928 in romfs_cachenode romfs/fs_romfsutil.c:503
#2 0x5602d3c2854d in romfs_cachenode romfs/fs_romfsutil.c:486
#3 0x5602d3c2b056 in romfs_fsconfigure romfs/fs_romfsutil.c:777
#4 0x5602d3c24856 in romfs_bind romfs/fs_romfs.c:1111
#5 0x5602d3bf5179 in nx_mount mount/fs_mount.c:427
#6 0x5602d3bf5796 in mount mount/fs_mount.c:539
#7 0x5602d3bc1154 in nsh_romfsetc apps/nshlib/nsh_romfsetc.c:110
#8 0x5602d3b8f38d in nsh_initialize apps/nshlib/nsh_init.c:127
#9 0x5602d3b8f2b7 in nsh_main apps/system/nsh/nsh_main.c:69
#10 0x5602d3b7a3a6 in nxtask_startup sched/task_startup.c:70
#11 0x5602d3b5de89 in nxtask_start task/task_start.c:134
0x603000000130 is located 0 bytes inside of 32-byte region [0x603000000130,0x603000000150)
allocated by thread T0 here:
#0 0x7fcdac74793c in __interceptor_posix_memalign ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:226
#1 0x5602d3c9024e in host_memalign sim/posix/sim_hostmemory.c:180
#2 0x5602d3c907d2 in host_realloc sim/posix/sim_hostmemory.c:222
#3 0x5602d3b8aaff in mm_realloc sim/sim_heap.c:262
#4 0x5602d3b87a6a in realloc umm_heap/umm_realloc.c:91
#5 0x5602d3c280c4 in romfs_cachenode romfs/fs_romfsutil.c:466
#6 0x5602d3c2854d in romfs_cachenode romfs/fs_romfsutil.c:486
#7 0x5602d3c2b056 in romfs_fsconfigure romfs/fs_romfsutil.c:777
#8 0x5602d3c24856 in romfs_bind romfs/fs_romfs.c:1111
#9 0x5602d3bf5179 in nx_mount mount/fs_mount.c:427
#10 0x5602d3bf5796 in mount mount/fs_mount.c:539
#11 0x5602d3bc1154 in nsh_romfsetc apps/nshlib/nsh_romfsetc.c:110
#12 0x5602d3b8f38d in nsh_initialize apps/nshlib/nsh_init.c:127
#13 0x5602d3b8f2b7 in nsh_main apps/system/nsh/nsh_main.c:69
#14 0x5602d3b7a3a6 in nxtask_startup sched/task_startup.c:70
#15 0x5602d3b5de89 in nxtask_start task/task_start.c:134
Address 0x000000000000 is a wild pointer.
SUMMARY: AddressSanitizer: invalid-pointer-pair stdlib/lib_qsort.c:180 in qsort
==2920138==ABORTING
Aborted (core dumped)
Signed-off-by: chao an <anchao@xiaomi.com>
The POSIX standard dictates that during abnormal termination the functions
registered by atexit() are _not_ called, also flushing the streams is
optional. So in this case, it is perfectly legal / better to call the
kernel system call _exit() instead.
This fixes regression issues caused by removal exit() from the kernel.
Remove calls to the userspace API exit() from the kernel. The problem
with doing such calls is that the exit functions are called with kernel
mode privileges which is a big security no-no.
with the test of sim:lua ,I have fixed some bug of error output
1.input: " " and error output string is "NULL"
2.input: "1.2e+", error output string is "NULL"
3.input: "." error output string is "NULL"