xref: /freebsd/tools/test/stress2/misc/unionfs20.sh (revision df21a004be237a1dccd03c7b47254625eea62fa9)
1#!/bin/sh
2
3#
4# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
5#
6# SPDX-License-Identifier: BSD-2-Clause
7#
8
9# Bug 289700 - unionfs: page fault in unionfs_find_node_status when closing a file within a socket's receive buffer
10
11# "Fatal trap 12: page fault while in kernel mode" seen:
12# https://people.freebsd.org/~pho/stress/log/log0618.txt
13
14. ../default.cfg
15
16prog=$(basename "$0" .sh)
17here=`pwd`
18log=/tmp/$prog.log
19md1=$mdstart
20md2=$((md1 + 1))
21mp1=/mnt$md1
22mp2=/mnt$md2
23
24set -eu
25mdconfig -l | grep -q md$md1 && mdconfig -d -u $md1
26mdconfig -l | grep -q md$md2 && mdconfig -d -u $md2
27
28mdconfig -s 2g -u $md1
29newfs $newfs_flags /dev/md$md1 > /dev/null
30mdconfig -s 2g -u $md2
31newfs $newfs_flags /dev/md$md2 > /dev/null
32
33mkdir -p $mp1 $mp2
34mount /dev/md$md1 $mp1
35mount /dev/md$md2 $mp2
36mount -t unionfs -o noatime $mp1 $mp2
37set +e
38
39cd /tmp
40sed '1,/^EOF/d' < $here/$0 > $prog.c
41mycc -o $prog -Wall -Wextra -O2 $prog.c
42rm -f $prog.c
43[ -d $RUNDIR ] || mkdir -p $RUNDIR
44cd $RUNDIR
45
46n=3
47for i in `jot $n`; do
48	mkdir $mp2/d$i
49done
50(cd $here/../testcases/swap; ./swap -t 3m -i 20 -l 100 -h > /dev/null) &
51sleep 2
52for i in `jot $n`; do
53	(cd $mp2/d$i; /tmp/$prog) &
54done
55while pgrep -q $prog; do sleep .5; done
56while pkill swap; do :; done
57wait
58
59cd $here
60umount $mp2 # The unionfs mount
61umount $mp2
62umount $mp1
63
64mdconfig -d -u $md1
65mdconfig -d -u $md2
66rm -f /tmp/$prog
67exit 0
68EOF
69#include <sys/param.h>
70#include <sys/mman.h>
71#include <sys/socket.h>
72#include <sys/uio.h>
73#include <sys/wait.h>
74
75#include <stdio.h>
76#include <errno.h>
77#include <err.h>
78#include <stdlib.h>
79#include <time.h>
80#include <unistd.h>
81#include <fcntl.h>
82#include <poll.h>
83#include <stdatomic.h>
84#include <string.h>
85
86#define PARALLEL 2
87#define SYNC 0
88
89static int debug;
90static _Atomic(int) *share;
91
92int
93send_fd(int socket, int fd_to_send)
94{
95	struct cmsghdr *cmsg;
96	struct msghdr msg = {0};
97	struct iovec iov;
98	char buf[1] = {0};  // dummy data
99	char cmsgbuf[CMSG_SPACE(sizeof(fd_to_send))];
100
101	memset(cmsgbuf, 0, sizeof(cmsgbuf));
102
103	iov.iov_base = buf;
104	iov.iov_len = sizeof(buf);
105	msg.msg_iov = &iov;
106	msg.msg_iovlen = 1;
107
108	msg.msg_control = cmsgbuf;
109	msg.msg_controllen = sizeof(cmsgbuf);
110
111	cmsg = CMSG_FIRSTHDR(&msg);
112	cmsg->cmsg_level = SOL_SOCKET;
113	cmsg->cmsg_type = SCM_RIGHTS;
114	cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
115
116	memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(fd_to_send));
117
118	return (sendmsg(socket, &msg, 0));
119}
120
121int
122recv_fd(int socket)
123{
124	struct cmsghdr *cmsg;
125	struct msghdr msg = {0};
126	struct iovec iov;
127	char buf[1];
128	int received_fd;
129	char cmsgbuf[CMSG_SPACE(sizeof(received_fd))];
130
131	memset(cmsgbuf, 0, sizeof(cmsgbuf));
132
133	iov.iov_base = buf;
134	iov.iov_len = sizeof(buf);
135	msg.msg_iov = &iov;
136	msg.msg_iovlen = 1;
137
138	msg.msg_control = cmsgbuf;
139	msg.msg_controllen = sizeof(cmsgbuf);
140
141	if (recvmsg(socket, &msg, 0) < 0)
142		err(1, "recvmsg()");
143
144	cmsg = CMSG_FIRSTHDR(&msg);
145	if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN(sizeof(received_fd))) {
146		fprintf(stderr, "No passed fd\n");
147		return (-1);
148	}
149
150	if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
151		fprintf(stderr, "Invalid cmsg_level or cmsg_type\n");
152		return (-1);
153	}
154
155	memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(received_fd));
156	return (received_fd);
157}
158
159int
160main(void)
161{
162	pid_t pid;
163	time_t start;
164	size_t len;
165	int fd, pair[2], status;
166
167	fd  = -1;
168	len = PAGE_SIZE;
169	if ((share = mmap(NULL, len, PROT_READ | PROT_WRITE,
170	    MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED)
171		err(1, "mmap");
172
173	if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, pair) == -1)
174		err(1, "socketpair");
175
176	start = time(NULL);
177	while (time(NULL) - start < 180) {
178		share[SYNC] = 0;
179		if ((pid = fork()) == -1)
180			err(1, "fork");
181		if (pid == 0) {
182			close(pair[0]);
183			atomic_fetch_add(&share[SYNC], 1);
184			while (share[SYNC] != PARALLEL)
185				usleep(1000);
186			// Not calling recv_fd() triggers the issue
187//			fd = recv_fd(pair[1]);
188			if (debug)
189				fprintf(stderr, "Received fd=%d\n", fd);
190			_exit(0);
191		}
192		fd = open("foo", O_RDWR|O_CREAT|O_TRUNC, 0666);
193		if (fd == -1)
194			err(1, "open");
195		if (debug)
196			fprintf(stderr, "Sending fd=%d\n", fd);
197		atomic_fetch_add(&share[SYNC], 1);
198		while (share[SYNC] != PARALLEL)
199			usleep(1000);
200		send_fd(pair[0], fd);
201		usleep(arc4random() % 1000);
202		wait(&status);
203		close(fd);
204		unlink("foo");
205	}
206}
207