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