1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *   Copyright (c) Red Hat Inc., 2007
5 *
6 *   This program is free software;  you can redistribute it and/or modify
7 *   it under the terms of the GNU General Public License as published by
8 *   the Free Software Foundation; either version 2 of the License, or
9 *   (at your option) any later version.
10 *
11 *   This program is distributed in the hope that it will be useful,
12 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
14 *   the GNU General Public License for more details.
15 *
16 *   You should have received a copy of the GNU General Public License
17 *   along with this program;  if not, write to the Free Software
18 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/*
22 * NAME
23 *	sendfile04.c
24 *
25 * DESCRIPTION
26 *	Testcase to test that sendfile(2) system call returns EFAULT
27 *	when passing wrong buffer.
28 *
29 * ALGORITHM
30 *     Given wrong address or protected buffer as OFFSET argument to sendfile.
31 *     A wrong address is created by munmap a buffer allocated by mmap.
32 *     A protected buffer is created by mmap with specifying protection.
33 *
34 * USAGE:  <for command-line>
35 *  sendfile04 [-c n] [-f] [-i n] [-I x] [-P x] [-t]
36 *     where,
37 *             -f   : Turn off functionality Testing.
38 *             -i n : Execute test n times.
39 *             -I x : Execute test for x seconds.
40 *             -P x : Pause for x seconds between iterations.
41 *             -t   : Turn on syscall timing.
42 *
43 * HISTORY
44 *	11/2007 Copyed from sendfile02.c by Masatake YAMATO
45 *
46 * RESTRICTIONS
47 *	NONE
48 */
49#include <stdio.h>
50#include <errno.h>
51#include <fcntl.h>
52#include <sys/stat.h>
53#include <sys/sendfile.h>
54#include <sys/types.h>
55#include <sys/socket.h>
56#include <sys/mman.h>
57#include <netinet/in.h>
58#include <arpa/inet.h>
59#include "test.h"
60
61#ifndef OFF_T
62#define OFF_T off_t
63#endif /* Not def: OFF_T */
64
65TCID_DEFINE(sendfile04);
66
67char in_file[100];
68char out_file[100];
69int out_fd;
70pid_t child_pid;
71static int sockfd, s;
72static struct sockaddr_in sin1;	/* shared between do_child and create_server */
73
74void cleanup(void);
75void do_child(void);
76void setup(void);
77int create_server(void);
78
79#define PASS_MAPPED_BUFFER 0
80#define PASS_UNMAPPED_BUFFER 1
81
82struct test_case_t {
83	int protection;
84	int pass_unmapped_buffer;
85} testcases[] = {
86	{
87	PROT_NONE, PASS_MAPPED_BUFFER}, {
88	PROT_READ, PASS_MAPPED_BUFFER}, {
89	PROT_EXEC, PASS_MAPPED_BUFFER}, {
90	PROT_EXEC | PROT_READ, PASS_MAPPED_BUFFER}, {
91PROT_READ | PROT_WRITE, PASS_UNMAPPED_BUFFER},};
92
93int TST_TOTAL = sizeof(testcases) / sizeof(testcases[0]);
94
95#ifdef UCLINUX
96static char *argv0;
97#endif
98
99void do_sendfile(int prot, int pass_unmapped_buffer)
100{
101	OFF_T *protected_buffer;
102	int in_fd;
103	struct stat sb;
104
105	protected_buffer = mmap(NULL,
106				sizeof(*protected_buffer),
107				prot, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
108	if (protected_buffer == MAP_FAILED) {
109		tst_brkm(TBROK, cleanup, "mmap failed: %d", errno);
110	}
111
112	out_fd = create_server();
113
114	if ((in_fd = open(in_file, O_RDONLY)) < 0) {
115		tst_brkm(TBROK, cleanup, "open failed: %d", errno);
116	}
117	if (stat(in_file, &sb) < 0) {
118		tst_brkm(TBROK, cleanup, "stat failed: %d", errno);
119	}
120
121	if (pass_unmapped_buffer) {
122		if (munmap(protected_buffer, sizeof(*protected_buffer)) < 0) {
123			tst_brkm(TBROK, cleanup, "munmap failed: %d", errno);
124		}
125	}
126
127	TEST(sendfile(out_fd, in_fd, protected_buffer, sb.st_size));
128
129	if (TEST_RETURN != -1) {
130		tst_resm(TFAIL, "call succeeded unexpectedly");
131	} else {
132		if (TEST_ERRNO != EFAULT) {
133			tst_resm(TFAIL, "sendfile returned unexpected "
134				 "errno, expected: %d, got: %d",
135				 EFAULT, TEST_ERRNO);
136		} else {
137			tst_resm(TPASS, "sendfile() returned %d : %s",
138				 TEST_ERRNO, strerror(TEST_ERRNO));
139		}
140	}
141
142	shutdown(sockfd, SHUT_RDWR);
143	shutdown(s, SHUT_RDWR);
144	kill(child_pid, SIGKILL);
145	close(in_fd);
146
147	if (!pass_unmapped_buffer) {
148		/* Not unmapped yet. So do it now. */
149		munmap(protected_buffer, sizeof(*protected_buffer));
150	}
151}
152
153/*
154 * do_child
155 */
156void do_child(void)
157{
158	int lc;
159	socklen_t length;
160	char rbuf[4096];
161
162	for (lc = 0; TEST_LOOPING(lc); lc++) {
163		length = sizeof(sin1);
164		recvfrom(sockfd, rbuf, 4096, 0, (struct sockaddr *)&sin1,
165			 &length);
166	}
167	exit(0);
168}
169
170/*
171 * setup() - performs all ONE TIME setup for this test.
172 */
173void setup(void)
174{
175	int fd;
176	char buf[100];
177
178	tst_sig(FORK, DEF_HANDLER, cleanup);
179
180	TEST_PAUSE;
181
182	/* make a temporary directory and cd to it */
183	tst_tmpdir();
184	sprintf(in_file, "in.%d", getpid());
185	if ((fd = creat(in_file, 00700)) < 0) {
186		tst_brkm(TBROK, cleanup, "creat failed in setup, errno: %d",
187			 errno);
188	}
189	sprintf(buf, "abcdefghijklmnopqrstuvwxyz");
190	if (write(fd, buf, strlen(buf)) < 0) {
191		tst_brkm(TBROK, cleanup, "write failed, errno: %d", errno);
192	}
193	close(fd);
194	sprintf(out_file, "out.%d", getpid());
195}
196
197/*
198 * cleanup() - performs all ONE TIME cleanup for this test at
199 *	       completion or premature exit.
200 */
201void cleanup(void)
202{
203
204	close(out_fd);
205	/* delete the test directory created in setup() */
206	tst_rmdir();
207
208}
209
210int create_server(void)
211{
212	static int count = 0;
213	socklen_t slen = sizeof(sin1);
214
215	sockfd = socket(PF_INET, SOCK_DGRAM, 0);
216	if (sockfd < 0) {
217		tst_brkm(TBROK, cleanup, "call to socket() failed: %s",
218			 strerror(errno));
219		return -1;
220	}
221	sin1.sin_family = AF_INET;
222	sin1.sin_port = 0; /* pick random free port */
223	sin1.sin_addr.s_addr = INADDR_ANY;
224	count++;
225	if (bind(sockfd, (struct sockaddr *)&sin1, sizeof(sin1)) < 0) {
226		tst_brkm(TBROK, cleanup, "call to bind() failed: %s",
227			 strerror(errno));
228		return -1;
229	}
230	if (getsockname(sockfd, (struct sockaddr *)&sin1, &slen) == -1)
231		tst_brkm(TBROK | TERRNO, cleanup, "getsockname failed");
232
233	child_pid = FORK_OR_VFORK();
234	if (child_pid < 0) {
235		tst_brkm(TBROK, cleanup, "client/server fork failed: %s",
236			 strerror(errno));
237		return -1;
238	}
239	if (!child_pid) {	/* child */
240#ifdef UCLINUX
241		if (self_exec(argv0, "") < 0) {
242			tst_brkm(TBROK, cleanup, "self_exec failed");
243			return -1;
244
245		}
246#else
247		do_child();
248#endif
249	}
250
251	s = socket(PF_INET, SOCK_DGRAM, 0);
252	inet_aton("127.0.0.1", &sin1.sin_addr);
253	if (s < 0) {
254		tst_brkm(TBROK, cleanup, "call to socket() failed: %s",
255			 strerror(errno));
256		return -1;
257	}
258	if (connect(s, (struct sockaddr *)&sin1, sizeof(sin1)) < 0) {
259		tst_brkm(TBROK, cleanup, "call to connect() failed: %s",
260			 strerror(errno));
261	}
262	return s;
263
264}
265
266int main(int ac, char **av)
267{
268	int i;
269	int lc;
270
271	tst_parse_opts(ac, av, NULL, NULL);
272#ifdef UCLINUX
273	argv0 = av[0];
274	maybe_run_child(&do_child, "");
275#endif
276
277	setup();
278
279	/*
280	 * The following loop checks looping state if -c option given
281	 */
282	for (lc = 0; TEST_LOOPING(lc); lc++) {
283		tst_count = 0;
284
285		for (i = 0; i < TST_TOTAL; ++i) {
286			do_sendfile(testcases[i].protection,
287				    testcases[i].pass_unmapped_buffer);
288		}
289	}
290	cleanup();
291
292	tst_exit();
293}
294