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