xref: /freebsd/tools/test/stress2/misc/sendfile21.sh (revision 02e9120893770924227138ba49df1edb3896112a)
1#!/bin/sh
2
3#
4# SPDX-License-Identifier: BSD-2-Clause
5#
6# Copyright (c) 2020 Peter Holm <pho@FreeBSD.org>
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27# SUCH DAMAGE.
28#
29
30# Konstantin Belousov <kib@FreeBSD.org> wrote:
31# 1. create a suffuciently large new file, e.g. of size 100 * PAGE_SIZE
32# 2. fill the file with some data, so no holes exist
33# 3. fsync it
34# 4. mmap the file
35# 5. for each odd-numbered page in the file, do msync(MS_INVALIDATE),
36#    but keep even-numbered pages intact
37# 6. unmap the file
38# 7. sendfile the file to a receiver (similar to existing tests in stress2)
39# 8. compare received data with the content of file
40#
41# Without the fixes from r359767 and r359818, the kernel should panic or
42# supply incorrect data to the sendfile peer.
43
44# "Fatal trap 9: general protection fault while in kernel mode
45# (unp_dispose+0x99)" seen.
46# https://people.freebsd.org/~pho/stress/log/sendfile21.txt
47# Not seen on r359843
48
49. ../default.cfg
50[ `id -u` -ne 0 ] && echo "Must be root!" && exit 1
51
52dir=/tmp
53odir=`pwd`
54cd $dir
55sed '1,/^EOF/d' < $odir/$0 > $dir/sendfile21.c
56mycc -o sendfile21 -Wall -Wextra -O0 -g sendfile21.c || exit 1
57rm -f sendfile21.c
58cd $odir
59
60set -e
61mount | grep "on $mntpoint " | grep -q /dev/md && umount -f $mntpoint
62[ -c /dev/md$mdstart ] &&  mdconfig -d -u $mdstart
63mdconfig -a -t swap -s 1g -u $mdstart
64newfs $newfs_flags -n md$mdstart > /dev/null
65mount /dev/md$mdstart $mntpoint
66set +e
67
68cd $mntpoint
69dd if=/dev/random of=input bs=4k count=100 status=none
70
71(cd $odir/../testcases/swap; ./swap -t 5m -i 20 -l 100 > /dev/null) &
72sleep 30
73n=1
74start=` date +%s`
75while [ $((` date +%s` - start)) -lt 180 ]; do
76	rm -f output
77#	The umount is needed to trigger the short read error
78	umount $mntpoint 2>/dev/null # busy umount
79	$dir/sendfile21
80	s=$?
81	cmp -s input output || break
82	[ `stat -f '%z' input` -ne ` stat -f '%z' output` ] && break
83	n=$((n + 1))
84done
85while pgrep -q swap; do
86	pkill swap
87done
88cmp -s input output || { echo "Loop #$n"; ls -l; s=1; }
89wait
90[ -f sendfile21.core -a $s -eq 0 ] &&
91    { ls -l sendfile21.core; mv sendfile21.core $dir; s=1; }
92cd $odir
93
94for i in `jot 6`; do
95	mount | grep -q "on $mntpoint " || break
96	umount $mntpoint && break || sleep 10
97	[ $i -eq 6 ] &&
98	    { echo FATAL; fstat -mf $mntpoint; exit 1; }
99done
100mdconfig -d -u $mdstart
101rm -rf $dir/sendfile21
102exit $s
103
104EOF
105#include <sys/param.h>
106#include <sys/fcntl.h>
107#include <sys/mman.h>
108#include <sys/socket.h>
109#include <sys/stat.h>
110#include <sys/uio.h>
111#include <sys/wait.h>
112
113#include <err.h>
114#include <errno.h>
115#include <fcntl.h>
116#include <stdio.h>
117#include <stdlib.h>
118#include <unistd.h>
119
120#define DEBUG 1
121
122static void
123test(void)
124{
125	struct stat st;
126	off_t i, j, k __unused, rd, written, pos;
127	pid_t pid;
128	int error, from, n, status, sv[2], to;
129	char buf[4086], *cp;
130	const char *from_name, *to_name;
131
132	from_name = "input";
133	to_name = "output";
134
135	if ((error = socketpair(AF_UNIX, SOCK_STREAM, 0, sv)) == -1)
136		err(1, "socketpair");
137
138	if ((from = open(from_name, O_RDONLY)) == -1)
139		err(1, "open read %s", from_name);
140
141	if ((error = fstat(from, &st)) == -1)
142		err(1, "stat %s", from_name);
143
144	pid = fork();
145	if (pid == -1)
146		err(1, "fork");
147	else if (pid != 0) {
148		setproctitle("parent");
149		close(sv[1]);
150
151		if ((cp = mmap(NULL, st.st_size, PROT_READ,
152		    MAP_PRIVATE, from, 0)) == MAP_FAILED)
153			err(1, "mmap");
154		if (fsync(from) == -1)
155			err(1, "fsync()");
156
157                for (i = 0; i < st.st_size; i += PAGE_SIZE)
158                        k = cp[i]; /* touch all pages */
159
160		for (i = 0, j = 0; i < st.st_size; i += PAGE_SIZE, j++) {
161			if (j % 2 == 1) {
162				if (msync(cp + i, PAGE_SIZE, MS_INVALIDATE)
163				    == -1)
164					err(1, "msync(), j = %d", (int)j);
165			}
166		}
167		if (munmap(cp, st.st_size) == -1)
168			err(1, "munmap()");
169
170		pos = 0;
171		for (;;) {
172			error = sendfile(from, sv[0], pos, st.st_size - pos,
173			    NULL, &written, 0);
174			if (error == -1)
175				err(1, "sendfile");
176			if (written != st.st_size)
177				fprintf(stderr, "sendfile sent %d bytes\n",
178				    (int)written);
179			pos += written;
180			if (pos == st.st_size)
181				break;
182		}
183		close(sv[0]);
184		if (pos != st.st_size)
185			fprintf(stderr, "%d written, expected %d\n",
186			   (int) pos, (int)st.st_size);
187		if (waitpid(pid, &status, 0) != pid)
188			err(1, "waitpid(%d)", pid);
189		_exit(WEXITSTATUS(status));
190	} else {
191		setproctitle("child");
192		close(from);
193		close(sv[0]);
194
195		if ((to = open(to_name, O_RDWR | O_CREAT, DEFFILEMODE)) ==
196		    -1)
197			err(1, "open write %s", to_name);
198
199		rd = 0;
200		for (;;) {
201			n = read(sv[1], buf, sizeof(buf));
202			if (n == -1)
203				err(1, "read");
204			else if (n == 0)
205				break;
206			rd += n;
207			if (write(to, buf, n) != n)
208				err(1, "write()");
209		}
210		close(to);
211		close(sv[1]);
212		if (rd != st.st_size)
213			fprintf(stderr, "Short read %d, expected %d\n",
214			    (int)rd, (int)st.st_size);
215		_exit(0);
216	}
217	_exit(0);
218}
219
220int
221main(void)
222{
223	pid_t pid;
224	int e, status;
225
226	e = 0;
227	if ((pid = fork()) == 0)
228		test();
229	if (pid == -1)
230		err(1, "fork()");
231	if (waitpid(pid, &status, 0) == -1)
232		err(1, "waitpid(%d)", pid);
233	if (status != 0) {
234		if (WIFSIGNALED(status))
235			fprintf(stderr,
236			    "pid %d exit signal %d\n",
237			    pid, WTERMSIG(status));
238	}
239	e += status == 0 ? 0 : 1;
240
241	return (e);
242}
243