xref: /freebsd/contrib/netbsd-tests/lib/libc/sys/t_setrlimit.c (revision 39ee7a7a6bdd1557b1c3532abf60d139798ac88b)
1 /* $NetBSD: t_setrlimit.c,v 1.4 2012/06/12 23:56:19 christos 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.4 2012/06/12 23:56:19 christos 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 #ifdef __NetBSD__
43 #include <lwp.h>
44 #endif
45 #include <signal.h>
46 #include <stdint.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <ucontext.h>
51 #include <unistd.h>
52 
53 static void		 sighandler(int);
54 static const char	 path[] = "setrlimit";
55 
56 static const int rlimit[] = {
57 	RLIMIT_AS,
58 	RLIMIT_CORE,
59 	RLIMIT_CPU,
60 	RLIMIT_DATA,
61 	RLIMIT_FSIZE,
62 	RLIMIT_MEMLOCK,
63 	RLIMIT_NOFILE,
64 	RLIMIT_NPROC,
65 	RLIMIT_RSS,
66 	RLIMIT_SBSIZE,
67 	RLIMIT_STACK
68 };
69 
70 ATF_TC(setrlimit_basic);
71 ATF_TC_HEAD(setrlimit_basic, tc)
72 {
73 	atf_tc_set_md_var(tc, "descr", "A basic soft limit test");
74 }
75 
76 ATF_TC_BODY(setrlimit_basic, tc)
77 {
78 	struct rlimit res;
79 	int *buf, lim;
80 	size_t i;
81 
82 	buf = calloc(__arraycount(rlimit), sizeof(int));
83 
84 	if (buf == NULL)
85 		atf_tc_fail("initialization failed");
86 
87 	for (i = lim = 0; i < __arraycount(rlimit); i++) {
88 
89 		(void)memset(&res, 0, sizeof(struct rlimit));
90 
91 		if (getrlimit(rlimit[i], &res) != 0)
92 			continue;
93 
94 		if (res.rlim_cur == RLIM_INFINITY || res.rlim_cur == 0)
95 			continue;
96 
97 		if (res.rlim_cur == res.rlim_max) /* An unprivileged run. */
98 			continue;
99 
100 		buf[i] = res.rlim_cur;
101 		res.rlim_cur = res.rlim_cur - 1;
102 
103 		if (setrlimit(rlimit[i], &res) != 0) {
104 			lim = rlimit[i];
105 			goto out;
106 		}
107 	}
108 
109 out:
110 	for (i = 0; i < __arraycount(rlimit); i++) {
111 
112 		(void)memset(&res, 0, sizeof(struct rlimit));
113 
114 		if (buf[i] == 0)
115 			continue;
116 
117 		if (getrlimit(rlimit[i], &res) != 0)
118 			continue;
119 
120 		res.rlim_cur = buf[i];
121 
122 		(void)setrlimit(rlimit[i], &res);
123 	}
124 
125 	if (lim != 0)
126 		atf_tc_fail("failed to set limit (%d)", lim);
127 }
128 
129 ATF_TC(setrlimit_current);
130 ATF_TC_HEAD(setrlimit_current, tc)
131 {
132 	atf_tc_set_md_var(tc, "descr", "setrlimit(3) with current limits");
133 }
134 
135 ATF_TC_BODY(setrlimit_current, tc)
136 {
137 	struct rlimit res;
138 	size_t i;
139 
140 	for (i = 0; i < __arraycount(rlimit); i++) {
141 
142 		(void)memset(&res, 0, sizeof(struct rlimit));
143 
144 		ATF_REQUIRE(getrlimit(rlimit[i], &res) == 0);
145 		ATF_REQUIRE(setrlimit(rlimit[i], &res) == 0);
146 	}
147 }
148 
149 ATF_TC(setrlimit_err);
150 ATF_TC_HEAD(setrlimit_err, tc)
151 {
152 	atf_tc_set_md_var(tc, "descr", "Test error conditions");
153 }
154 
155 ATF_TC_BODY(setrlimit_err, tc)
156 {
157 	struct rlimit res;
158 	size_t i;
159 
160 	for (i = 0; i < __arraycount(rlimit); i++) {
161 
162 		errno = 0;
163 
164 		ATF_REQUIRE(getrlimit(rlimit[i], (void *)0) != 0);
165 		ATF_REQUIRE(errno == EFAULT);
166 	}
167 
168 	errno = 0;
169 
170 	ATF_REQUIRE(getrlimit(INT_MAX, &res) != 0);
171 	ATF_REQUIRE(errno == EINVAL);
172 }
173 
174 ATF_TC_WITH_CLEANUP(setrlimit_fsize);
175 ATF_TC_HEAD(setrlimit_fsize, tc)
176 {
177 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_FSIZE");
178 }
179 
180 ATF_TC_BODY(setrlimit_fsize, tc)
181 {
182 	struct rlimit res;
183 	int fd, sta;
184 	pid_t pid;
185 
186 	fd = open(path, O_RDWR | O_CREAT, 0700);
187 
188 	if (fd < 0)
189 		atf_tc_fail("initialization failed");
190 
191 	pid = fork();
192 	ATF_REQUIRE(pid >= 0);
193 
194 	if (pid == 0) {
195 
196 		res.rlim_cur = 2;
197 		res.rlim_max = 2;
198 
199 		if (setrlimit(RLIMIT_FSIZE, &res) != 0)
200 			_exit(EXIT_FAILURE);
201 
202 		if (signal(SIGXFSZ, sighandler) == SIG_ERR)
203 			_exit(EXIT_FAILURE);
204 
205 		/*
206 		 * The third call should generate a SIGXFSZ.
207 		 */
208 		(void)write(fd, "X", 1);
209 		(void)write(fd, "X", 1);
210 		(void)write(fd, "X", 1);
211 
212 		_exit(EXIT_FAILURE);
213 	}
214 
215 	(void)close(fd);
216 	(void)wait(&sta);
217 	(void)unlink(path);
218 
219 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
220 		atf_tc_fail("RLIMIT_FSIZE not enforced");
221 }
222 
223 ATF_TC_CLEANUP(setrlimit_fsize, tc)
224 {
225 	(void)unlink(path);
226 }
227 
228 static void
229 sighandler(int signo)
230 {
231 
232 	if (signo != SIGXFSZ)
233 		_exit(EXIT_FAILURE);
234 
235 	_exit(EXIT_SUCCESS);
236 }
237 
238 ATF_TC(setrlimit_memlock);
239 ATF_TC_HEAD(setrlimit_memlock, tc)
240 {
241 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_MEMLOCK");
242 }
243 
244 ATF_TC_BODY(setrlimit_memlock, tc)
245 {
246 	struct rlimit res;
247 	void *buf;
248 	long page;
249 	pid_t pid;
250 	int sta;
251 
252 	page = sysconf(_SC_PAGESIZE);
253 	ATF_REQUIRE(page >= 0);
254 
255 	buf = malloc(page);
256 	pid = fork();
257 
258 	if (buf == NULL || pid < 0)
259 		atf_tc_fail("initialization failed");
260 
261 	if (pid == 0) {
262 
263 		/*
264 		 * Try to lock a page while
265 		 * RLIMIT_MEMLOCK is zero.
266 		 */
267 		if (mlock(buf, page) != 0)
268 			_exit(EXIT_FAILURE);
269 
270 		if (munlock(buf, page) != 0)
271 			_exit(EXIT_FAILURE);
272 
273 		res.rlim_cur = 0;
274 		res.rlim_max = 0;
275 
276 		if (setrlimit(RLIMIT_MEMLOCK, &res) != 0)
277 			_exit(EXIT_FAILURE);
278 
279 		if (mlock(buf, page) != 0)
280 			_exit(EXIT_SUCCESS);
281 
282 		(void)munlock(buf, page);
283 
284 		_exit(EXIT_FAILURE);
285 	}
286 
287 	free(buf);
288 
289 	(void)wait(&sta);
290 
291 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
292 		atf_tc_fail("RLIMIT_MEMLOCK not enforced");
293 }
294 
295 ATF_TC(setrlimit_nofile_1);
296 ATF_TC_HEAD(setrlimit_nofile_1, tc)
297 {
298 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NOFILE, #1");
299 }
300 
301 ATF_TC_BODY(setrlimit_nofile_1, tc)
302 {
303 	struct rlimit res;
304 	int fd, i, rv, sta;
305 	pid_t pid;
306 
307 	res.rlim_cur = 0;
308 	res.rlim_max = 0;
309 
310 	pid = fork();
311 	ATF_REQUIRE(pid >= 0);
312 
313 	if (pid == 0) {
314 
315 		/*
316 		 * Close all descriptors, set RLIMIT_NOFILE
317 		 * to zero, and try to open a random file.
318 		 * This should fail with EMFILE.
319 		 */
320 		for (i = 0; i < 1024; i++)
321 			(void)close(i);
322 
323 		rv = setrlimit(RLIMIT_NOFILE, &res);
324 
325 		if (rv != 0)
326 			_exit(EXIT_FAILURE);
327 
328 		errno = 0;
329 		fd = open("/etc/passwd", O_RDONLY);
330 
331 		if (fd >= 0 || errno != EMFILE)
332 			_exit(EXIT_FAILURE);
333 
334 		_exit(EXIT_SUCCESS);
335 	}
336 
337 	(void)wait(&sta);
338 
339 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
340 		atf_tc_fail("RLIMIT_NOFILE not enforced");
341 }
342 
343 ATF_TC(setrlimit_nofile_2);
344 ATF_TC_HEAD(setrlimit_nofile_2, tc)
345 {
346 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NOFILE, #2");
347 }
348 
349 ATF_TC_BODY(setrlimit_nofile_2, tc)
350 {
351 	static const rlim_t lim = 12;
352 	struct rlimit res;
353 	int fd, i, rv, sta;
354 	pid_t pid;
355 
356 	/*
357 	 * See that an arbitrary limit on
358 	 * open files is being enforced.
359 	 */
360 	res.rlim_cur = lim;
361 	res.rlim_max = lim;
362 
363 	pid = fork();
364 	ATF_REQUIRE(pid >= 0);
365 
366 	if (pid == 0) {
367 
368 		for (i = 0; i < 1024; i++)
369 			(void)close(i);
370 
371 		rv = setrlimit(RLIMIT_NOFILE, &res);
372 
373 		if (rv != 0)
374 			_exit(EXIT_FAILURE);
375 
376 		for (i = 0; i < (int)lim; i++) {
377 
378 			fd = open("/etc/passwd", O_RDONLY);
379 
380 			if (fd < 0)
381 				_exit(EXIT_FAILURE);
382 		}
383 
384 		/*
385 		 * After the limit has been reached,
386 		 * EMFILE should again follow.
387 		 */
388 		fd = open("/etc/passwd", O_RDONLY);
389 
390 		if (fd >= 0 || errno != EMFILE)
391 			_exit(EXIT_FAILURE);
392 
393 		_exit(EXIT_SUCCESS);
394 	}
395 
396 	(void)wait(&sta);
397 
398 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
399 		atf_tc_fail("RLIMIT_NOFILE not enforced");
400 }
401 
402 ATF_TC(setrlimit_nproc);
403 ATF_TC_HEAD(setrlimit_nproc, tc)
404 {
405 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NPROC");
406 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
407 }
408 
409 ATF_TC_BODY(setrlimit_nproc, tc)
410 {
411 	struct rlimit res;
412 	pid_t pid, cpid;
413 	int sta;
414 
415 	pid = fork();
416 	ATF_REQUIRE(pid >= 0);
417 
418 	if (pid == 0) {
419 
420 		/*
421 		 * Set RLIMIT_NPROC to zero and try to fork.
422 		 */
423 		res.rlim_cur = 0;
424 		res.rlim_max = 0;
425 
426 		if (setrlimit(RLIMIT_NPROC, &res) != 0)
427 			_exit(EXIT_FAILURE);
428 
429 		cpid = fork();
430 
431 		if (cpid < 0)
432 			_exit(EXIT_SUCCESS);
433 
434 		_exit(EXIT_FAILURE);
435 	}
436 
437 	(void)waitpid(pid, &sta, 0);
438 
439 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
440 		atf_tc_fail("RLIMIT_NPROC not enforced");
441 }
442 
443 #ifdef __NetBSD__
444 ATF_TC(setrlimit_nthr);
445 ATF_TC_HEAD(setrlimit_nthr, tc)
446 {
447 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NTHR");
448 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
449 }
450 
451 static void
452 func(lwpid_t *id)
453 {
454 	printf("thread %d\n", *id);
455 	fflush(stdout);
456 	_lwp_exit();
457 }
458 
459 ATF_TC_BODY(setrlimit_nthr, tc)
460 {
461 	struct rlimit res;
462 	lwpid_t lwpid;
463 	ucontext_t c;
464 
465 	/*
466 	 * Set RLIMIT_NTHR to zero and try to create a thread.
467 	 */
468 	res.rlim_cur = 0;
469 	res.rlim_max = 0;
470 	ATF_REQUIRE(setrlimit(RLIMIT_NTHR, &res) == 0);
471 	ATF_REQUIRE(getcontext(&c) == 0);
472 	c.uc_link = NULL;
473 	sigemptyset(&c.uc_sigmask);
474 	c.uc_stack.ss_flags = 0;
475 	c.uc_stack.ss_size = 4096;
476 	ATF_REQUIRE((c.uc_stack.ss_sp = malloc(c.uc_stack.ss_size)) != NULL);
477 	makecontext(&c, func, 1, &lwpid);
478 	ATF_CHECK_ERRNO(EAGAIN, _lwp_create(&c, 0, &lwpid) == -1);
479 }
480 #endif
481 
482 ATF_TC(setrlimit_perm);
483 ATF_TC_HEAD(setrlimit_perm, tc)
484 {
485 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2) for EPERM");
486 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
487 }
488 
489 ATF_TC_BODY(setrlimit_perm, tc)
490 {
491 	struct rlimit res;
492 	size_t i;
493 
494 	/*
495 	 * Try to raise the maximum limits as an user.
496 	 */
497 	for (i = 0; i < __arraycount(rlimit); i++) {
498 
499 		ATF_REQUIRE(getrlimit(rlimit[i], &res) == 0);
500 
501 #ifdef __FreeBSD__
502 		if (res.rlim_max == INT64_MAX) /* Overflow. */
503 #else
504 		if (res.rlim_max == UINT64_MAX) /* Overflow. */
505 #endif
506 			continue;
507 
508 		errno = 0;
509 		res.rlim_max = res.rlim_max + 1;
510 
511 		ATF_CHECK_ERRNO(EPERM, setrlimit(rlimit[i], &res) != 0);
512 	}
513 }
514 
515 ATF_TP_ADD_TCS(tp)
516 {
517 
518 	ATF_TP_ADD_TC(tp, setrlimit_basic);
519 	ATF_TP_ADD_TC(tp, setrlimit_current);
520 	ATF_TP_ADD_TC(tp, setrlimit_err);
521 	ATF_TP_ADD_TC(tp, setrlimit_fsize);
522 	ATF_TP_ADD_TC(tp, setrlimit_memlock);
523 	ATF_TP_ADD_TC(tp, setrlimit_nofile_1);
524 	ATF_TP_ADD_TC(tp, setrlimit_nofile_2);
525 	ATF_TP_ADD_TC(tp, setrlimit_nproc);
526 	ATF_TP_ADD_TC(tp, setrlimit_perm);
527 #ifdef __NetBSD__
528 	ATF_TP_ADD_TC(tp, setrlimit_nthr);
529 #endif
530 
531 	return atf_no_error();
532 }
533