xref: /freebsd/tools/test/stress2/misc/sendfile21.sh (revision dd41de95a84d979615a2ef11df6850622bf6184e)
1#!/bin/sh
2
3#
4# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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
64bsdlabel -w md$mdstart auto
65newfs $newfs_flags -n md$mdstart > /dev/null
66mount /dev/md$mdstart $mntpoint
67set +e
68
69cd $mntpoint
70dd if=/dev/random of=input bs=4k count=100 status=none
71
72(cd $odir/../testcases/swap; ./swap -t 5m -i 20 -l 100 > /dev/null) &
73sleep 30
74n=1
75start=` date +%s`
76while [ $((` date +%s` - start)) -lt 180 ]; do
77	rm -f output
78#	The umount is needed to trigger the short read error
79	umount $mntpoint 2>/dev/null # busy umount
80	$dir/sendfile21
81	s=$?
82	cmp -s input output || break
83	[ `stat -f '%z' input` -ne ` stat -f '%z' output` ] && break
84	n=$((n + 1))
85done
86while pgrep -q swap; do
87	pkill swap
88done
89cmp -s input output || { echo "Loop #$n"; ls -l; s=1; }
90wait
91[ -f sendfile21.core -a $s -eq 0 ] &&
92    { ls -l sendfile21.core; mv sendfile21.core $dir; s=1; }
93cd $odir
94
95for i in `jot 6`; do
96	mount | grep -q "on $mntpoint " || break
97	umount $mntpoint && break || sleep 10
98	[ $i -eq 6 ] &&
99	    { echo FATAL; fstat -mf $mntpoint; exit 1; }
100done
101mdconfig -d -u $mdstart
102rm -rf $dir/sendfile21
103exit $s
104
105EOF
106#include <sys/param.h>
107#include <sys/fcntl.h>
108#include <sys/mman.h>
109#include <sys/socket.h>
110#include <sys/stat.h>
111#include <sys/uio.h>
112#include <sys/wait.h>
113
114#include <err.h>
115#include <errno.h>
116#include <fcntl.h>
117#include <stdio.h>
118#include <stdlib.h>
119#include <unistd.h>
120
121#define DEBUG 1
122
123static void
124test(void)
125{
126	struct stat st;
127	off_t i, j, k, rd, written, pos;
128	pid_t pid;
129	int error, from, n, status, sv[2], to;
130	char buf[4086], *cp;
131	const char *from_name, *to_name;
132
133	from_name = "input";
134	to_name = "output";
135
136	if ((error = socketpair(AF_UNIX, SOCK_STREAM, 0, sv)) == -1)
137		err(1, "socketpair");
138
139	if ((from = open(from_name, O_RDONLY)) == -1)
140		err(1, "open read %s", from_name);
141
142	if ((error = fstat(from, &st)) == -1)
143		err(1, "stat %s", from_name);
144
145	pid = fork();
146	if (pid == -1)
147		err(1, "fork");
148	else if (pid != 0) {
149		setproctitle("parent");
150		close(sv[1]);
151
152		if ((cp = mmap(NULL, st.st_size, PROT_READ,
153		    MAP_PRIVATE, from, 0)) == MAP_FAILED)
154			err(1, "mmap");
155		if (fsync(from) == -1)
156			err(1, "fsync()");
157
158                for (i = 0; i < st.st_size; i += PAGE_SIZE)
159                        k = cp[i]; /* touch all pages */
160
161		for (i = 0, j = 0; i < st.st_size; i += PAGE_SIZE, j++) {
162			if (j % 2 == 1) {
163				if (msync(cp + i, PAGE_SIZE, MS_INVALIDATE)
164				    == -1)
165					err(1, "msync(), j = %d", (int)j);
166			}
167		}
168		if (munmap(cp, st.st_size) == -1)
169			err(1, "munmap()");
170
171		pos = 0;
172		for (;;) {
173			error = sendfile(from, sv[0], pos, st.st_size - pos,
174			    NULL, &written, 0);
175			if (error == -1)
176				err(1, "sendfile");
177			if (written != st.st_size)
178				fprintf(stderr, "sendfile sent %d bytes\n",
179				    (int)written);
180			pos += written;
181			if (pos == st.st_size)
182				break;
183		}
184		close(sv[0]);
185		if (pos != st.st_size)
186			fprintf(stderr, "%d written, expected %d\n",
187			   (int) pos, (int)st.st_size);
188		if (waitpid(pid, &status, 0) != pid)
189			err(1, "waitpid(%d)", pid);
190		_exit(WEXITSTATUS(status));
191	} else {
192		setproctitle("child");
193		close(from);
194		close(sv[0]);
195
196		if ((to = open(to_name, O_RDWR | O_CREAT, DEFFILEMODE)) ==
197		    -1)
198			err(1, "open write %s", to_name);
199
200		rd = 0;
201		for (;;) {
202			n = read(sv[1], buf, sizeof(buf));
203			if (n == -1)
204				err(1, "read");
205			else if (n == 0)
206				break;
207			rd += n;
208			if (write(to, buf, n) != n)
209				err(1, "write()");
210		}
211		close(to);
212		close(sv[1]);
213		if (rd != st.st_size)
214			fprintf(stderr, "Short read %d, expected %d\n",
215			    (int)rd, (int)st.st_size);
216		_exit(0);
217	}
218	_exit(0);
219}
220
221int
222main(void)
223{
224	pid_t pid;
225	int e, status;
226
227	e = 0;
228	if ((pid = fork()) == 0)
229		test();
230	if (pid == -1)
231		err(1, "fork()");
232	if (waitpid(pid, &status, 0) == -1)
233		err(1, "waitpid(%d)", pid);
234	if (status != 0) {
235		if (WIFSIGNALED(status))
236			fprintf(stderr,
237			    "pid %d exit signal %d\n",
238			    pid, WTERMSIG(status));
239	}
240	e += status == 0 ? 0 : 1;
241
242	return (e);
243}
244