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