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