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