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 #include "safe_macros.h"
61
62 #ifndef OFF_T
63 #define OFF_T off_t
64 #endif /* Not def: OFF_T */
65
66 TCID_DEFINE(sendfile04);
67
68 char in_file[100];
69 char out_file[100];
70 int out_fd;
71 pid_t child_pid;
72 static int sockfd, s;
73 static struct sockaddr_in sin1; /* shared between do_child and create_server */
74
75 void cleanup(void);
76 void do_child(void);
77 void setup(void);
78 int create_server(void);
79
80 #define PASS_MAPPED_BUFFER 0
81 #define PASS_UNMAPPED_BUFFER 1
82
83 struct test_case_t {
84 int protection;
85 int pass_unmapped_buffer;
86 } testcases[] = {
87 {
88 PROT_NONE, PASS_MAPPED_BUFFER}, {
89 PROT_READ, PASS_MAPPED_BUFFER}, {
90 PROT_EXEC, PASS_MAPPED_BUFFER}, {
91 PROT_EXEC | PROT_READ, PASS_MAPPED_BUFFER}, {
92 PROT_READ | PROT_WRITE, PASS_UNMAPPED_BUFFER},};
93
94 int TST_TOTAL = sizeof(testcases) / sizeof(testcases[0]);
95
96 #ifdef UCLINUX
97 static char *argv0;
98 #endif
99
do_sendfile(int prot,int pass_unmapped_buffer)100 void do_sendfile(int prot, int pass_unmapped_buffer)
101 {
102 OFF_T *protected_buffer;
103 int in_fd;
104 struct stat sb;
105
106 protected_buffer = mmap(NULL,
107 sizeof(*protected_buffer),
108 prot, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
109 if (protected_buffer == MAP_FAILED) {
110 tst_brkm(TBROK, cleanup, "mmap failed: %d", errno);
111 }
112
113 out_fd = create_server();
114
115 if ((in_fd = open(in_file, O_RDONLY)) < 0) {
116 tst_brkm(TBROK, cleanup, "open failed: %d", errno);
117 }
118 SAFE_STAT(cleanup, in_file, &sb);
119
120 if (pass_unmapped_buffer) {
121 SAFE_MUNMAP(cleanup, protected_buffer,
122 sizeof(*protected_buffer));
123 }
124
125 TEST(sendfile(out_fd, in_fd, protected_buffer, sb.st_size));
126
127 if (TEST_RETURN != -1) {
128 tst_resm(TFAIL, "call succeeded unexpectedly");
129 } else {
130 if (TEST_ERRNO != EFAULT) {
131 tst_resm(TFAIL, "sendfile returned unexpected "
132 "errno, expected: %d, got: %d",
133 EFAULT, TEST_ERRNO);
134 } else {
135 tst_resm(TPASS, "sendfile() returned %d : %s",
136 TEST_ERRNO, strerror(TEST_ERRNO));
137 }
138 }
139
140 shutdown(sockfd, SHUT_RDWR);
141 shutdown(s, SHUT_RDWR);
142 kill(child_pid, SIGKILL);
143 close(in_fd);
144
145 if (!pass_unmapped_buffer) {
146 /* Not unmapped yet. So do it now. */
147 munmap(protected_buffer, sizeof(*protected_buffer));
148 }
149 }
150
151 /*
152 * do_child
153 */
do_child(void)154 void do_child(void)
155 {
156 int lc;
157 socklen_t length;
158 char rbuf[4096];
159
160 for (lc = 0; TEST_LOOPING(lc); lc++) {
161 length = sizeof(sin1);
162 recvfrom(sockfd, rbuf, 4096, 0, (struct sockaddr *)&sin1,
163 &length);
164 }
165 exit(0);
166 }
167
168 /*
169 * setup() - performs all ONE TIME setup for this test.
170 */
setup(void)171 void setup(void)
172 {
173 int fd;
174 char buf[100];
175
176 tst_sig(FORK, DEF_HANDLER, cleanup);
177
178 TEST_PAUSE;
179
180 /* make a temporary directory and cd to it */
181 tst_tmpdir();
182 sprintf(in_file, "in.%d", getpid());
183 if ((fd = creat(in_file, 00700)) < 0) {
184 tst_brkm(TBROK, cleanup, "creat failed in setup, errno: %d",
185 errno);
186 }
187 sprintf(buf, "abcdefghijklmnopqrstuvwxyz");
188 if (write(fd, buf, strlen(buf)) < 0) {
189 tst_brkm(TBROK, cleanup, "write failed, errno: %d", errno);
190 }
191 close(fd);
192 sprintf(out_file, "out.%d", getpid());
193 }
194
195 /*
196 * cleanup() - performs all ONE TIME cleanup for this test at
197 * completion or premature exit.
198 */
cleanup(void)199 void cleanup(void)
200 {
201
202 close(out_fd);
203 /* delete the test directory created in setup() */
204 tst_rmdir();
205
206 }
207
create_server(void)208 int create_server(void)
209 {
210 static int count = 0;
211 socklen_t slen = sizeof(sin1);
212
213 sockfd = socket(PF_INET, SOCK_DGRAM, 0);
214 if (sockfd < 0) {
215 tst_brkm(TBROK, cleanup, "call to socket() failed: %s",
216 strerror(errno));
217 return -1;
218 }
219 sin1.sin_family = AF_INET;
220 sin1.sin_port = 0; /* pick random free port */
221 sin1.sin_addr.s_addr = INADDR_ANY;
222 count++;
223 if (bind(sockfd, (struct sockaddr *)&sin1, sizeof(sin1)) < 0) {
224 tst_brkm(TBROK, cleanup, "call to bind() failed: %s",
225 strerror(errno));
226 return -1;
227 }
228 SAFE_GETSOCKNAME(cleanup, sockfd, (struct sockaddr *)&sin1, &slen);
229
230 child_pid = FORK_OR_VFORK();
231 if (child_pid < 0) {
232 tst_brkm(TBROK, cleanup, "client/server fork failed: %s",
233 strerror(errno));
234 return -1;
235 }
236 if (!child_pid) { /* child */
237 #ifdef UCLINUX
238 if (self_exec(argv0, "") < 0) {
239 tst_brkm(TBROK, cleanup, "self_exec failed");
240 return -1;
241
242 }
243 #else
244 do_child();
245 #endif
246 }
247
248 s = socket(PF_INET, SOCK_DGRAM, 0);
249 inet_aton("127.0.0.1", &sin1.sin_addr);
250 if (s < 0) {
251 tst_brkm(TBROK, cleanup, "call to socket() failed: %s",
252 strerror(errno));
253 return -1;
254 }
255 SAFE_CONNECT(cleanup, s, (struct sockaddr *)&sin1, sizeof(sin1));
256 return s;
257
258 }
259
main(int ac,char ** av)260 int main(int ac, char **av)
261 {
262 int i;
263 int lc;
264
265 tst_parse_opts(ac, av, NULL, NULL);
266 #ifdef UCLINUX
267 argv0 = av[0];
268 maybe_run_child(&do_child, "");
269 #endif
270
271 setup();
272
273 /*
274 * The following loop checks looping state if -c option given
275 */
276 for (lc = 0; TEST_LOOPING(lc); lc++) {
277 tst_count = 0;
278
279 for (i = 0; i < TST_TOTAL; ++i) {
280 do_sendfile(testcases[i].protection,
281 testcases[i].pass_unmapped_buffer);
282 }
283 }
284 cleanup();
285
286 tst_exit();
287 }
288