Re: git: 86db3c735dd9 - main - stress2: Added a regression test
Date: Fri, 17 Oct 2025 15:54:56 UTC
Peter,
Thank you for adding this test.
On Fri, Oct 17, 2025 at 3:05 AM Peter Holm <pho@freebsd.org> wrote:
>
> The branch main has been updated by pho:
>
> URL: https://cgit.FreeBSD.org/src/commit/?id=86db3c735dd999f75eab54d848fb8402e6689c80
>
> commit 86db3c735dd999f75eab54d848fb8402e6689c80
> Author: Peter Holm <pho@FreeBSD.org>
> AuthorDate: 2025-10-17 08:04:48 +0000
> Commit: Peter Holm <pho@FreeBSD.org>
> CommitDate: 2025-10-17 08:04:48 +0000
>
> stress2: Added a regression test
> ---
> tools/test/stress2/misc/unionfs20.sh | 206 +++++++++++++++++++++++++++++++++++
> 1 file changed, 206 insertions(+)
>
> diff --git a/tools/test/stress2/misc/unionfs20.sh b/tools/test/stress2/misc/unionfs20.sh
> new file mode 100755
> index 000000000000..0c87bc0d80aa
> --- /dev/null
> +++ b/tools/test/stress2/misc/unionfs20.sh
> @@ -0,0 +1,206 @@
> +#!/bin/sh
> +
> +#
> +# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause
> +#
> +
> +# Bug 289700 - unionfs: page fault in unionfs_find_node_status when closing a file within a socket's receive buffer
> +
> +# "Fatal trap 12: page fault while in kernel mode" seen:
> +# https://people.freebsd.org/~pho/stress/log/log0618.txt
> +
> +. ../default.cfg
> +
> +prog=$(basename "$0" .sh)
> +here=`pwd`
> +log=/tmp/$prog.log
> +md1=$mdstart
> +md2=$((md1 + 1))
> +mp1=/mnt$md1
> +mp2=/mnt$md2
> +
> +set -eu
> +mdconfig -l | grep -q md$md1 && mdconfig -d -u $md1
> +mdconfig -l | grep -q md$md2 && mdconfig -d -u $md2
> +
> +mdconfig -s 2g -u $md1
> +newfs $newfs_flags /dev/md$md1 > /dev/null
> +mdconfig -s 2g -u $md2
> +newfs $newfs_flags /dev/md$md2 > /dev/null
> +
> +mkdir -p $mp1 $mp2
> +mount /dev/md$md1 $mp1
> +mount /dev/md$md2 $mp2
> +mount -t unionfs -o noatime $mp1 $mp2
> +set +e
> +
> +cd /tmp
> +sed '1,/^EOF/d' < $here/$0 > $prog.c
> +mycc -o $prog -Wall -Wextra -O2 $prog.c
> +rm -f $prog.c
> +[ -d $RUNDIR ] || mkdir -p $RUNDIR
> +cd $RUNDIR
> +
> +n=3
> +for i in `jot $n`; do
> + mkdir $mp2/d$i
> +done
> +(cd $here/../testcases/swap; ./swap -t 3m -i 20 -l 100 -h > /dev/null) &
> +sleep 2
> +for i in `jot $n`; do
> + (cd $mp2/d$i; /tmp/$prog) &
> +done
> +while pgrep -q $prog; do sleep .5; done
> +while pkill swap; do :; done
> +wait
> +
> +cd $here
> +umount $mp2 # The unionfs mount
> +umount $mp2
> +umount $mp1
> +
> +mdconfig -d -u $md1
> +mdconfig -d -u $md2
> +rm -f /tmp/$prog
> +exit 0
> +EOF
> +#include <sys/param.h>
> +#include <sys/mman.h>
> +#include <sys/socket.h>
> +#include <sys/uio.h>
> +#include <sys/wait.h>
> +
> +#include <stdio.h>
> +#include <errno.h>
> +#include <err.h>
> +#include <stdlib.h>
> +#include <time.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <poll.h>
> +#include <stdatomic.h>
> +#include <string.h>
> +
> +#define PARALLEL 2
> +#define SYNC 0
> +
> +static int debug;
> +static _Atomic(int) *share;
> +
> +int
> +send_fd(int socket, int fd_to_send)
> +{
> + struct cmsghdr *cmsg;
> + struct msghdr msg = {0};
> + struct iovec iov;
> + char buf[1] = {0}; // dummy data
> + char cmsgbuf[CMSG_SPACE(sizeof(fd_to_send))];
> +
> + memset(cmsgbuf, 0, sizeof(cmsgbuf));
> +
> + iov.iov_base = buf;
> + iov.iov_len = sizeof(buf);
> + msg.msg_iov = &iov;
> + msg.msg_iovlen = 1;
> +
> + msg.msg_control = cmsgbuf;
> + msg.msg_controllen = sizeof(cmsgbuf);
> +
> + cmsg = CMSG_FIRSTHDR(&msg);
> + cmsg->cmsg_level = SOL_SOCKET;
> + cmsg->cmsg_type = SCM_RIGHTS;
> + cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
> +
> + memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(fd_to_send));
> +
> + return (sendmsg(socket, &msg, 0));
> +}
> +
> +int
> +recv_fd(int socket)
> +{
> + struct cmsghdr *cmsg;
> + struct msghdr msg = {0};
> + struct iovec iov;
> + char buf[1];
> + int received_fd;
> + char cmsgbuf[CMSG_SPACE(sizeof(received_fd))];
> +
> + memset(cmsgbuf, 0, sizeof(cmsgbuf));
> +
> + iov.iov_base = buf;
> + iov.iov_len = sizeof(buf);
> + msg.msg_iov = &iov;
> + msg.msg_iovlen = 1;
> +
> + msg.msg_control = cmsgbuf;
> + msg.msg_controllen = sizeof(cmsgbuf);
> +
> + if (recvmsg(socket, &msg, 0) < 0)
> + err(1, "recvmsg()");
> +
> + cmsg = CMSG_FIRSTHDR(&msg);
> + if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN(sizeof(received_fd))) {
> + fprintf(stderr, "No passed fd\n");
> + return (-1);
> + }
> +
> + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
> + fprintf(stderr, "Invalid cmsg_level or cmsg_type\n");
> + return (-1);
> + }
> +
> + memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(received_fd));
> + return (received_fd);
> +}
> +
> +int
> +main(void)
> +{
> + pid_t pid;
> + time_t start;
> + size_t len;
> + int fd, pair[2], status;
> +
> + fd = -1;
> + len = PAGE_SIZE;
> + if ((share = mmap(NULL, len, PROT_READ | PROT_WRITE,
> + MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED)
> + err(1, "mmap");
> +
> + if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, pair) == -1)
> + err(1, "socketpair");
> +
> + start = time(NULL);
> + while (time(NULL) - start < 180) {
> + share[SYNC] = 0;
> + if ((pid = fork()) == -1)
> + err(1, "fork");
> + if (pid == 0) {
> + close(pair[0]);
> + atomic_fetch_add(&share[SYNC], 1);
> + while (share[SYNC] != PARALLEL)
> + usleep(1000);
> + // Not calling recv_fd() triggers the issue
> +// fd = recv_fd(pair[1]);
> + if (debug)
> + fprintf(stderr, "Received fd=%d\n", fd);
> + _exit(0);
> + }
> + fd = open("foo", O_RDWR|O_CREAT|O_TRUNC, 0666);
> + if (fd == -1)
> + err(1, "open");
> + if (debug)
> + fprintf(stderr, "Sending fd=%d\n", fd);
> + atomic_fetch_add(&share[SYNC], 1);
> + while (share[SYNC] != PARALLEL)
> + usleep(1000);
> + send_fd(pair[0], fd);
> + usleep(arc4random() % 1000);
> + wait(&status);
> + close(fd);
> + unlink("foo");
> + }
> +}