git: 7a1ade5109ac - main - system(3): Write our own tests
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 25 Feb 2026 21:12:54 UTC
The branch main has been updated by des:
URL: https://cgit.FreeBSD.org/src/commit/?id=7a1ade5109ac57d1f59eaa75b5d0f13fabecf6ba
commit 7a1ade5109ac57d1f59eaa75b5d0f13fabecf6ba
Author: Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-02-25 21:12:29 +0000
Commit: Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-02-25 21:12:29 +0000
system(3): Write our own tests
Replace the somewhat perfunctory NetBSD tests with our own.
MFC after: 1 week
Sponsored by: Klara, Inc.
Reviewed by: bnovkov, kevans
Differential Revision: https://reviews.freebsd.org/D55482
---
lib/libc/tests/stdlib/Makefile | 3 +-
lib/libc/tests/stdlib/system_test.c | 164 ++++++++++++++++++++++++++++++++++++
2 files changed, 166 insertions(+), 1 deletion(-)
diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile
index 9d84becfbd1f..a714a8cab774 100644
--- a/lib/libc/tests/stdlib/Makefile
+++ b/lib/libc/tests/stdlib/Makefile
@@ -17,6 +17,7 @@ ATF_TESTS_C+= qsort_s_test
ATF_TESTS_C+= qsort_bench
ATF_TESTS_C+= set_constraint_handler_s_test
ATF_TESTS_C+= strfmon_test
+ATF_TESTS_C+= system_test
ATF_TESTS_C+= tsearch_test
ATF_TESTS_CXX+= cxa_thread_atexit_test
ATF_TESTS_CXX+= cxa_thread_atexit_nothr_test
@@ -44,7 +45,6 @@ NETBSD_ATF_TESTS_C+= posix_memalign_test
NETBSD_ATF_TESTS_C+= random_test
NETBSD_ATF_TESTS_C+= strtod_test
NETBSD_ATF_TESTS_C+= strtol_test
-NETBSD_ATF_TESTS_C+= system_test
# TODO: need to come up with a correct explanation of what the patch pho does
# with h_atexit
@@ -78,6 +78,7 @@ LIBADD.${t}+= netbsd util
LIBADD.libc_exit_test+= pthread
LIBADD.strtod_test+= m
+LIBADD.system_test+= pthread
SUBDIR+= dynthr_mod
SUBDIR+= libatexit
diff --git a/lib/libc/tests/stdlib/system_test.c b/lib/libc/tests/stdlib/system_test.c
new file mode 100644
index 000000000000..713e4bb0f87a
--- /dev/null
+++ b/lib/libc/tests/stdlib/system_test.c
@@ -0,0 +1,164 @@
+/*-
+ * Copyright (c) 2026 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC(system_true);
+ATF_TC_HEAD(system_true, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "system(\"true\")");
+}
+ATF_TC_BODY(system_true, tc)
+{
+ ATF_REQUIRE_EQ(W_EXITCODE(0, 0), system("true"));
+}
+
+ATF_TC(system_false);
+ATF_TC_HEAD(system_false, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "system(\"false\")");
+}
+ATF_TC_BODY(system_false, tc)
+{
+ ATF_REQUIRE_EQ(W_EXITCODE(1, 0), system("false"));
+}
+
+ATF_TC(system_touch);
+ATF_TC_HEAD(system_touch, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "system(\"touch file\")");
+}
+ATF_TC_BODY(system_touch, tc)
+{
+ /* The file does not exist */
+ ATF_CHECK_ERRNO(ENOENT, unlink("file"));
+
+ /* Run a command that creates it */
+ ATF_REQUIRE_EQ(W_EXITCODE(0, 0), system("touch file"));
+
+ /* Now the file exists */
+ ATF_CHECK_EQ(0, unlink("file"));
+}
+
+ATF_TC(system_null);
+ATF_TC_HEAD(system_null, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "system(NULL)");
+}
+ATF_TC_BODY(system_null, tc)
+{
+ ATF_REQUIRE_EQ(1, system(NULL));
+}
+
+/*
+ * Define PROCMASK_IS_THREADMASK if sigprocmask() gets / sets the thread
+ * mask in multithreaded programs, which makes it impossible to verify
+ * that system(3) correctly blocks and unblocks SIGCHLD.
+ */
+#ifdef __FreeBSD__
+#define PROCMASK_IS_THREADMASK 1
+#endif
+
+static void *
+system_thread(void *arg)
+{
+ char cmd[64];
+ int i = (int)(intptr_t)arg;
+
+ snprintf(cmd, sizeof(cmd), "rm flag%d ; lockf -ns lock%d true", i, i);
+ return ((void *)(intptr_t)system(cmd));
+}
+
+ATF_TC(system_concurrent);
+ATF_TC_HEAD(system_concurrent, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Concurrent calls");
+}
+ATF_TC_BODY(system_concurrent, tc)
+{
+#define N 3
+ sigset_t normset, sigset;
+ pthread_t thr[N];
+ char fn[8];
+ int fd[N];
+ void *arg, *ret;
+
+ /* Create and lock the locks */
+ for (int i = 0; i < N; i++) {
+ snprintf(fn, sizeof(fn), "lock%d", i);
+ fd[i] = open(fn, O_CREAT|O_EXCL|O_EXLOCK|O_CLOEXEC, 0644);
+ ATF_REQUIRE_MSG(fd[i] >= 0, "%s: %m", fn);
+ }
+
+ /* Create the flags */
+ for (int i = 0; i < N; i++) {
+ snprintf(fn, sizeof(fn), "flag%d", i);
+ ATF_REQUIRE_EQ(0, symlink(fn, fn));
+ }
+
+ /* Get the current and expected signal mask */
+ sigprocmask(0, NULL, &normset);
+
+ /* Spawn threads which block on these files */
+ for (int i = 0; i < N; i++) {
+ arg = (void *)(intptr_t)i;
+ ATF_REQUIRE_INTEQ(0,
+ pthread_create(&thr[i], NULL, system_thread, arg));
+ }
+
+ /* Wait until the flags are gone */
+ for (int i = 0; i < N; i++) {
+ snprintf(fn, sizeof(fn), "flag%d", i);
+ while (readlink(fn, fn, sizeof(fn)) > 0)
+ usleep(10000);
+ ATF_REQUIRE_EQ(ENOENT, errno);
+ }
+
+ /* Release the locks */
+ for (int i = 0; i < N; i++) {
+ /* Check the signal dispositions */
+ ATF_CHECK_EQ(SIG_IGN, signal(SIGINT, SIG_IGN));
+ ATF_CHECK_EQ(SIG_IGN, signal(SIGQUIT, SIG_IGN));
+#ifndef PROCMASK_IS_THREADMASK
+ sigprocmask(0, NULL, &sigset);
+ ATF_CHECK(sigismember(&sigset, SIGCHLD));
+#endif
+
+ /* Close the file, releasing the lock */
+ ATF_REQUIRE_INTEQ(0, close(fd[i]));
+
+ /* Join the thread and check the return value */
+ ATF_CHECK_INTEQ(0, pthread_join(thr[i], &ret));
+ ATF_CHECK_INTEQ(W_EXITCODE(0, 0), (int)(intptr_t)ret);
+ }
+
+ /* Check the signal dispositions */
+ ATF_CHECK_EQ(SIG_DFL, signal(SIGINT, SIG_DFL));
+ ATF_CHECK_EQ(SIG_DFL, signal(SIGQUIT, SIG_DFL));
+ sigprocmask(0, NULL, &sigset);
+ ATF_CHECK_EQ(0, memcmp(&sigset, &normset, sizeof(sigset_t)));
+#undef N
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, system_true);
+ ATF_TP_ADD_TC(tp, system_false);
+ ATF_TP_ADD_TC(tp, system_touch);
+ ATF_TP_ADD_TC(tp, system_null);
+ ATF_TP_ADD_TC(tp, system_concurrent);
+ return (atf_no_error());
+}