t_setrlimit.c revision 276478
1/* $NetBSD: t_setrlimit.c,v 1.4 2012/06/12 23:56:19 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.4 2012/06/12 23:56:19 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 53static void sighandler(int); 54static const char path[] = "setrlimit"; 55 56static 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 70ATF_TC(setrlimit_basic); 71ATF_TC_HEAD(setrlimit_basic, tc) 72{ 73 atf_tc_set_md_var(tc, "descr", "A basic soft limit test"); 74} 75 76ATF_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 109out: 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} 128 129ATF_TC(setrlimit_current); 130ATF_TC_HEAD(setrlimit_current, tc) 131{ 132 atf_tc_set_md_var(tc, "descr", "setrlimit(3) with current limits"); 133} 134 135ATF_TC_BODY(setrlimit_current, tc) 136{ 137 struct rlimit res; 138 size_t i; 139 140 for (i = 0; i < __arraycount(rlimit); i++) { 141 142 (void)memset(&res, 0, sizeof(struct rlimit)); 143 144 ATF_REQUIRE(getrlimit(rlimit[i], &res) == 0); 145 ATF_REQUIRE(setrlimit(rlimit[i], &res) == 0); 146 } 147} 148 149ATF_TC(setrlimit_err); 150ATF_TC_HEAD(setrlimit_err, tc) 151{ 152 atf_tc_set_md_var(tc, "descr", "Test error conditions"); 153} 154 155ATF_TC_BODY(setrlimit_err, tc) 156{ 157 struct rlimit res; 158 size_t i; 159 160 for (i = 0; i < __arraycount(rlimit); i++) { 161 162 errno = 0; 163 164 ATF_REQUIRE(getrlimit(rlimit[i], (void *)0) != 0); 165 ATF_REQUIRE(errno == EFAULT); 166 } 167 168 errno = 0; 169 170 ATF_REQUIRE(getrlimit(INT_MAX, &res) != 0); 171 ATF_REQUIRE(errno == EINVAL); 172} 173 174ATF_TC_WITH_CLEANUP(setrlimit_fsize); 175ATF_TC_HEAD(setrlimit_fsize, tc) 176{ 177 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_FSIZE"); 178} 179 180ATF_TC_BODY(setrlimit_fsize, tc) 181{ 182 struct rlimit res; 183 int fd, sta; 184 pid_t pid; 185 186 fd = open(path, O_RDWR | O_CREAT, 0700); 187 188 if (fd < 0) 189 atf_tc_fail("initialization failed"); 190 191 pid = fork(); 192 ATF_REQUIRE(pid >= 0); 193 194 if (pid == 0) { 195 196 res.rlim_cur = 2; 197 res.rlim_max = 2; 198 199 if (setrlimit(RLIMIT_FSIZE, &res) != 0) 200 _exit(EXIT_FAILURE); 201 202 if (signal(SIGXFSZ, sighandler) == SIG_ERR) 203 _exit(EXIT_FAILURE); 204 205 /* 206 * The third call should generate a SIGXFSZ. 207 */ 208 (void)write(fd, "X", 1); 209 (void)write(fd, "X", 1); 210 (void)write(fd, "X", 1); 211 212 _exit(EXIT_FAILURE); 213 } 214 215 (void)close(fd); 216 (void)wait(&sta); 217 (void)unlink(path); 218 219 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 220 atf_tc_fail("RLIMIT_FSIZE not enforced"); 221} 222 223ATF_TC_CLEANUP(setrlimit_fsize, tc) 224{ 225 (void)unlink(path); 226} 227 228static void 229sighandler(int signo) 230{ 231 232 if (signo != SIGXFSZ) 233 _exit(EXIT_FAILURE); 234 235 _exit(EXIT_SUCCESS); 236} 237 238ATF_TC(setrlimit_memlock); 239ATF_TC_HEAD(setrlimit_memlock, tc) 240{ 241 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_MEMLOCK"); 242} 243 244ATF_TC_BODY(setrlimit_memlock, tc) 245{ 246 struct rlimit res; 247 void *buf; 248 long page; 249 pid_t pid; 250 int sta; 251 252 page = sysconf(_SC_PAGESIZE); 253 ATF_REQUIRE(page >= 0); 254 255 buf = malloc(page); 256 pid = fork(); 257 258 if (buf == NULL || pid < 0) 259 atf_tc_fail("initialization failed"); 260 261 if (pid == 0) { 262 263 /* 264 * Try to lock a page while 265 * RLIMIT_MEMLOCK is zero. 266 */ 267 if (mlock(buf, page) != 0) 268 _exit(EXIT_FAILURE); 269 270 if (munlock(buf, page) != 0) 271 _exit(EXIT_FAILURE); 272 273 res.rlim_cur = 0; 274 res.rlim_max = 0; 275 276 if (setrlimit(RLIMIT_MEMLOCK, &res) != 0) 277 _exit(EXIT_FAILURE); 278 279 if (mlock(buf, page) != 0) 280 _exit(EXIT_SUCCESS); 281 282 (void)munlock(buf, page); 283 284 _exit(EXIT_FAILURE); 285 } 286 287 free(buf); 288 289 (void)wait(&sta); 290 291 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 292 atf_tc_fail("RLIMIT_MEMLOCK not enforced"); 293} 294 295ATF_TC(setrlimit_nofile_1); 296ATF_TC_HEAD(setrlimit_nofile_1, tc) 297{ 298 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NOFILE, #1"); 299} 300 301ATF_TC_BODY(setrlimit_nofile_1, tc) 302{ 303 struct rlimit res; 304 int fd, i, rv, sta; 305 pid_t pid; 306 307 res.rlim_cur = 0; 308 res.rlim_max = 0; 309 310 pid = fork(); 311 ATF_REQUIRE(pid >= 0); 312 313 if (pid == 0) { 314 315 /* 316 * Close all descriptors, set RLIMIT_NOFILE 317 * to zero, and try to open a random file. 318 * This should fail with EMFILE. 319 */ 320 for (i = 0; i < 1024; i++) 321 (void)close(i); 322 323 rv = setrlimit(RLIMIT_NOFILE, &res); 324 325 if (rv != 0) 326 _exit(EXIT_FAILURE); 327 328 errno = 0; 329 fd = open("/etc/passwd", O_RDONLY); 330 331 if (fd >= 0 || errno != EMFILE) 332 _exit(EXIT_FAILURE); 333 334 _exit(EXIT_SUCCESS); 335 } 336 337 (void)wait(&sta); 338 339 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 340 atf_tc_fail("RLIMIT_NOFILE not enforced"); 341} 342 343ATF_TC(setrlimit_nofile_2); 344ATF_TC_HEAD(setrlimit_nofile_2, tc) 345{ 346 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NOFILE, #2"); 347} 348 349ATF_TC_BODY(setrlimit_nofile_2, tc) 350{ 351 static const rlim_t lim = 12; 352 struct rlimit res; 353 int fd, i, rv, sta; 354 pid_t pid; 355 356 /* 357 * See that an arbitrary limit on 358 * open files is being enforced. 359 */ 360 res.rlim_cur = lim; 361 res.rlim_max = lim; 362 363 pid = fork(); 364 ATF_REQUIRE(pid >= 0); 365 366 if (pid == 0) { 367 368 for (i = 0; i < 1024; i++) 369 (void)close(i); 370 371 rv = setrlimit(RLIMIT_NOFILE, &res); 372 373 if (rv != 0) 374 _exit(EXIT_FAILURE); 375 376 for (i = 0; i < (int)lim; i++) { 377 378 fd = open("/etc/passwd", O_RDONLY); 379 380 if (fd < 0) 381 _exit(EXIT_FAILURE); 382 } 383 384 /* 385 * After the limit has been reached, 386 * EMFILE should again follow. 387 */ 388 fd = open("/etc/passwd", O_RDONLY); 389 390 if (fd >= 0 || errno != EMFILE) 391 _exit(EXIT_FAILURE); 392 393 _exit(EXIT_SUCCESS); 394 } 395 396 (void)wait(&sta); 397 398 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 399 atf_tc_fail("RLIMIT_NOFILE not enforced"); 400} 401 402ATF_TC(setrlimit_nproc); 403ATF_TC_HEAD(setrlimit_nproc, tc) 404{ 405 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2), RLIMIT_NPROC"); 406 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 407} 408 409ATF_TC_BODY(setrlimit_nproc, tc) 410{ 411 struct rlimit res; 412 pid_t pid, cpid; 413 int sta; 414 415 pid = fork(); 416 ATF_REQUIRE(pid >= 0); 417 418 if (pid == 0) { 419 420 /* 421 * Set RLIMIT_NPROC to zero and try to fork. 422 */ 423 res.rlim_cur = 0; 424 res.rlim_max = 0; 425 426 if (setrlimit(RLIMIT_NPROC, &res) != 0) 427 _exit(EXIT_FAILURE); 428 429 cpid = fork(); 430 431 if (cpid < 0) 432 _exit(EXIT_SUCCESS); 433 434 _exit(EXIT_FAILURE); 435 } 436 437 (void)waitpid(pid, &sta, 0); 438 439 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 440 atf_tc_fail("RLIMIT_NPROC not enforced"); 441} 442 443#ifdef __NetBSD__ 444ATF_TC(setrlimit_nthr); 445ATF_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 451static void 452func(lwpid_t *id) 453{ 454 printf("thread %d\n", *id); 455 fflush(stdout); 456 _lwp_exit(); 457} 458 459ATF_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#endif 481 482ATF_TC(setrlimit_perm); 483ATF_TC_HEAD(setrlimit_perm, tc) 484{ 485 atf_tc_set_md_var(tc, "descr", "Test setrlimit(2) for EPERM"); 486 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 487} 488 489ATF_TC_BODY(setrlimit_perm, tc) 490{ 491 struct rlimit res; 492 size_t i; 493 494 /* 495 * Try to raise the maximum limits as an user. 496 */ 497 for (i = 0; i < __arraycount(rlimit); i++) { 498 499 ATF_REQUIRE(getrlimit(rlimit[i], &res) == 0); 500 501#ifdef __FreeBSD__ 502 if (res.rlim_max == INT64_MAX) /* Overflow. */ 503#else 504 if (res.rlim_max == UINT64_MAX) /* Overflow. */ 505#endif 506 continue; 507 508 errno = 0; 509 res.rlim_max = res.rlim_max + 1; 510 511 ATF_CHECK_ERRNO(EPERM, setrlimit(rlimit[i], &res) != 0); 512 } 513} 514 515ATF_TP_ADD_TCS(tp) 516{ 517 518 ATF_TP_ADD_TC(tp, setrlimit_basic); 519 ATF_TP_ADD_TC(tp, setrlimit_current); 520 ATF_TP_ADD_TC(tp, setrlimit_err); 521 ATF_TP_ADD_TC(tp, setrlimit_fsize); 522 ATF_TP_ADD_TC(tp, setrlimit_memlock); 523 ATF_TP_ADD_TC(tp, setrlimit_nofile_1); 524 ATF_TP_ADD_TC(tp, setrlimit_nofile_2); 525 ATF_TP_ADD_TC(tp, setrlimit_nproc); 526 ATF_TP_ADD_TC(tp, setrlimit_perm); 527#ifdef __NetBSD__ 528 ATF_TP_ADD_TC(tp, setrlimit_nthr); 529#endif 530 531 return atf_no_error(); 532} 533