xref: /netbsd-src/tests/lib/libc/sys/t_setrlimit.c (revision 29fd213712376920b8464caaf1fd7a0b57afec8d)
1 /* $NetBSD: t_setrlimit.c,v 1.11 2023/12/07 16:54:44 riastradh 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.11 2023/12/07 16:54:44 riastradh 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 #include "h_macros.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);
ATF_TC_HEAD(setrlimit_basic,tc)71 ATF_TC_HEAD(setrlimit_basic, tc)
72 {
73 	atf_tc_set_md_var(tc, "descr", "A basic soft limit test");
74 }
75 
ATF_TC_BODY(setrlimit_basic,tc)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 	free(buf);
128 }
129 
130 ATF_TC(setrlimit_current);
ATF_TC_HEAD(setrlimit_current,tc)131 ATF_TC_HEAD(setrlimit_current, tc)
132 {
133 	atf_tc_set_md_var(tc, "descr", "setrlimit(3) with current limits");
134 }
135 
ATF_TC_BODY(setrlimit_current,tc)136 ATF_TC_BODY(setrlimit_current, tc)
137 {
138 	struct rlimit res;
139 	size_t i;
140 
141 	for (i = 0; i < __arraycount(rlimit); i++) {
142 
143 		(void)memset(&res, 0, sizeof(struct rlimit));
144 
145 		ATF_REQUIRE(getrlimit(rlimit[i], &res) == 0);
146 		ATF_REQUIRE(setrlimit(rlimit[i], &res) == 0);
147 	}
148 }
149 
150 ATF_TC(setrlimit_err);
ATF_TC_HEAD(setrlimit_err,tc)151 ATF_TC_HEAD(setrlimit_err, tc)
152 {
153 	atf_tc_set_md_var(tc, "descr", "Test error conditions");
154 }
155 
ATF_TC_BODY(setrlimit_err,tc)156 ATF_TC_BODY(setrlimit_err, tc)
157 {
158 	struct rlimit res;
159 	size_t i;
160 
161 	for (i = 0; i < __arraycount(rlimit); i++) {
162 
163 		errno = 0;
164 
165 		ATF_REQUIRE(getrlimit(rlimit[i], (void *)0) != 0);
166 		ATF_REQUIRE(errno == EFAULT);
167 	}
168 
169 	errno = 0;
170 
171 	ATF_REQUIRE(getrlimit(INT_MAX, &res) != 0);
172 	ATF_REQUIRE(errno == EINVAL);
173 }
174 
175 ATF_TC_WITH_CLEANUP(setrlimit_fsize);
ATF_TC_HEAD(setrlimit_fsize,tc)176 ATF_TC_HEAD(setrlimit_fsize, tc)
177 {
178 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_FSIZE");
179 }
180 
ATF_TC_BODY(setrlimit_fsize,tc)181 ATF_TC_BODY(setrlimit_fsize, tc)
182 {
183 	struct rlimit res;
184 	int fd, sta;
185 	pid_t pid;
186 
187 	fd = open(path, O_RDWR | O_CREAT, 0700);
188 
189 	if (fd < 0)
190 		atf_tc_fail("initialization failed");
191 
192 	pid = fork();
193 	ATF_REQUIRE(pid >= 0);
194 
195 	if (pid == 0) {
196 
197 		res.rlim_cur = 2;
198 		res.rlim_max = 2;
199 
200 		if (setrlimit(RLIMIT_FSIZE, &res) != 0)
201 			_exit(EXIT_FAILURE);
202 
203 		if (signal(SIGXFSZ, sighandler) == SIG_ERR)
204 			_exit(EXIT_FAILURE);
205 
206 		/*
207 		 * The third call should generate a SIGXFSZ.
208 		 */
209 		(void)write(fd, "X", 1);
210 		(void)write(fd, "X", 1);
211 		(void)write(fd, "X", 1);
212 
213 		_exit(EXIT_FAILURE);
214 	}
215 
216 	(void)close(fd);
217 	(void)wait(&sta);
218 	(void)unlink(path);
219 
220 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
221 		atf_tc_fail("RLIMIT_FSIZE not enforced");
222 }
223 
ATF_TC_CLEANUP(setrlimit_fsize,tc)224 ATF_TC_CLEANUP(setrlimit_fsize, tc)
225 {
226 	(void)unlink(path);
227 }
228 
229 static void
sighandler(int signo)230 sighandler(int signo)
231 {
232 
233 	if (signo != SIGXFSZ)
234 		_exit(EXIT_FAILURE);
235 
236 	_exit(EXIT_SUCCESS);
237 }
238 
239 ATF_TC(setrlimit_memlock);
ATF_TC_HEAD(setrlimit_memlock,tc)240 ATF_TC_HEAD(setrlimit_memlock, tc)
241 {
242 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_MEMLOCK");
243 }
244 
ATF_TC_BODY(setrlimit_memlock,tc)245 ATF_TC_BODY(setrlimit_memlock, tc)
246 {
247 	struct rlimit res;
248 	void *buf;
249 	long page;
250 	pid_t pid;
251 	int sta;
252 
253 	page = sysconf(_SC_PAGESIZE);
254 	ATF_REQUIRE(page >= 0);
255 
256 	buf = malloc(page);
257 	pid = fork();
258 
259 	if (buf == NULL || pid < 0)
260 		atf_tc_fail("initialization failed");
261 
262 	if (pid == 0) {
263 
264 		/*
265 		 * Try to lock a page while
266 		 * RLIMIT_MEMLOCK is zero.
267 		 */
268 		if (mlock(buf, page) != 0)
269 			_exit(EXIT_FAILURE);
270 
271 		if (munlock(buf, page) != 0)
272 			_exit(EXIT_FAILURE);
273 
274 		res.rlim_cur = 0;
275 		res.rlim_max = 0;
276 
277 		if (setrlimit(RLIMIT_MEMLOCK, &res) != 0)
278 			_exit(EXIT_FAILURE);
279 
280 		if (mlock(buf, page) != 0)
281 			_exit(EXIT_SUCCESS);
282 
283 		(void)munlock(buf, page);
284 
285 		_exit(EXIT_FAILURE);
286 	}
287 
288 	free(buf);
289 
290 	(void)wait(&sta);
291 
292 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
293 		atf_tc_fail("RLIMIT_MEMLOCK not enforced");
294 }
295 
296 ATF_TC(setrlimit_nofile_1);
ATF_TC_HEAD(setrlimit_nofile_1,tc)297 ATF_TC_HEAD(setrlimit_nofile_1, tc)
298 {
299 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NOFILE, #1");
300 }
301 
ATF_TC_BODY(setrlimit_nofile_1,tc)302 ATF_TC_BODY(setrlimit_nofile_1, tc)
303 {
304 	struct rlimit res;
305 	int fd, i, rv, sta;
306 	pid_t pid;
307 
308 	res.rlim_cur = 0;
309 	res.rlim_max = 0;
310 
311 	pid = fork();
312 	ATF_REQUIRE(pid >= 0);
313 
314 	if (pid == 0) {
315 
316 		/*
317 		 * Close all descriptors, set RLIMIT_NOFILE
318 		 * to zero, and try to open a random file.
319 		 * This should fail with EMFILE.
320 		 */
321 		for (i = 0; i < 1024; i++)
322 			(void)close(i);
323 
324 		rv = setrlimit(RLIMIT_NOFILE, &res);
325 
326 		if (rv != 0)
327 			_exit(EXIT_FAILURE);
328 
329 		errno = 0;
330 		fd = open("/etc/passwd", O_RDONLY);
331 
332 		if (fd >= 0 || errno != EMFILE)
333 			_exit(EXIT_FAILURE);
334 
335 		_exit(EXIT_SUCCESS);
336 	}
337 
338 	(void)wait(&sta);
339 
340 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
341 		atf_tc_fail("RLIMIT_NOFILE not enforced");
342 }
343 
344 ATF_TC(setrlimit_nofile_2);
ATF_TC_HEAD(setrlimit_nofile_2,tc)345 ATF_TC_HEAD(setrlimit_nofile_2, tc)
346 {
347 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NOFILE, #2");
348 }
349 
ATF_TC_BODY(setrlimit_nofile_2,tc)350 ATF_TC_BODY(setrlimit_nofile_2, tc)
351 {
352 	static const rlim_t lim = 12;
353 	struct rlimit res;
354 	int fd, i, rv, sta;
355 	pid_t pid;
356 
357 	/*
358 	 * See that an arbitrary limit on
359 	 * open files is being enforced.
360 	 */
361 	res.rlim_cur = lim;
362 	res.rlim_max = lim;
363 
364 	pid = fork();
365 	ATF_REQUIRE(pid >= 0);
366 
367 	if (pid == 0) {
368 
369 		for (i = 0; i < 1024; i++)
370 			(void)close(i);
371 
372 		rv = setrlimit(RLIMIT_NOFILE, &res);
373 
374 		if (rv != 0)
375 			_exit(EXIT_FAILURE);
376 
377 		for (i = 0; i < (int)lim; i++) {
378 
379 			fd = open("/etc/passwd", O_RDONLY);
380 
381 			if (fd < 0)
382 				_exit(EXIT_FAILURE);
383 		}
384 
385 		/*
386 		 * After the limit has been reached,
387 		 * EMFILE should again follow.
388 		 */
389 		fd = open("/etc/passwd", O_RDONLY);
390 
391 		if (fd >= 0 || errno != EMFILE)
392 			_exit(EXIT_FAILURE);
393 
394 		_exit(EXIT_SUCCESS);
395 	}
396 
397 	(void)wait(&sta);
398 
399 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
400 		atf_tc_fail("RLIMIT_NOFILE not enforced");
401 }
402 
403 ATF_TC(setrlimit_nproc);
ATF_TC_HEAD(setrlimit_nproc,tc)404 ATF_TC_HEAD(setrlimit_nproc, tc)
405 {
406 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NPROC");
407 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
408 }
409 
ATF_TC_BODY(setrlimit_nproc,tc)410 ATF_TC_BODY(setrlimit_nproc, tc)
411 {
412 	struct rlimit res;
413 	pid_t pid, cpid;
414 	int sta;
415 
416 	pid = fork();
417 	ATF_REQUIRE(pid >= 0);
418 
419 	if (pid == 0) {
420 
421 		/*
422 		 * Set RLIMIT_NPROC to zero and try to fork.
423 		 */
424 		res.rlim_cur = 0;
425 		res.rlim_max = 0;
426 
427 		if (setrlimit(RLIMIT_NPROC, &res) != 0)
428 			_exit(EXIT_FAILURE);
429 
430 		cpid = fork();
431 
432 		if (cpid < 0)
433 			_exit(EXIT_SUCCESS);
434 
435 		_exit(EXIT_FAILURE);
436 	}
437 
438 	(void)waitpid(pid, &sta, 0);
439 
440 	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
441 		atf_tc_fail("RLIMIT_NPROC not enforced");
442 }
443 
444 ATF_TC(setrlimit_nthr);
ATF_TC_HEAD(setrlimit_nthr,tc)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
func(lwpid_t * id)452 func(lwpid_t *id)
453 {
454 	printf("thread %d\n", *id);
455 	fflush(stdout);
456 	_lwp_exit();
457 }
458 
ATF_TC_BODY(setrlimit_nthr,tc)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 
481 ATF_TC(setrlimit_perm);
ATF_TC_HEAD(setrlimit_perm,tc)482 ATF_TC_HEAD(setrlimit_perm, tc)
483 {
484 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2) for EPERM");
485 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
486 }
487 
ATF_TC_BODY(setrlimit_perm,tc)488 ATF_TC_BODY(setrlimit_perm, tc)
489 {
490 	struct rlimit res;
491 	size_t i;
492 
493 	/*
494 	 * Try to raise the maximum limits as an user.
495 	 */
496 	for (i = 0; i < __arraycount(rlimit); i++) {
497 
498 		ATF_REQUIRE(getrlimit(rlimit[i], &res) == 0);
499 
500 		if (res.rlim_max == UINT64_MAX) /* Overflow. */
501 			continue;
502 
503 		errno = 0;
504 		res.rlim_max = res.rlim_max + 1;
505 
506 		ATF_CHECK_ERRNO(EPERM, setrlimit(rlimit[i], &res) != 0);
507 	}
508 }
509 
510 ATF_TC(setrlimit_stack);
ATF_TC_HEAD(setrlimit_stack,tc)511 ATF_TC_HEAD(setrlimit_stack, tc)
512 {
513 	atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_STACK");
514 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
515 }
516 
ATF_TC_BODY(setrlimit_stack,tc)517 ATF_TC_BODY(setrlimit_stack, tc)
518 {
519 	struct rlimit res;
520 
521 	/* Ensure soft limit is not bigger than hard limit */
522 	res.rlim_cur = res.rlim_max = 6 * 1024 * 1024;
523 	ATF_REQUIRE(setrlimit(RLIMIT_STACK, &res) == 0);
524 	ATF_REQUIRE(getrlimit(RLIMIT_STACK, &res) == 0);
525 	ATF_CHECK(res.rlim_cur <= res.rlim_max);
526 
527 }
528 
529 ATF_TC(setrlimit_stack_growshrink);
ATF_TC_HEAD(setrlimit_stack_growshrink,tc)530 ATF_TC_HEAD(setrlimit_stack_growshrink, tc)
531 {
532 	atf_tc_set_md_var(tc, "descr",
533 	    "Test that setrlimit(2), RLIMIT_STACK, grows & shrinks the stack");
534 }
535 
536 /*
537  * checkstackchild(n)
538  *
539  *	Allocate an array of size n on the stack, and verify it can be
540  *	used.  If it can't be used, this will crash with SIGSEGV,
541  *	deliberately.
542  */
543 _Pragma("GCC diagnostic push")
544 _Pragma("GCC diagnostic ignored \"-Wstack-protector\"")
545 static void
checkstackchild(size_t n)546 checkstackchild(size_t n)
547 {
548 	volatile char *const x = alloca(n);
549 	size_t i;
550 
551 	for (i = 0; i < n; i++)
552 		x[i] = 0x1a;
553 }
554 _Pragma("GCC diagnostic pop")
555 
556 /*
557  * checkstack(n, expectsegv)
558  *
559  *	Check whether we can allocate an array of size n on the stack.
560  *
561  *	- If expectsegv, verify that access fails with SIGSEGV.
562  *	- If not expectsegv, verify that access succeeds.
563  *
564  *	Do this in a subprocess rather than with a SIGSEGV handler,
565  *	because once we've allocated an array of size n on the stack,
566  *	in the case where the stack is inaccessible, we have just
567  *	trashed the stack pointer so badly we can't make function calls
568  *	like to a SIGSEGV handler.
569  *
570  *	(We could use an alternate signal stack, but I already wrote it
571  *	this way, and this is a little simpler and more robust than
572  *	juggling signals, setjmp/longjmp, and sigaltstack.)
573  */
574 static void
checkstack(size_t n,int expectsegv)575 checkstack(size_t n, int expectsegv)
576 {
577 	pid_t forked, waited;
578 	int status;
579 
580 	RL(forked = fork());
581 	if (forked == 0) {	/* child */
582 		checkstackchild(n);
583 		_exit(expectsegv);
584 	}
585 
586 	/* parent */
587 	RL(waited = waitpid(forked, &status, 0));
588 	ATF_REQUIRE_EQ_MSG(waited, forked, "waited=%jd forked=%jd",
589 	    (intmax_t)waited, (intmax_t)forked);
590 	if (expectsegv) {
591 		ATF_REQUIRE_MSG(!WIFEXITED(status),
592 		    "expected signal but exited normally with status %d",
593 		    WEXITSTATUS(status));
594 		ATF_REQUIRE_MSG(WIFSIGNALED(status), "status=0x%x", status);
595 		ATF_REQUIRE_EQ_MSG(WTERMSIG(status), SIGSEGV, "termsig=%d",
596 		    WTERMSIG(status));
597 	} else {
598 		ATF_REQUIRE_MSG(!WIFSIGNALED(status),
599 		    "expected normal exit but terminated on signal %d",
600 		    WTERMSIG(status));
601 		ATF_REQUIRE_MSG(WIFEXITED(status), "status=0x%x", status);
602 		ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), 0, "exitstatus=%d",
603 		    WEXITSTATUS(status));
604 	}
605 }
606 
ATF_TC_BODY(setrlimit_stack_growshrink,tc)607 ATF_TC_BODY(setrlimit_stack_growshrink, tc)
608 {
609 	struct rlimit res;
610 	size_t n;
611 
612 	/*
613 	 * Disable core dumps -- we're going to deliberately cause
614 	 * SIGSEGV to test stack accessibility (which breaks even
615 	 * calling a function so we can't just use a SIGSEGV handler),
616 	 * so let's not waste time dumping core.
617 	 */
618 	res = (struct rlimit){ .rlim_cur = 0, .rlim_max = 0 };
619 	RL(setrlimit(RLIMIT_CORE, &res));
620 
621 	/*
622 	 * Get the current stack size and hard limit.
623 	 */
624 	RL(getrlimit(RLIMIT_STACK, &res));
625 	n = res.rlim_cur;
626 
627 	/*
628 	 * Verify that we can't get at pages past the end of the stack
629 	 * right now.
630 	 */
631 	checkstack(n, /*expectsegv*/1);
632 
633 	/*
634 	 * Stop if the hard limit is too small to test.  Not sure
635 	 * exactly how much more space we need to verify that setrlimit
636 	 * actually expands the stack without examining the current
637 	 * stack pointer relative to the process's stack base, so we'll
638 	 * just double the stack size -- definitely enough to test
639 	 * stack growth -- and hope the hard rlimit is big enough to
640 	 * let us double it.
641 	 */
642 	if (n > res.rlim_max/2)
643 		atf_tc_skip("hard stack rlimit is too small");
644 
645 	/*
646 	 * Double the stack size.  This way we can allocate an array of
647 	 * length equal to the current stack size and be guaranteed
648 	 * that (a) it can be allocated, and (b) access to it requires
649 	 * the stack to have grown.
650 	 */
651 	res.rlim_cur = 2*n;
652 	RL(setrlimit(RLIMIT_STACK, &res));
653 
654 	/*
655 	 * Verify that we can now get at pages past the end of the new
656 	 * stack but not beyond that.
657 	 */
658 	checkstack(n, /*expectsegv*/0);
659 	if (n < SIZE_MAX/2)
660 		checkstack(2*n, /*expectsegv*/1);
661 
662 	/*
663 	 * Restore the stack size and verify that we can no longer
664 	 * access an array of length equal to the whole stack size.
665 	 */
666 	res.rlim_cur = n;
667 	RL(setrlimit(RLIMIT_STACK, &res));
668 	checkstack(n, /*expectsegv*/1);
669 }
670 
ATF_TP_ADD_TCS(tp)671 ATF_TP_ADD_TCS(tp)
672 {
673 
674 	ATF_TP_ADD_TC(tp, setrlimit_basic);
675 	ATF_TP_ADD_TC(tp, setrlimit_current);
676 	ATF_TP_ADD_TC(tp, setrlimit_err);
677 	ATF_TP_ADD_TC(tp, setrlimit_fsize);
678 	ATF_TP_ADD_TC(tp, setrlimit_memlock);
679 	ATF_TP_ADD_TC(tp, setrlimit_nofile_1);
680 	ATF_TP_ADD_TC(tp, setrlimit_nofile_2);
681 	ATF_TP_ADD_TC(tp, setrlimit_nproc);
682 	ATF_TP_ADD_TC(tp, setrlimit_perm);
683 	ATF_TP_ADD_TC(tp, setrlimit_nthr);
684 	ATF_TP_ADD_TC(tp, setrlimit_stack);
685 	ATF_TP_ADD_TC(tp, setrlimit_stack_growshrink);
686 
687 	return atf_no_error();
688 }
689