t_setrlimit.c revision 1.1 1 /* $NetBSD: t_setrlimit.c,v 1.1 2011/07/07 06:57:54 jruoho Exp $ */
2
3 /*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jukka Ruohonen.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: t_setrlimit.c,v 1.1 2011/07/07 06:57:54 jruoho Exp $");
33
34 #include <sys/resource.h>
35 #include <sys/mman.h>
36 #include <sys/wait.h>
37
38 #include <atf-c.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <limits.h>
42 #include <signal.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 static void sighandler(int);
48 static const char path[] = "setrlimit";
49
50 static const int rlimit[] = {
51 RLIMIT_AS,
52 RLIMIT_CORE,
53 RLIMIT_CPU,
54 RLIMIT_DATA,
55 RLIMIT_FSIZE,
56 RLIMIT_MEMLOCK,
57 RLIMIT_NOFILE,
58 RLIMIT_NPROC,
59 RLIMIT_RSS,
60 RLIMIT_SBSIZE,
61 RLIMIT_STACK
62 };
63
64 ATF_TC(setrlimit_basic);
65 ATF_TC_HEAD(setrlimit_basic, tc)
66 {
67 atf_tc_set_md_var(tc, "descr", "A basic soft limit test");
68 }
69
70 ATF_TC_BODY(setrlimit_basic, tc)
71 {
72 struct rlimit res;
73 int *buf, lim;
74 size_t i;
75
76 buf = calloc(__arraycount(rlimit), sizeof(int));
77
78 if (buf == NULL)
79 atf_tc_fail("initialization failed");
80
81 for (i = lim = 0; i < __arraycount(rlimit); i++) {
82
83 (void)memset(&res, 0, sizeof(struct rlimit));
84
85 if (getrlimit(rlimit[i], &res) != 0)
86 continue;
87
88 if (res.rlim_cur == RLIM_INFINITY || res.rlim_cur == 0)
89 continue;
90
91 if (res.rlim_cur == res.rlim_max) /* An unprivileged run. */
92 continue;
93
94 buf[i] = res.rlim_cur;
95 res.rlim_cur = res.rlim_cur - 1;
96
97 if (setrlimit(rlimit[i], &res) != 0) {
98 lim = rlimit[i];
99 goto out;
100 }
101 }
102
103 out:
104 for (i = 0; i < __arraycount(rlimit); i++) {
105
106 (void)memset(&res, 0, sizeof(struct rlimit));
107
108 if (buf[i] == 0)
109 continue;
110
111 if (getrlimit(rlimit[i], &res) != 0)
112 continue;
113
114 res.rlim_cur = buf[i];
115
116 (void)setrlimit(rlimit[i], &res);
117 }
118
119 if (lim != 0)
120 atf_tc_fail("failed to set limit (%d)", lim);
121 }
122
123 ATF_TC(setrlimit_current);
124 ATF_TC_HEAD(setrlimit_current, tc)
125 {
126 atf_tc_set_md_var(tc, "descr", "setrlimit(3) with current limits");
127 }
128
129 ATF_TC_BODY(setrlimit_current, tc)
130 {
131 struct rlimit res;
132 size_t i;
133
134 for (i = 0; i < __arraycount(rlimit); i++) {
135
136 (void)memset(&res, 0, sizeof(struct rlimit));
137
138 ATF_REQUIRE(getrlimit(rlimit[i], &res) == 0);
139 ATF_REQUIRE(setrlimit(rlimit[i], &res) == 0);
140 }
141 }
142
143 ATF_TC(setrlimit_err);
144 ATF_TC_HEAD(setrlimit_err, tc)
145 {
146 atf_tc_set_md_var(tc, "descr", "Test error conditions");
147 }
148
149 ATF_TC_BODY(setrlimit_err, tc)
150 {
151 struct rlimit res;
152 size_t i;
153
154 for (i = 0; i < __arraycount(rlimit); i++) {
155
156 errno = 0;
157
158 ATF_REQUIRE(getrlimit(rlimit[i], (void *)0) != 0);
159 ATF_REQUIRE(errno == EFAULT);
160 }
161
162 errno = 0;
163
164 ATF_REQUIRE(getrlimit(INT_MAX, &res) != 0);
165 ATF_REQUIRE(errno == EINVAL);
166 }
167
168 ATF_TC_WITH_CLEANUP(setrlimit_fsize);
169 ATF_TC_HEAD(setrlimit_fsize, tc)
170 {
171 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_FSIZE");
172 }
173
174 ATF_TC_BODY(setrlimit_fsize, tc)
175 {
176 struct rlimit res;
177 int fd, sta;
178 pid_t pid;
179
180 fd = open(path, O_RDWR | O_CREAT, 0700);
181
182 if (fd < 0)
183 atf_tc_fail("initialization failed");
184
185 pid = fork();
186 ATF_REQUIRE(pid >= 0);
187
188 if (pid == 0) {
189
190 res.rlim_cur = 2;
191 res.rlim_max = 2;
192
193 if (setrlimit(RLIMIT_FSIZE, &res) != 0)
194 _exit(EXIT_FAILURE);
195
196 if (signal(SIGXFSZ, sighandler) == SIG_ERR)
197 _exit(EXIT_FAILURE);
198
199 /*
200 * The third call should generate a SIGXFSZ.
201 */
202 (void)write(fd, "X", 1);
203 (void)write(fd, "X", 1);
204 (void)write(fd, "X", 1);
205
206 _exit(EXIT_FAILURE);
207 }
208
209 (void)close(fd);
210 (void)wait(&sta);
211 (void)unlink(path);
212
213 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
214 atf_tc_fail("RLIMIT_FSIZE not enforced");
215 }
216
217 ATF_TC_CLEANUP(setrlimit_fsize, tc)
218 {
219 (void)unlink(path);
220 }
221
222 static void
223 sighandler(int signo)
224 {
225
226 if (signo != SIGXFSZ)
227 _exit(EXIT_FAILURE);
228
229 _exit(EXIT_SUCCESS);
230 }
231
232 ATF_TC(setrlimit_memlock);
233 ATF_TC_HEAD(setrlimit_memlock, tc)
234 {
235 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_MEMLOCK");
236 }
237
238 ATF_TC_BODY(setrlimit_memlock, tc)
239 {
240 struct rlimit res;
241 void *buf;
242 long page;
243 pid_t pid;
244 int sta;
245
246 page = sysconf(_SC_PAGESIZE);
247 ATF_REQUIRE(page >= 0);
248
249 buf = malloc(page);
250 pid = fork();
251
252 if (buf == NULL || pid < 0)
253 atf_tc_fail("initialization failed");
254
255 if (pid == 0) {
256
257 /*
258 * Try to lock a page while
259 * RLIMIT_MEMLOCK is zero.
260 */
261 if (mlock(buf, page) != 0)
262 _exit(EXIT_FAILURE);
263
264 if (munlock(buf, page) != 0)
265 _exit(EXIT_FAILURE);
266
267 res.rlim_cur = 0;
268 res.rlim_max = 0;
269
270 if (setrlimit(RLIMIT_MEMLOCK, &res) != 0)
271 _exit(EXIT_FAILURE);
272
273 if (mlock(buf, page) != 0)
274 _exit(EXIT_SUCCESS);
275
276 (void)munlock(buf, page);
277
278 _exit(EXIT_FAILURE);
279 }
280
281 free(buf);
282
283 (void)wait(&sta);
284
285 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
286 atf_tc_fail("RLIMIT_MEMLOCK not enforced");
287 }
288
289 ATF_TC(setrlimit_nofile_1);
290 ATF_TC_HEAD(setrlimit_nofile_1, tc)
291 {
292 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NOFILE, #1");
293 }
294
295 ATF_TC_BODY(setrlimit_nofile_1, tc)
296 {
297 struct rlimit res;
298 int fd, i, rv, sta;
299 pid_t pid;
300
301 res.rlim_cur = 0;
302 res.rlim_max = 0;
303
304 pid = fork();
305 ATF_REQUIRE(pid >= 0);
306
307 if (pid == 0) {
308
309 /*
310 * Close all descriptors, set RLIMIT_NOFILE
311 * to zero, and try to open a random file.
312 * This should fail with EMFILE.
313 */
314 for (i = 0; i < 1024; i++)
315 (void)close(i);
316
317 rv = setrlimit(RLIMIT_NOFILE, &res);
318
319 if (rv != 0)
320 _exit(EXIT_FAILURE);
321
322 errno = 0;
323 fd = open("/etc/passwd", O_RDONLY);
324
325 if (fd >= 0 || errno != EMFILE)
326 _exit(EXIT_FAILURE);
327
328 _exit(EXIT_SUCCESS);
329 }
330
331 (void)wait(&sta);
332
333 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
334 atf_tc_fail("RLIMIT_NOFILE not enforced");
335 }
336
337 ATF_TC(setrlimit_nofile_2);
338 ATF_TC_HEAD(setrlimit_nofile_2, tc)
339 {
340 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NOFILE, #2");
341 }
342
343 ATF_TC_BODY(setrlimit_nofile_2, tc)
344 {
345 static const rlim_t lim = 12;
346 struct rlimit res;
347 int fd, i, rv, sta;
348 pid_t pid;
349
350 /*
351 * See that an arbitrary limit on
352 * open files is being enforced.
353 */
354 res.rlim_cur = lim;
355 res.rlim_max = lim;
356
357 pid = fork();
358 ATF_REQUIRE(pid >= 0);
359
360 if (pid == 0) {
361
362 for (i = 0; i < 1024; i++)
363 (void)close(i);
364
365 rv = setrlimit(RLIMIT_NOFILE, &res);
366
367 if (rv != 0)
368 _exit(EXIT_FAILURE);
369
370 for (i = 0; i < (int)lim; i++) {
371
372 fd = open("/etc/passwd", O_RDONLY);
373
374 if (fd < 0)
375 _exit(EXIT_FAILURE);
376 }
377
378 /*
379 * After the limit has been reached,
380 * EMFILE should again follow.
381 */
382 fd = open("/etc/passwd", O_RDONLY);
383
384 if (fd >= 0 || errno != EMFILE)
385 _exit(EXIT_FAILURE);
386
387 _exit(EXIT_SUCCESS);
388 }
389
390 (void)wait(&sta);
391
392 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
393 atf_tc_fail("RLIMIT_NOFILE not enforced");
394 }
395
396 ATF_TC(setrlimit_nproc);
397 ATF_TC_HEAD(setrlimit_nproc, tc)
398 {
399 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NPROC");
400 atf_tc_set_md_var(tc, "require.user", "unprivileged");
401 }
402
403 ATF_TC_BODY(setrlimit_nproc, tc)
404 {
405 struct rlimit res;
406 pid_t pid, cpid;
407 int sta;
408
409 pid = fork();
410 ATF_REQUIRE(pid >= 0);
411
412 if (pid == 0) {
413
414 /*
415 * Set RLIMIT_NPROC to zero and try to fork.
416 */
417 res.rlim_cur = 0;
418 res.rlim_max = 0;
419
420 if (setrlimit(RLIMIT_NPROC, &res) != 0)
421 _exit(EXIT_FAILURE);
422
423 cpid = fork();
424
425 if (cpid < 0)
426 _exit(EXIT_SUCCESS);
427
428 _exit(EXIT_FAILURE);
429 }
430
431 (void)waitpid(pid, &sta, 0);
432
433 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
434 atf_tc_fail("RLIMIT_NPROC not enforced");
435 }
436
437 ATF_TC(setrlimit_perm);
438 ATF_TC_HEAD(setrlimit_perm, tc)
439 {
440 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2) for EPERM");
441 atf_tc_set_md_var(tc, "require.user", "unprivileged");
442 }
443
444 ATF_TC_BODY(setrlimit_perm, tc)
445 {
446 struct rlimit res;
447 size_t i;
448
449 /*
450 * Try to raise the maximum limits as an user.
451 */
452 for (i = 0; i < __arraycount(rlimit); i++) {
453
454 ATF_REQUIRE(getrlimit(rlimit[i], &res) == 0);
455
456 if (res.rlim_max == UINT64_MAX) /* Overflow. */
457 continue;
458
459 errno = 0;
460 res.rlim_max = res.rlim_max + 1;
461
462 ATF_REQUIRE(setrlimit(rlimit[i], &res) != 0);
463 ATF_REQUIRE(errno == EPERM);
464 }
465 }
466
467 ATF_TP_ADD_TCS(tp)
468 {
469
470 ATF_TP_ADD_TC(tp, setrlimit_basic);
471 ATF_TP_ADD_TC(tp, setrlimit_current);
472 ATF_TP_ADD_TC(tp, setrlimit_err);
473 ATF_TP_ADD_TC(tp, setrlimit_fsize);
474 ATF_TP_ADD_TC(tp, setrlimit_memlock);
475 ATF_TP_ADD_TC(tp, setrlimit_nofile_1);
476 ATF_TP_ADD_TC(tp, setrlimit_nofile_2);
477 ATF_TP_ADD_TC(tp, setrlimit_nproc);
478 ATF_TP_ADD_TC(tp, setrlimit_perm);
479
480 return atf_no_error();
481 }
482