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