diff --git a/lib/libc/tests/gen/Makefile b/lib/libc/tests/gen/Makefile index f5d57275cb44..8a198b5dfb2b 100644 --- a/lib/libc/tests/gen/Makefile +++ b/lib/libc/tests/gen/Makefile @@ -1,5 +1,7 @@ .include +SUBDIR+= libdummy + ATF_TESTS_C+= arc4random_test ATF_TESTS_C+= dir2_test ATF_TESTS_C+= dlopen_empty_test @@ -114,12 +116,12 @@ TESTS_SUBDIRS= execve TESTS_SUBDIRS+= posix_spawn # Tests that require address sanitizer -.if ${COMPILER_FEATURES:Masan} -.for t in scandir_test realpath2_test +#.if ${COMPILER_FEATURES:Masan} +.for t in posix_spawn_test scandir_test realpath2_test CFLAGS.${t}.c+= -fsanitize=address LDFLAGS.${t}+= -fsanitize=address .endfor -.endif +#.endif # Tests that require blocks support .for t in fts_blocks_test glob_blocks_test scandir_blocks_test diff --git a/lib/libc/tests/gen/libdummy/Makefile b/lib/libc/tests/gen/libdummy/Makefile new file mode 100644 index 000000000000..351b188e5642 --- /dev/null +++ b/lib/libc/tests/gen/libdummy/Makefile @@ -0,0 +1,10 @@ +SHLIB?= dummy +SHLIB_MAJOR= 0 +SONAME?= libdummy.so.XXX + +LIBDIR= ${TESTSBASE}/lib/libc/gen +SHLIBDIR= ${TESTSBASE}/lib/libc/gen + +SRCS= libdummy.c + +.include diff --git a/lib/libc/tests/gen/libdummy/libdummy.c b/lib/libc/tests/gen/libdummy/libdummy.c new file mode 100644 index 000000000000..89e94c016311 --- /dev/null +++ b/lib/libc/tests/gen/libdummy/libdummy.c @@ -0,0 +1,14 @@ +/* + * + * Copyright (C) 2026 Kyle Evans + * + * SPDX-license-Identifier: BSD-2-Clause + */ + +int dummy_random(void); + +int +dummy_random(void) +{ + return (42); +} diff --git a/lib/libc/tests/gen/posix_spawn_test.c b/lib/libc/tests/gen/posix_spawn_test.c index 22133cf1d59a..031f6b3de37f 100644 --- a/lib/libc/tests/gen/posix_spawn_test.c +++ b/lib/libc/tests/gen/posix_spawn_test.c @@ -30,8 +30,10 @@ */ #include +#include #include #include +#include #include #include #include @@ -173,6 +175,70 @@ ATF_TC_BODY(posix_spawnp_eacces, tc) } } +#define NUM_DSO 128 +ATF_TC_WITHOUT_HEAD(posix_spawnp_stackoverflow); +ATF_TC_BODY(posix_spawnp_stackoverflow, tc) +{ + struct stat sb; + char dsopath[MAXPATHLEN]; + char *myargs[] = { "true", NULL }; + void **handles; + char *dsomap, *dsoverpos; + size_t dsosz; + int error, fd, nfd, status; + pid_t pid, waitres; + + /* Make sure we have no child processes. */ + while (waitpid(-1, NULL, 0) != -1) + ; + ATF_REQUIRE_MSG(errno == ECHILD, "errno was not ECHILD: %d", errno); + + (void)snprintf(dsopath, sizeof(dsopath), "%s/libdummy.so", + atf_tc_get_config_var(tc, "srcdir")); + + fd = open(dsopath, O_RDWR); + ATF_REQUIRE(fd >= 0); + ATF_REQUIRE(fstat(fd, &sb) == 0); + + dsosz = sb.st_size; + dsomap = mmap(NULL, dsosz, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd, 0); + ATF_REQUIRE(dsomap != MAP_FAILED); + + handles = calloc(sizeof(*handles), NUM_DSO); + ATF_REQUIRE(handles != NULL); + + dsoverpos = memmem(dsomap, dsosz, ".XXX", 4); + ATF_REQUIRE(dsoverpos != NULL); + dsoverpos++; + + for (int i = 0; i < NUM_DSO; i++) { + char newver[4]; /* 3 digits + NUL */ + + nfd = memfd_create("dsobase", MFD_CLOEXEC); + ATF_REQUIRE(nfd >= 0); + ATF_REQUIRE(ftruncate(nfd, dsosz) == 0); + + /* Write a new version. */ + snprintf(newver, sizeof(newver), "%.3d", i); + memcpy(dsoverpos, newver, 3); + + ATF_REQUIRE(write(nfd, dsomap, dsosz) == dsosz); + + handles[i] = fdlopen(nfd, RTLD_LAZY); + ATF_REQUIRE(handles[i] != NULL); + if (i > 0) + ATF_REQUIRE(handles[i] != handles[i - 1]); + } + + /* Simple test. */ + error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs, myenv); + ATF_REQUIRE(error == 0); + waitres = waitpid(pid, &status, 0); + ATF_REQUIRE(waitres == pid); + ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + ATF_TP_ADD_TCS(tp) { @@ -181,6 +247,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback); ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback_null_argv0); ATF_TP_ADD_TC(tp, posix_spawnp_eacces); + ATF_TP_ADD_TC(tp, posix_spawnp_stackoverflow); return (atf_no_error()); }