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); 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 free(buf); 128 } 129 130 ATF_TC(setrlimit_current); 131 ATF_TC_HEAD(setrlimit_current, tc) 132 { 133 atf_tc_set_md_var(tc, "descr", "setrlimit(3) with current limits"); 134 } 135 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); 151 ATF_TC_HEAD(setrlimit_err, tc) 152 { 153 atf_tc_set_md_var(tc, "descr", "Test error conditions"); 154 } 155 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); 176 ATF_TC_HEAD(setrlimit_fsize, tc) 177 { 178 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_FSIZE"); 179 } 180 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 224 ATF_TC_CLEANUP(setrlimit_fsize, tc) 225 { 226 (void)unlink(path); 227 } 228 229 static void 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); 240 ATF_TC_HEAD(setrlimit_memlock, tc) 241 { 242 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_MEMLOCK"); 243 } 244 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); 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 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); 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 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); 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 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); 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 481 ATF_TC(setrlimit_perm); 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 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); 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 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); 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 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 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 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 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