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