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