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
65 TCID_DEFINE(sendfile04);
66
67 char in_file[100];
68 char out_file[100];
69 int out_fd;
70 pid_t child_pid;
71 static int sockfd, s;
72 static struct sockaddr_in sin1; /* shared between do_child and create_server */
73
74 void cleanup(void);
75 void do_child(void);
76 void setup(void);
77 int create_server(void);
78
79 #define PASS_MAPPED_BUFFER 0
80 #define PASS_UNMAPPED_BUFFER 1
81
82 struct 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}, {
91 PROT_READ | PROT_WRITE, PASS_UNMAPPED_BUFFER},};
92
93 int TST_TOTAL = sizeof(testcases) / sizeof(testcases[0]);
94
95 #ifdef UCLINUX
96 static char *argv0;
97 #endif
98
do_sendfile(int prot,int pass_unmapped_buffer)99 void 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 */
do_child(void)156 void 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 */
setup(void)173 void 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 */
cleanup(void)201 void cleanup(void)
202 {
203
204 close(out_fd);
205 /* delete the test directory created in setup() */
206 tst_rmdir();
207
208 }
209
create_server(void)210 int 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
main(int ac,char ** av)266 int 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