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