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