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