git: 9c6ed9a88d3a - main - stress2: Added new test scenarios
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 14 Jul 2026 07:29:01 UTC
The branch main has been updated by pho:
URL: https://cgit.FreeBSD.org/src/commit/?id=9c6ed9a88d3afb8bc9aba7cf852cc36e03bcd34c
commit 9c6ed9a88d3afb8bc9aba7cf852cc36e03bcd34c
Author: Peter Holm <pho@FreeBSD.org>
AuthorDate: 2026-07-14 07:27:42 +0000
Commit: Peter Holm <pho@FreeBSD.org>
CommitDate: 2026-07-14 07:27:42 +0000
stress2: Added new test scenarios
---
tools/test/stress2/misc/nullfs33.sh | 129 ++++++++++++++++++++++
tools/test/stress2/misc/nullfs34.sh | 28 +++++
tools/test/stress2/misc/pddupfd.sh | 132 +++++++++++++++++++++++
tools/test/stress2/misc/rename17.sh | 57 ++++++++++
tools/test/stress2/misc/rename18.sh | 21 ++++
tools/test/stress2/misc/rename_exchange.sh | 162 ++++++++++++++++++++++++++++
tools/test/stress2/misc/rename_exchange2.sh | 84 +++++++++++++++
7 files changed, 613 insertions(+)
diff --git a/tools/test/stress2/misc/nullfs33.sh b/tools/test/stress2/misc/nullfs33.sh
new file mode 100755
index 000000000000..8b445405e213
--- /dev/null
+++ b/tools/test/stress2/misc/nullfs33.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+# Bug 275570 - self-referential nullfs mount over tmpfs in combination with MNT_UPDATE results in a hang
+
+# Test scenario mounts:
+# tmpfs on /mnt (tmpfs, local, noexec, read-only)
+# .setup-done on /mnt/.setup-done (nullfs, local)
+
+# Hang seen on a GENERIC kernel only
+# UID PID PPID C PRI NI VSZ RSS MWCHAN STAT TT TIME COMMAND
+# 0 4429 4417 4 59 0 14192 2832 vlp2 D+ 0 0:00.06 ./nullfs33
+# https://people.freebsd.org/~pho/stress/log/log0663.txt
+
+
+. ../default.cfg
+set -u
+prog=$(basename "$0" .sh)
+
+cat > /tmp/$prog.c <<EOF
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/_iovec.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+
+static void xmount(const char* fstype, const char* from, const char* to, int flags) {
+
+ char errmsg[255];
+ errmsg[0] = '\0';
+
+ int capacity = 8;
+
+ struct iovec* iov = malloc(sizeof(struct iovec) * capacity);
+
+ iov[0].iov_base = "fstype";
+ iov[0].iov_len = sizeof("fstype");
+ iov[1].iov_base = __DECONST(char*, fstype);
+ iov[1].iov_len = strlen(fstype) + 1;
+
+ iov[2].iov_base = "fspath";
+ iov[2].iov_len = sizeof("fspath");
+ iov[3].iov_base = __DECONST(char*, to);
+ iov[3].iov_len = strlen(to) + 1;
+
+ iov[4].iov_base = "from";
+ iov[4].iov_len = sizeof("from");
+ iov[5].iov_base = __DECONST(char*, from);
+ iov[5].iov_len = strlen(from) + 1;
+
+ iov[6].iov_base = "errmsg";
+ iov[6].iov_len = sizeof("errmsg");
+ iov[7].iov_base = errmsg;
+ iov[7].iov_len = sizeof(errmsg);
+
+ int len = 8;
+
+ if (flags & MNT_NOCOVER) {
+
+ assert(len + 2 <= capacity);
+
+ iov[len].iov_base = "nocover";
+ iov[len].iov_len = sizeof("nocover");
+ len++;
+
+ iov[len].iov_base = NULL;
+ iov[len].iov_len = 0;
+ len++;
+ }
+
+ assert(len == capacity);
+
+ if (nmount(iov, len, (int)flags) == -1) {
+ err(EXIT_FAILURE, "nmount %s -> %s: %s", from, to, errmsg);
+ }
+
+ free(iov);
+}
+
+static void xchdir(const char* path) {
+ if (chdir(path) == -1) {
+ err(EXIT_FAILURE, "chdir(\"%s\")", path);
+ }
+}
+
+static void xcreat(char* path, int mode) {
+ int fd = open(path, O_CREAT | O_WRONLY, mode);
+ if (fd == -1) {
+ err(EXIT_FAILURE, "can't create %s", path);
+ }
+ close(fd);
+}
+
+//#define ROOT_DIR "/mnt"
+#define ROOT_DIR "$mntpoint"
+
+int main(int argc __unused, char* argv[] __unused) {
+
+ xchdir(ROOT_DIR);
+
+ xmount("tmpfs", "tmpfs", ".", MNT_NOEXEC);
+
+ xcreat(".setup-done", 0444);
+ xmount("nullfs", ".setup-done", ".setup-done", 0);
+
+ xmount("tmpfs", "tmpfs", ".", MNT_RDONLY | MNT_NOEXEC | MNT_UPDATE);
+
+ unmount(ROOT_DIR "/.setup-done", MNT_FORCE);
+ unmount(ROOT_DIR, MNT_FORCE);
+
+ return 0;
+}
+EOF
+cc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c || exit 1
+
+cd /tmp
+start=`date +%s`
+while [ $((`date +%s` - start)) -lt 60 ]; do
+ ./$prog || break
+done
+cd -
+
+mount | grep -q "on $mntpoint " && umount $mntpoint
+rm -f /tmp/$prog /tmp/$prog.c
+exit 0
diff --git a/tools/test/stress2/misc/nullfs34.sh b/tools/test/stress2/misc/nullfs34.sh
new file mode 100755
index 000000000000..9e70049e23ec
--- /dev/null
+++ b/tools/test/stress2/misc/nullfs34.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2026 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# "nullfs mount over a file" scenario
+
+set -u
+prog=$(basename "$0" .sh)
+
+file=/tmp/$prog.file
+mp=/tmp/$prog.mp
+touch $file $mp
+
+s=0
+mount_nullfs $file $mp || exit 1
+echo a > $mp
+cmp -s $file $mp ||
+ { echo "Files differ"; ls -l $file $mp; s=1; }
+umount $mp
+cmp -s $file $mp &&
+ { echo "Files are equal"; ls -l $file $mp; s=2; }
+
+rm -f $file $mp
+exit $s
diff --git a/tools/test/stress2/misc/pddupfd.sh b/tools/test/stress2/misc/pddupfd.sh
new file mode 100755
index 000000000000..cc71cf883e41
--- /dev/null
+++ b/tools/test/stress2/misc/pddupfd.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+
+set -u
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+/* \$Id: pddupfd.c,v 1.6 2026/07/04 17:48:47 kostik Exp kostik $ */
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/procdesc.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <machine/atomic.h>
+#include <err.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define MY_SYS_pdopenpid 603
+#define MY_SYS_pddupfd 604
+
+static int
+my_pdopenpid(pid_t pid, int flags)
+{
+ return (syscall(MY_SYS_pdopenpid, pid, flags));
+}
+
+static int
+my_pddupfd(pid_t pid, int remote_fd, int flags)
+{
+ return (syscall(MY_SYS_pddupfd, pid, remote_fd, flags));
+}
+
+static void
+check(int fd, int child, int *comm)
+{
+ struct __wrusage wu;
+ struct __siginfo si;
+ int error, f, pp, rfd, status, workfd;
+ char cmd[128];
+
+ error = pdgetpid(fd, &pp);
+ if (error == -1)
+ err(1, "pgdetpid");
+ printf("child pid %d %d\n", child, pp);
+
+ while ((rfd = (int)atomic_load_int(comm)) == -1)
+ ;
+ if (rfd == -2)
+ errx(1, "open in child failed");
+ workfd = my_pddupfd(fd, rfd, 0);
+ if (workfd == -1)
+ err(1, "pddupfd");
+
+ f = fcntl(workfd, F_GETFD);
+ if (f == -1)
+ err(1, "F_GETFD %d", workfd);
+ f &= ~FD_CLOEXEC;
+ error = fcntl(workfd, F_SETFD, f);
+ if (error == -1)
+ err(1, "F_SETFD %d", workfd);
+
+ snprintf(cmd, sizeof(cmd), "cat 0<&%d", workfd);
+ system(cmd);
+ close(workfd);
+
+ atomic_store_int(comm, -1);
+
+ error = pdwait(fd, &status, WEXITED, &wu, &si);
+ if (error == -1)
+ err(1, "pdwait");
+ printf("exited pid %d status %#x si.si_status %#x si.si_code %#x\n",
+ pp, status, si.si_status, si.si_code);
+}
+
+int
+main(void)
+{
+ int *comm;
+ pid_t child;
+ int fd, workfd;
+
+ comm = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED |
+ MAP_ANONYMOUS, -1, 0);
+ if (comm == MAP_FAILED)
+ err(1, "mmap");
+ atomic_store_int(comm, -1);
+
+ child = fork();
+ if (child == -1)
+ err(1, "fork");
+ if (child == 0) {
+ workfd = open("/etc/passwd", O_RDONLY);
+ if (workfd == -1) {
+ atomic_store_int(comm, -2);
+ err(1, "open");
+ }
+ atomic_store_int(comm, workfd);
+ while ((int)atomic_load_int(comm) >= 0)
+ sleep(1);
+ _exit(0x123);
+ }
+
+ fd = my_pdopenpid(child, 0);
+ if (fd == -1)
+ err(1, "pdopenpid");
+
+ check(fd, child, comm);
+ close(fd);
+}
+EOF
+
+cc -o /tmp/$prog -Wall -Wextra -g -O0 /tmp/$prog.c || exit 1
+
+../testcases/swap/swap -t 2m -i 20 &
+sleep 2
+
+cd /tmp
+s=0
+start=`date +%s`
+while [ $((`date +%s`- start)) -lt 120 ]; do
+ ./$prog > /dev/null ; s=$?
+done
+[ $s -eq 124 ] && s=0
+while pkill swap; do :; done
+wait
+cd -
+
+rm /tmp/$prog /tmp/$prog.c
+exit $s
diff --git a/tools/test/stress2/misc/rename17.sh b/tools/test/stress2/misc/rename17.sh
new file mode 100755
index 000000000000..682e1908a9e0
--- /dev/null
+++ b/tools/test/stress2/misc/rename17.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2026 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# Test rename(2) over nfs.
+# This needs to be in /etc/exports: /mnt -maproot=root 127.0.0.1
+
+# "panic: Lock rename not exclusively locked @ ../../../kern/vfs_subr.c:5878" seen
+
+. ../default.cfg
+
+[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
+grep -q $mntpoint /etc/exports ||
+ { echo "$mntpoint missing from /etc/exports"; exit 0; }
+ps -x | grep -v grep | grep -q nfsd || { echo "nfsd is not running"; exit 0; }
+
+set -u
+mp1=$mntpoint
+mp2=${mntpoint}2
+
+mount | grep "$mp2" | grep nfs > /dev/null && umount -f $mp2
+mount | grep "$mp1" | grep -q /md && umount -f $mp1
+mdconfig -l | grep -q $mdstart && mdconfig -d -u $mdstart
+
+set -e
+mdconfig -a -t swap -s 2g -u $mdstart
+newfs $newfs_flags /dev/md$mdstart > /dev/null
+mount /dev/md$mdstart $mp1
+set +e
+
+mkdir $mp1/stressX $mp1/stressX.control
+chmod 777 $mp1/stressX $mp1/stressX.control
+
+[ ! -d $mp2 ] && mkdir $mp2
+chmod 777 $mp2
+
+mount -t nfs -o tcp -o retrycnt=3 -o soft -o rw \
+ 127.0.0.1:$mp1 $mp2
+
+sleep 1
+export CTRLDIR=$mp2/stressX.control
+export RUNDIR=$mp2/stressX
+export renameLOAD=100
+export runRUNTIME=5m
+export TESTPROGS="
+testcases/rename/rename
+testcases/swap/swap"
+su -m $testuser -c 'cd ..; ./testcases/run/run $TESTPROGS'
+
+umount -f $mp2 > /dev/null 2>&1
+umount -f $mp1 > /dev/null 2>&1
+mdconfig -d -u $mdstart
+exit 0
diff --git a/tools/test/stress2/misc/rename18.sh b/tools/test/stress2/misc/rename18.sh
new file mode 100755
index 000000000000..f71e29bb1bef
--- /dev/null
+++ b/tools/test/stress2/misc/rename18.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# Test scenario from:
+# Bug 295826 - kern_renameat() infinite loop
+
+set -u
+root=/root
+cd $root
+mkdir -p $root/test_loop/a
+mkdir -p $root/test_loop/b
+echo '123' > $root/test_loop/b/file
+touch $root/test_loop/a/file
+mount_nullfs -o rw -o nocache -o noatime -o noexec -o nosuid $root/test_loop/b/file $root/test_loop/a/file
+echo '123' > /tmp/file
+
+# The "mv" would spin
+mv /tmp/file $root/test_loop/a/file
+
+umount $root/test_loop/a/file
+rm -rf /tmp/file $root/test_loop
+exit
diff --git a/tools/test/stress2/misc/rename_exchange.sh b/tools/test/stress2/misc/rename_exchange.sh
new file mode 100755
index 000000000000..23f837af84d6
--- /dev/null
+++ b/tools/test/stress2/misc/rename_exchange.sh
@@ -0,0 +1,162 @@
+#!/bin/sh
+
+# Test of RENAME_EXCHANGE for tmpfs
+
+. ../default.cfg
+set -u
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#if ! defined(AT_RENAME_EXCHANGE)
+#define AT_RENAME_EXCHANGE 0x0002
+#endif
+
+static char *dir, *f1, *f2;
+
+void
+test1(void)
+{
+ struct stat s, s1, s2;
+ time_t start;
+ char file1[1024], file2[1024];
+
+ snprintf(file1, sizeof(file1), "%s/%s", dir, f1);
+ snprintf(file2, sizeof(file2), "%s/%s", dir, f2);
+ if (stat(file1, &s1) < 0)
+ err(1, "stat(%s)", file1);
+ if (stat(file2, &s2) < 0)
+ err(1, "stat(%s)", file2);
+ assert(s1.st_size != s2.st_size);
+ start = time(NULL);
+ while (time(NULL) - start < 30) {
+ if (renameat2(AT_FDCWD, file1, AT_FDCWD, file2, RENAME_EXCHANGE) == -1)
+ err(1, "%s: rename2(%s, %s)", __func__, file1, file2);
+
+ if (stat(file1, &s) < 0)
+ err(1, "statfs(%s)", file1);
+ if (s.st_size != s2.st_size)
+ errx(1, "Got size %jd, expected %jd", (uintmax_t)s.st_size, (uintmax_t)s2.st_size);
+
+ if (renameat2(AT_FDCWD, file2, AT_FDCWD, file1, RENAME_EXCHANGE) == -1)
+ err(1, "%s: rename2(%s, %s)", __func__, file2, file1);
+ }
+}
+
+void
+test2(void)
+{
+ time_t start;
+ char cwd[1024];
+
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ err(1, "getcwd()");
+ chdir(dir);
+ start = time(NULL);
+ while (time(NULL) - start < 30) {
+ if (renameat2(AT_FDCWD, f1, AT_FDCWD, f2, RENAME_EXCHANGE) == -1)
+ err(1, "%s: rename2(%s, %s)", __func__, f1, f2);
+
+ if (renameat2(AT_FDCWD, f2, AT_FDCWD, f1, RENAME_EXCHANGE) == -1)
+ err(1, "%s: rename2(%s, %s)", __func__, f2, f1);
+ }
+ chdir(cwd);
+}
+
+/* rename(<file>, <non existing file>) */
+void
+test3(void)
+{
+ int e;
+ char file1[1024], file2[1024];
+
+ snprintf(file1, sizeof(file1), "%s/%s", dir, f1);
+ snprintf(file2, sizeof(file2), "%s/NotThere", dir);
+ if (renameat2(AT_FDCWD, file1, AT_FDCWD, file2, RENAME_EXCHANGE) != -1)
+ err(1, "%s: rename2(%s, %s)", __func__, file1, file2);
+ e = errno;
+ if (e != ENOENT)
+ errx(1, "%s: Expected errno %i, got %d\n", __func__, ENOENT, e);
+
+ if (renameat2(AT_FDCWD, file2, AT_FDCWD, file1, RENAME_EXCHANGE) != -1)
+ err(1, "%s: rename2(%s, %s)", __func__, file2, file1);
+ e = errno;
+ if (e != ENOENT)
+ errx(1, "%s: Expected errno %i, got %d\n", __func__, ENOENT, e);
+}
+
+/* rename(<file>, <directory>) */
+void
+test4(void)
+{
+ int e;
+ char file1[1024], file2[1024];
+
+ snprintf(file1, sizeof(file1), "%s/%s", dir, f1);
+ snprintf(file2, sizeof(file2), "%s", dir);
+ if (renameat2(AT_FDCWD, file1, AT_FDCWD, file2, RENAME_EXCHANGE) != -1)
+ err(1, "%s: rename2(%s, %s)", __func__, file1, file2);
+ e = errno;
+ if (e != EINVAL)
+ errx(1, "%s: Expected errno %i, got %d\n", __func__, EINVAL, e);
+
+ if (renameat2(AT_FDCWD, file2, AT_FDCWD, file1, RENAME_EXCHANGE) != -1)
+ err(1, "%s: rename2(%s, %s)", __func__, file2, file1);
+ e = errno;
+ if (e != EINVAL)
+ errx(1, "%s: Expected errno %i, got %d\n", __func__, EINVAL, e);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: %s <dir> <file1> <file2>\n", argv[0]);
+ exit(1);
+ }
+ dir = argv[1];
+ f1 = argv[2];
+ f2 = argv[3];
+
+ test1();
+ test2();
+ test3();
+ test4();
+}
+EOF
+cc -o /tmp/$prog -Wall -Wextra -g -O0 /tmp/$prog.c || exit 1
+
+mount | grep -q "on $mntpoint " && umount $mntpoint
+size=2g
+mount -o size=${size}m -t tmpfs tmpfs $mntpoint
+
+../testcases/swap/swap -t 5m -i 20 > /dev/null &
+sleep 1
+
+for d in d1 d2 d3 d4 d5; do
+ mkdir $mntpoint/$d
+ echo 1 > $mntpoint/$d/f1
+ echo 22 > $mntpoint/$d/f2
+done
+cd $mntpoint
+for d in d1 d2 d3 d4 d5; do
+ /tmp/$prog $mntpoint/$d f1 f2 &
+done
+while pgrep -q $prog; do sleep .2; done
+cd -
+
+while pkill swap; do sleep .5; done
+wait
+
+umount $mntpoint
+exit 0
diff --git a/tools/test/stress2/misc/rename_exchange2.sh b/tools/test/stress2/misc/rename_exchange2.sh
new file mode 100755
index 000000000000..c8ecd7713cd4
--- /dev/null
+++ b/tools/test/stress2/misc/rename_exchange2.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+# Test of RENAME_EXCHANGE for tmpfs, cross directory version
+
+. ../default.cfg
+set -u
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#if ! defined(AT_RENAME_EXCHANGE)
+#define AT_RENAME_EXCHANGE 0x0002
+#endif
+
+static char *f1, *f2;
+
+void
+test1(void)
+{
+ struct stat s, s1, s2;
+ time_t start;
+
+ if (stat(f1, &s1) < 0)
+ err(1, "stat(%s)", f1);
+ if (stat(f2, &s2) < 0)
+ err(1, "stat(%s)", f2);
+ assert(s1.st_size != s2.st_size);
+ start = time(NULL);
+ while (time(NULL) - start < 30) {
+ if (renameat2(AT_FDCWD, f1, AT_FDCWD, f2, RENAME_EXCHANGE) == -1)
+ err(1, "%s: rename2(%s, %s)", __func__, f1, f2);
+
+ if (stat(f1, &s) < 0)
+ err(1, "statfs(%s)", f1);
+ if (s.st_size != s2.st_size)
+ errx(1, "Got size %jd, expected %jd", (uintmax_t)s.st_size, (uintmax_t)s2.st_size);
+
+ if (renameat2(AT_FDCWD, f2, AT_FDCWD, f1, RENAME_EXCHANGE) == -1)
+ err(1, "%s: rename2(%s, %s)", __func__, f2, f1);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s <file1> <file2>\n", argv[0]);
+ exit(1);
+ }
+ f1 = argv[1];
+ f2 = argv[2];
+
+ test1();
+}
+EOF
+cc -o /tmp/$prog -Wall -Wextra -g -O0 /tmp/$prog.c || exit 1
+
+mount | grep -q "on $mntpoint " && umount $mntpoint
+size=2g
+mount -o size=${size}m -t tmpfs tmpfs $mntpoint
+
+../testcases/swap/swap -t 5m -i 20 > /dev/null &
+sleep 1
+
+mkdir $mntpoint/d1 $mntpoint/d2
+echo 1 > $mntpoint/d1/f1
+echo 22 > $mntpoint/d2/f2
+/tmp/$prog $mntpoint/d1/f1 $mntpoint/d2/f2
+
+while pkill swap; do sleep .5; done
+wait
+
+umount $mntpoint
+exit 0