From 30b3853ab3b54e22edf3372fd6aaf35d52d667e0 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 18 Aug 2026 18:33:02 +0200 Subject: selftests/nolibc: stop treating skipped tests as warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nolibc testsuite will always have some tests which get skipped. Due to this the full testsuite is always marked as 'warning', even though all testcases succeeded as expected. Stop treating skipped tests as warning. Signed-off-by: Thomas Weißschuh Acked-by: Willy Tarreau Link: https://patch.msgid.link/20260818-nolibc-test-result-warning-v1-1-d15cdda84ccd@weissschuh.net --- tools/testing/selftests/nolibc/Makefile.nolibc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/nolibc/Makefile.nolibc b/tools/testing/selftests/nolibc/Makefile.nolibc index f70c8dfca018..c790f937fe0a 100644 --- a/tools/testing/selftests/nolibc/Makefile.nolibc +++ b/tools/testing/selftests/nolibc/Makefile.nolibc @@ -225,7 +225,7 @@ include $(srctree)/tools/scripts/Makefile.include REPORT ?= awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{if (!f) printf("\n"); f++; print;} /\[SKIPPED\][\r]*$$/{s++} \ /^Total number of errors:/{done++} \ END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \ - if (f || !p || !done) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \ + if (f || !p || !done) printf("failure\n"); else printf("success\n");; \ printf("\nSee all results in %s\n", ARGV[1]); }' # Execute the toplevel kernel Makefile -- cgit v1.2.3 From 836cecd18332a3152f09e79b02e613b3982c2e9c Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 19 Aug 2026 08:31:51 +0200 Subject: tools/nolibc: split the architecture list into multiple lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The list is getting long, split it up for easier additions. Signed-off-by: Thomas Weißschuh Acked-by: Willy Tarreau Reviewed-by: Brian Cain Link: https://patch.msgid.link/20260819-nolibc-hexagon-v1-1-6bc3be591f09@weissschuh.net --- tools/include/nolibc/Makefile | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index 6d213d372bf8..b5897b3964f8 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -17,7 +17,22 @@ endif # it defaults to this nolibc directory. OUTPUT ?= $(CURDIR)/ -architectures := alpha arm arm64 loongarch m68k mips openrisc parisc powerpc riscv s390 sh sparc x86 +architectures := \ + alpha \ + arm \ + arm64 \ + loongarch \ + m68k \ + mips \ + openrisc \ + parisc \ + powerpc \ + riscv \ + s390 \ + sh \ + sparc \ + x86 \ + arch_files := arch.h $(addsuffix .h, $(addprefix arch-, $(architectures))) all_files := \ alloca.h \ -- cgit v1.2.3 From 4b4f56583850ba13f576f1e34f1b9e217c003773 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 19 Aug 2026 08:31:52 +0200 Subject: selftests/nolibc: prepare for clang-only architectures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contrary to other architectures currently supported by nolibc, hexagon can only be built with clang. Prepare run-tests.sh to allow for this. Signed-off-by: Thomas Weißschuh Acked-by: Willy Tarreau Reviewed-by: Brian Cain Link: https://patch.msgid.link/20260819-nolibc-hexagon-v1-2-6bc3be591f09@weissschuh.net --- tools/testing/selftests/nolibc/run-tests.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/nolibc/run-tests.sh b/tools/testing/selftests/nolibc/run-tests.sh index dc0b1649c641..5732b956ead9 100755 --- a/tools/testing/selftests/nolibc/run-tests.sh +++ b/tools/testing/selftests/nolibc/run-tests.sh @@ -132,6 +132,12 @@ crosstool_abi() { esac } +need_gcc() { + case "$1" in + *) echo "1";; + esac +} + download_crosstool() { arch="$(crosstool_arch "$1")" abi="$(crosstool_abi "$1")" @@ -164,14 +170,18 @@ test_arch() { arch=$1 ct_arch=$(crosstool_arch "$arch") ct_abi=$(crosstool_abi "$1") + gcc="$(need_gcc "$1")" + cross_compile= - if [ ! -d "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/." ]; then + if [ -n "$gcc" ] && [ ! -d "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/." ]; then echo "No toolchain found in ${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}." echo "Did you install the toolchains or set the correct arch ? Rerun with -h for help." return 1 fi - cross_compile=$(realpath "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/${ct_arch}-${ct_abi}-") + if [ -n "$gcc" ]; then + cross_compile=$(realpath "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/${ct_arch}-${ct_abi}-") + fi build_dir="${build_location}/${arch}" if [ "$werror" -ne 0 ]; then CFLAGS_EXTRA="$CFLAGS_EXTRA -Werror -Wl,--fatal-warnings" -- cgit v1.2.3 From 3842a1f486a05c12b2a8712546858e6872431292 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 19 Aug 2026 08:31:53 +0200 Subject: tools/nolibc: add support for hexagon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A mostly straightforward new architecture with a few quirks: * Only qemu-user is supported for testing. * Clang is required for compilation. * -fsanitize=undefined is broken. Signed-off-by: Thomas Weißschuh Acked-by: Willy Tarreau Reviewed-by: Brian Cain Link: https://patch.msgid.link/20260819-nolibc-hexagon-v1-3-6bc3be591f09@weissschuh.net --- tools/include/nolibc/Makefile | 1 + tools/include/nolibc/arch-hexagon.h | 164 +++++++++++++++++++++++++ tools/include/nolibc/arch.h | 2 + tools/testing/selftests/nolibc/Makefile.nolibc | 8 ++ tools/testing/selftests/nolibc/run-tests.sh | 6 + 5 files changed, 181 insertions(+) create mode 100644 tools/include/nolibc/arch-hexagon.h diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index b5897b3964f8..c4a0879e4140 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -21,6 +21,7 @@ architectures := \ alpha \ arm \ arm64 \ + hexagon \ loongarch \ m68k \ mips \ diff --git a/tools/include/nolibc/arch-hexagon.h b/tools/include/nolibc/arch-hexagon.h new file mode 100644 index 000000000000..0767054882f3 --- /dev/null +++ b/tools/include/nolibc/arch-hexagon.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * hexagon specific definitions for NOLIBC + * Copyright (C) 2026 Thomas Weißschuh + */ + +#ifndef _NOLIBC_ARCH_HEXAGON_H +#define _NOLIBC_ARCH_HEXAGON_H + +#include + +#include "compiler.h" +#include "crt.h" + +/* + * Syscalls for hexagon: + * - syscall number is passed in r6 + * - arguments are in r0, r1, r2, r3, r4, r5 + * - the system call is performed by calling trap0(#1) + * - syscall return value is in r0 + */ + +#define _NOLIBC_SYSCALL_CLOBBERLIST "memory" + +#define __nolibc_syscall0(num) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0"); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "=r"(_arg1) \ + : "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall1(num, arg1) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall2(num, arg1, arg2) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall3(num, arg1, arg2, arg3) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall4(num, arg1, arg2, arg3, arg4) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + register long _arg5 __asm__ ("r4") = (long)(arg5); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + register long _arg5 __asm__ ("r4") = (long)(arg5); \ + register long _arg6 __asm__ ("r5") = (long)(arg6); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#ifndef NOLIBC_NO_RUNTIME +/* startup code */ +void __attribute__((weak, noreturn)) +__nolibc_entrypoint __nolibc_no_stack_protector +_start(void) +{ + __asm__ volatile ( + "r0 = sp\n" /* save stack pointer to r0, as arg1 of _start_c */ + "call _start_c\n" /* transfer to c runtime */ + ); + __nolibc_entrypoint_epilogue(); +} +#endif /* NOLIBC_NO_RUNTIME */ + +static __attribute__((unused)) +int _sys_ftruncate64(int fd, uint32_t length0, uint32_t length1) +{ + return __nolibc_syscall4(__NR_ftruncate64, fd, 0, length0, length1); +} +#define _sys_ftruncate64 _sys_ftruncate64 + +#endif /* _NOLIBC_ARCH_HEXAGON_H */ diff --git a/tools/include/nolibc/arch.h b/tools/include/nolibc/arch.h index 06cc2dbe7a33..24e0ec4fb2db 100644 --- a/tools/include/nolibc/arch.h +++ b/tools/include/nolibc/arch.h @@ -34,6 +34,8 @@ #include "arch-parisc.h" #elif defined(__alpha__) #include "arch-alpha.h" +#elif defined(__hexagon__) +#include "arch-hexagon.h" #else #error Unsupported Architecture #endif diff --git a/tools/testing/selftests/nolibc/Makefile.nolibc b/tools/testing/selftests/nolibc/Makefile.nolibc index c790f937fe0a..d8b71cbfc173 100644 --- a/tools/testing/selftests/nolibc/Makefile.nolibc +++ b/tools/testing/selftests/nolibc/Makefile.nolibc @@ -206,6 +206,8 @@ CFLAGS_mips64be = -EB -mabi=64 -march=mips64r2 CFLAGS_loongarch = $(if $(LLVM),-fuse-ld=lld) CFLAGS_sparc32 = $(call cc-option,-m32) -mcpu=v8 CFLAGS_sh4 = -ml -m4 +# github.com/llvm/llvm-project/issues/216765 +CFLAGS_hexagon = -fno-sanitize=undefined ifeq ($(origin XARCH),command line) CFLAGS_XARCH = $(CFLAGS_$(XARCH)) endif @@ -222,6 +224,12 @@ LDFLAGS := # Modify CFLAGS based on LLVM= include $(srctree)/tools/scripts/Makefile.include +ifeq ($(ARCH),hexagon) +# tools/scripts/Makefile.include requires CROSS_COMPILE which does not exist for hexagon +CFLAGS += --target=hexagon-linux-musl +CLANG_CROSS_FLAGS += --target=hexagon-linux-musl +endif + REPORT ?= awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{if (!f) printf("\n"); f++; print;} /\[SKIPPED\][\r]*$$/{s++} \ /^Total number of errors:/{done++} \ END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \ diff --git a/tools/testing/selftests/nolibc/run-tests.sh b/tools/testing/selftests/nolibc/run-tests.sh index 5732b956ead9..3da806e2b302 100755 --- a/tools/testing/selftests/nolibc/run-tests.sh +++ b/tools/testing/selftests/nolibc/run-tests.sh @@ -31,6 +31,7 @@ all_archs=( sh4 parisc32 alpha + hexagon ) archs="${all_archs[@]}" @@ -134,6 +135,7 @@ crosstool_abi() { need_gcc() { case "$1" in + hexagon);; *) echo "1";; esac } @@ -212,6 +214,10 @@ test_arch() { echo "Unsupported configuration" return fi + if [ "$arch" = "hexagon" ] && [ -z "$llvm" -o "$test_mode" = "system" ]; then + echo "Unsupported configuration" + return + fi mkdir -p "$build_dir" swallow_output "${MAKE[@]}" defconfig -- cgit v1.2.3 From 4c00540add56942d621a941481cf9d0fb7dcec0e Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 25 Aug 2026 22:41:33 +0200 Subject: selftests/nolibc: switch around the skip conditions in run-tests.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the potentially long list of affected architectures to the end of the conditional to make the logic easier to understand. Suggested-by: David Laight Link: https://lore.kernel.org/lkml/20260824091958.7810094f@pumpkin/ Signed-off-by: Thomas Weißschuh --- tools/testing/selftests/nolibc/run-tests.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/nolibc/run-tests.sh b/tools/testing/selftests/nolibc/run-tests.sh index 3da806e2b302..af2a6f510d1d 100755 --- a/tools/testing/selftests/nolibc/run-tests.sh +++ b/tools/testing/selftests/nolibc/run-tests.sh @@ -206,15 +206,15 @@ test_arch() { exit 1 esac printf '%-15s' "$arch:" - if [ "$arch" = "m68k" -o "$arch" = "sh4" -o "$arch" = "openrisc" -o "$arch" = "parisc32" -o "$arch" = "alpha" ] && [ "$llvm" = "1" ]; then + if [ "$llvm" = "1" ] && [ "$arch" = "m68k" -o "$arch" = "sh4" -o "$arch" = "openrisc" -o "$arch" = "parisc32" -o "$arch" = "alpha" ]; then echo "Unsupported configuration" return fi - if [ "$arch" = "x32" ] && [ "$test_mode" = "user" ]; then + if [ "$test_mode" = "user" ] && [ "$arch" = "x32" ]; then echo "Unsupported configuration" return fi - if [ "$arch" = "hexagon" ] && [ -z "$llvm" -o "$test_mode" = "system" ]; then + if [ -z "$llvm" -o "$test_mode" = "system" ] && [ "$arch" = "hexagon" ]; then echo "Unsupported configuration" return fi -- cgit v1.2.3 From c0da8b53603ad84f622b2c5f6c1a69f7d4b4b8bc Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Fri, 28 Aug 2026 19:32:04 +0900 Subject: tools/nolibc: Add sendfile() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is useful because it allows copying data up to 2GB from one fd to another without a buffer in userspace and with a single syscall. For 32bit kernels we use the sendfile64 syscall to allow for 64bit offsets to work. Signed-off-by: Daniel Palmer Link: https://patch.msgid.link/20260828103205.384589-2-daniel@thingy.jp Signed-off-by: Thomas Weißschuh --- tools/include/nolibc/Makefile | 1 + tools/include/nolibc/nolibc.h | 1 + tools/include/nolibc/sys/sendfile.h | 41 +++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 tools/include/nolibc/sys/sendfile.h diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index c4a0879e4140..0880be0f6ef5 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -75,6 +75,7 @@ all_files := \ sys/reboot.h \ sys/resource.h \ sys/select.h \ + sys/sendfile.h \ sys/stat.h \ sys/syscall.h \ sys/sysmacros.h \ diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h index faa94f247281..352a207bbedc 100644 --- a/tools/include/nolibc/nolibc.h +++ b/tools/include/nolibc/nolibc.h @@ -106,6 +106,7 @@ #include "sys/reboot.h" #include "sys/resource.h" #include "sys/select.h" +#include "sys/sendfile.h" #include "sys/stat.h" #include "sys/syscall.h" #include "sys/sysmacros.h" diff --git a/tools/include/nolibc/sys/sendfile.h b/tools/include/nolibc/sys/sendfile.h new file mode 100644 index 000000000000..ac7d85847718 --- /dev/null +++ b/tools/include/nolibc/sys/sendfile.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * sendfile for NOLIBC + * Copyright (C) 2026 Daniel Palmer + */ + +/* make sure to include all global symbols */ +#include "../nolibc.h" + +#ifndef _NOLIBC_SYS_SENDFILE_H +#define _NOLIBC_SYS_SENDFILE_H + +#include "../sys.h" +#include + +/* + * ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); + */ + +static __attribute__((unused)) +ssize_t _sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count) +{ + __nolibc_static_assert(sizeof(*offset) == sizeof(__kernel_loff_t)); + +#ifdef __NR_sendfile64 + /* 32-bit applications use sendfile64 so offset is treated as a __kernel_loff_t */ + return __nolibc_syscall4(__NR_sendfile64, out_fd, in_fd, offset, count); +#else + __nolibc_static_assert(sizeof(*offset) == sizeof(__kernel_off_t)); + + return __nolibc_syscall4(__NR_sendfile, out_fd, in_fd, offset, count); +#endif +} + +static __attribute__((unused)) +ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count) +{ + return __sysret(_sys_sendfile(out_fd, in_fd, offset, count)); +} + +#endif /* _NOLIBC_SYS_SENDFILE_H */ -- cgit v1.2.3 From 9c47af906bc655c8f45aaf1f156656239c2c5073 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Fri, 28 Aug 2026 19:32:05 +0900 Subject: selftests/nolibc: Add basic test for sendfile() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Basic smoke test for sendfile(). Signed-off-by: Daniel Palmer Link: https://patch.msgid.link/20260828103205.384589-3-daniel@thingy.jp [Thomas: add '#include sys/syscall.h', simplify test cleanup a bit] Signed-off-by: Thomas Weißschuh --- tools/testing/selftests/nolibc/nolibc-test.c | 68 ++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index ed860b0a15a1..0a8fe5100b7f 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1563,6 +1564,72 @@ out: return ret; } +int test_sendfile(void) +{ + char data_in[] = "This is some data"; + const size_t data_sz = sizeof(data_in); + char data_out[data_sz + 10]; + int in_fd, out_fd; + int ret; + + /* Create two tmp files */ + in_fd = open("/tmp", O_TMPFILE | O_RDWR, 0644); + out_fd = open("/tmp", O_TMPFILE | O_RDWR, 0644); + + if (in_fd == -1 || out_fd == -1) { + ret = __LINE__; + goto out; + } + + /* Populate the "in" file */ + ret = write(in_fd, data_in, data_sz); + if (ret != data_sz) { + ret = __LINE__; + goto out; + } + + /* Rewind the "in" file */ + if (lseek(in_fd, 0, SEEK_SET)) { + ret = __LINE__; + goto out; + } + + /* Use sendfile() to copy "in" to "out" */ + ret = sendfile(out_fd, in_fd, NULL, data_sz + 5); + if (ret != data_sz) { + ret = __LINE__; + goto out; + } + + /* Rewind the "out" file */ + if (lseek(out_fd, 0, SEEK_SET)) { + ret = __LINE__; + goto out; + } + + /* Read back the transferred data */ + ret = read(out_fd, data_out, sizeof(data_out)); + if (ret != data_sz) { + ret = __LINE__; + goto out; + } + + /* Check we have the same data in both files */ + if (memcmp(data_out, data_in, data_sz)) { + ret = __LINE__; + goto out; + } + + /* Test passed */ + ret = 0; + +out: + close(out_fd); + close(in_fd); + + return ret; +} + /* Run syscall tests between IDs and . * Return 0 on success, non-zero on failure. */ @@ -1684,6 +1751,7 @@ int run_syscall(int min, int max) CASE_TEST(select_null); EXPECT_SYSZR(1, ({ struct timeval tv = { 0 }; select(0, NULL, NULL, NULL, &tv); })); break; CASE_TEST(select_stdout); EXPECT_SYSNE(1, ({ fd_set fds; FD_ZERO(&fds); FD_SET(1, &fds); select(2, NULL, &fds, NULL, NULL); }), -1); break; CASE_TEST(select_fault); EXPECT_SYSER(1, select(1, (void *)1, NULL, NULL, 0), -1, EFAULT); break; + CASE_TEST(sendfile); EXPECT_SYSZR(1, test_sendfile()); break; CASE_TEST(stat_blah); EXPECT_SYSER(1, stat("/proc/self/blah", &stat_buf), -1, ENOENT); break; CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break; CASE_TEST(stat_rdev); EXPECT_SYSZR(1, ({ int ret = stat("/dev/null", &stat_buf); ret ?: stat_buf.st_rdev != makedev(1, 3); })); break; -- cgit v1.2.3 From 9e697dcb8cc01f708cacc4ef3d8b6fb8a67dcd54 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Mon, 31 Aug 2026 18:04:59 +0200 Subject: tools/nolibc: verify that a directory is opened MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a non-directory is opened, ENODIR should be returned from opendir()/fdopendir() right away and not only during readdir_r(). Validate the type of opened file during open. Signed-off-by: Thomas Weißschuh Reviewed-by: Willy Tarreau Link: https://patch.msgid.link/20260831-nolibc-fdopendir-enotdir-v1-1-8cf0e79c4e6f@weissschuh.net --- tools/include/nolibc/dirent.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/include/nolibc/dirent.h b/tools/include/nolibc/dirent.h index 4e02ef25e72d..25fbff208998 100644 --- a/tools/include/nolibc/dirent.h +++ b/tools/include/nolibc/dirent.h @@ -30,10 +30,23 @@ typedef struct { static __attribute__((unused)) DIR *fdopendir(int fd) { + struct stat buf; + int ret; + if (fd < 0) { SET_ERRNO(EBADF); return NULL; } + + ret = fstat(fd, &buf); + if (ret < 0) + return NULL; + + if (!S_ISDIR(buf.st_mode)) { + SET_ERRNO(ENOTDIR); + return NULL; + } + return (DIR *)(intptr_t)~fd; } -- cgit v1.2.3 From 6a6c14d2664e441f016b3b5f0d0b77d4aca2f54c Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Mon, 31 Aug 2026 18:05:00 +0200 Subject: selftests/nolibc: validate ENOTDIR return values from opendir()/fdopendir() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure that ENOTDIR is detected already during directory opening. Signed-off-by: Thomas Weißschuh Reviewed-by: Willy Tarreau Link: https://patch.msgid.link/20260831-nolibc-fdopendir-enotdir-v1-2-8cf0e79c4e6f@weissschuh.net --- tools/testing/selftests/nolibc/nolibc-test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 0a8fe5100b7f..7091f63f3b25 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1711,6 +1711,7 @@ int run_syscall(int min, int max) CASE_TEST(execve_root); EXPECT_SYSER(1, execve("/", (char*[]){ [0] = (char []){"/"}, [1] = NULL }, NULL), -1, EACCES); break; CASE_TEST(fchdir_stdin); EXPECT_SYSER(1, fchdir(STDIN_FILENO), -1, ENOTDIR); break; CASE_TEST(fchdir_badfd); EXPECT_SYSER(1, fchdir(-1), -1, EBADF); break; + CASE_TEST(fdopendir_notdir); EXPECT_SYSER(1, (uintptr_t)fdopendir(STDIN_FILENO), (uintptr_t)NULL, ENOTDIR); break; CASE_TEST(file_stream); EXPECT_SYSZR(1, test_file_stream()); break; CASE_TEST(file_stream_wsr); EXPECT_SYSZR(1, test_file_stream_wsr()); break; CASE_TEST(fork); EXPECT_SYSZR(1, test_fork(FORK_STANDARD)); break; @@ -1739,6 +1740,7 @@ int run_syscall(int min, int max) CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", O_RDONLY), -1, ENOENT); if (tmp != -1) close(tmp); break; CASE_TEST(openat_dir); EXPECT_SYSZR(1, test_openat()); break; CASE_TEST(open_mode); EXPECT_SYSZR(1, test_open_mode()); break; + CASE_TEST(opendir_notdir); EXPECT_SYSER(1, (uintptr_t)opendir("/dev/stdin"), (uintptr_t)NULL, ENOTDIR); break; CASE_TEST(pipe); EXPECT_SYSZR(1, test_pipe()); break; CASE_TEST(poll_null); EXPECT_SYSZR(1, poll(NULL, 0, 0)); break; CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break; -- cgit v1.2.3 From 6ef847683e7d294a4187a7253924b8f0d8911416 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Mon, 31 Aug 2026 18:05:01 +0200 Subject: tools/nolibc: validate directory with O_DIRECTORY in opendir() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fdopendir() requires a call to fstat() to determine if the opened file is a directory. Currently opendir() inherits this extra syscall. Switch to O_DIRECTORY and remove the call to fdopendir() in opendir() to make the directory type check cheaper. Signed-off-by: Thomas Weißschuh Reviewed-by: Willy Tarreau Link: https://patch.msgid.link/20260831-nolibc-fdopendir-enotdir-v1-3-8cf0e79c4e6f@weissschuh.net --- tools/include/nolibc/dirent.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/include/nolibc/dirent.h b/tools/include/nolibc/dirent.h index 25fbff208998..2dbf4052b85a 100644 --- a/tools/include/nolibc/dirent.h +++ b/tools/include/nolibc/dirent.h @@ -55,10 +55,11 @@ DIR *opendir(const char *name) { int fd; - fd = open(name, O_RDONLY); + fd = open(name, O_RDONLY | O_DIRECTORY); if (fd == -1) return NULL; - return fdopendir(fd); + + return (DIR *)(intptr_t)~fd; } static __attribute__((unused)) -- cgit v1.2.3