xref: /illumos-gate/usr/src/uts/common/syscall/rlimit.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/inttypes.h>
36 #include <sys/sysmacros.h>
37 #include <sys/systm.h>
38 #include <sys/tuneable.h>
39 #include <sys/user.h>
40 #include <sys/errno.h>
41 #include <sys/vnode.h>
42 #include <sys/file.h>
43 #include <sys/proc.h>
44 #include <sys/resource.h>
45 #include <sys/ulimit.h>
46 #include <sys/debug.h>
47 #include <sys/rctl.h>
48 
49 #include <vm/as.h>
50 
51 /*
52  * Perhaps ulimit could be moved into a user library, as calls to
53  * getrlimit and setrlimit, were it not for binary compatibility
54  * restrictions.
55  */
56 long
57 ulimit(int cmd, long arg)
58 {
59 	proc_t *p = curproc;
60 	long	retval;
61 
62 	switch (cmd) {
63 
64 	case UL_GFILLIM: /* Return current file size limit. */
65 	{
66 		rlim64_t filesize;
67 
68 		mutex_enter(&p->p_lock);
69 		filesize = rctl_enforced_value(rctlproc_legacy[RLIMIT_FSIZE],
70 		    p->p_rctls, p);
71 		mutex_exit(&p->p_lock);
72 
73 		if (get_udatamodel() == DATAMODEL_ILP32) {
74 			/*
75 			 * File size is returned in blocks for ulimit.
76 			 * This function is deprecated and therefore LFS API
77 			 * didn't define the behaviour of ulimit.
78 			 * Here we return maximum value of file size possible
79 			 * so that applications that do not check errors
80 			 * continue to work.
81 			 */
82 			if (filesize > MAXOFF32_T)
83 				filesize = MAXOFF32_T;
84 			retval = ((int)filesize >> SCTRSHFT);
85 		} else
86 			retval = filesize >> SCTRSHFT;
87 		break;
88 	}
89 
90 	case UL_SFILLIM: /* Set new file size limit. */
91 	{
92 		int error = 0;
93 		rlim64_t lim = (rlim64_t)arg;
94 		struct rlimit64 rl64;
95 		rctl_alloc_gp_t *gp = rctl_rlimit_set_prealloc(1);
96 
97 		if (lim >= (((rlim64_t)MAXOFFSET_T) >> SCTRSHFT))
98 			lim = (rlim64_t)RLIM64_INFINITY;
99 		else
100 			lim <<= SCTRSHFT;
101 
102 		rl64.rlim_max = rl64.rlim_cur = lim;
103 		mutex_enter(&p->p_lock);
104 		if (error = rctl_rlimit_set(rctlproc_legacy[RLIMIT_FSIZE], p,
105 		    &rl64, gp, RCTL_LOCAL_DENY | RCTL_LOCAL_SIGNAL, SIGXFSZ,
106 		    CRED())) {
107 			mutex_exit(&p->p_lock);
108 			rctl_prealloc_destroy(gp);
109 			return (set_errno(error));
110 		}
111 		mutex_exit(&p->p_lock);
112 		rctl_prealloc_destroy(gp);
113 		retval = arg;
114 		break;
115 	}
116 
117 	case UL_GMEMLIM: /* Return maximum possible break value. */
118 	{
119 		struct seg *seg;
120 		struct seg *nextseg;
121 		struct as *as = p->p_as;
122 		caddr_t brkend;
123 		caddr_t brkbase;
124 		size_t size;
125 		rlim64_t size_ctl;
126 		rlim64_t vmem_ctl;
127 
128 		/*
129 		 * Find the segment with a virtual address
130 		 * greater than the end of the current break.
131 		 */
132 		nextseg = NULL;
133 		mutex_enter(&p->p_lock);
134 		brkbase = (caddr_t)p->p_brkbase;
135 		brkend = (caddr_t)p->p_brkbase + p->p_brksize;
136 		mutex_exit(&p->p_lock);
137 
138 		/*
139 		 * Since we can't return less than the current break,
140 		 * initialize the return value to the current break
141 		 */
142 		retval = (long)brkend;
143 
144 		AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
145 		for (seg = as_findseg(as, brkend, 0); seg != NULL;
146 		    seg = AS_SEGNEXT(as, seg)) {
147 			if (seg->s_base >= brkend) {
148 				nextseg = seg;
149 				break;
150 			}
151 		}
152 
153 		mutex_enter(&p->p_lock);
154 		size_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_DATA],
155 		    p->p_rctls, p);
156 		vmem_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_VMEM],
157 		    p->p_rctls, p);
158 		mutex_exit(&p->p_lock);
159 
160 		/*
161 		 * First, calculate the maximum break value based on
162 		 * the user's RLIMIT_DATA, but also taking into account
163 		 * that this value cannot be greater than as->a_userlimit.
164 		 * We also take care to make sure that we don't overflow
165 		 * in the calculation.
166 		 */
167 		/*
168 		 * Since we are casting the RLIMIT_DATA value to a
169 		 * ulong (a 32-bit value in the 32-bit kernel) we have
170 		 * to pass this assertion.
171 		 */
172 		ASSERT32((size_t)size_ctl <= UINT32_MAX);
173 
174 		size = (size_t)size_ctl;
175 		if (as->a_userlimit - brkbase > size)
176 			retval = MAX((size_t)retval, (size_t)(brkbase + size));
177 					/* don't return less than current */
178 		else
179 			retval = (long)as->a_userlimit;
180 
181 		/*
182 		 * The max break cannot extend into the next segment
183 		 */
184 		if (nextseg != NULL)
185 			retval = MIN((uintptr_t)retval,
186 			    (uintptr_t)nextseg->s_base);
187 
188 		/*
189 		 * Handle the case where there is an limit on RLIMIT_VMEM
190 		 */
191 		if (vmem_ctl < UINT64_MAX) {
192 			/* calculate brkend based on the end of page */
193 			caddr_t brkendpg = (caddr_t)roundup((uintptr_t)brkend,
194 			    PAGESIZE);
195 			/*
196 			 * Large Files: The following assertion has to pass
197 			 * through to ensure the correctness of the cast.
198 			 */
199 			ASSERT32(vmem_ctl <= UINT32_MAX);
200 
201 			size = (size_t)(vmem_ctl & PAGEMASK);
202 
203 			if (as->a_size < size)
204 				size -= as->a_size;
205 			else
206 				size = 0;
207 			/*
208 			 * Take care to not overflow the calculation
209 			 */
210 			if (as->a_userlimit - brkendpg > size)
211 				retval = MIN((size_t)retval,
212 				    (size_t)(brkendpg + size));
213 		}
214 
215 		AS_LOCK_EXIT(as, &as->a_lock);
216 
217 		/* truncate to same boundary as sbrk */
218 
219 		switch (get_udatamodel()) {
220 		default:
221 		case DATAMODEL_ILP32:
222 			retval = retval & ~(8-1);
223 			break;
224 		case DATAMODEL_LP64:
225 			retval = retval & ~(16-1);
226 			break;
227 		}
228 		break;
229 	}
230 
231 	case UL_GDESLIM: /* Return approximate number of open files */
232 	{
233 		rlim64_t fdno_ctl;
234 
235 		mutex_enter(&curproc->p_lock);
236 		fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE],
237 		    curproc->p_rctls, curproc);
238 		ASSERT(fdno_ctl <= INT_MAX);
239 		retval = (rlim_t)fdno_ctl;
240 		mutex_exit(&curproc->p_lock);
241 		break;
242 	}
243 
244 	default:
245 		return (set_errno(EINVAL));
246 
247 	}
248 	return (retval);
249 }
250 
251 #ifdef _SYSCALL32_IMPL
252 
253 int
254 ulimit32(int cmd, int arg)
255 {
256 	return ((int)ulimit(cmd, (long)arg));
257 }
258 
259 #endif	/* _SYSCALL32_IMPL */
260 
261 #if defined(_ILP32) || defined(_SYSCALL32_IMPL)
262 
263 /*
264  * Large Files: getrlimit returns RLIM_SAVED_CUR or RLIM_SAVED_MAX when
265  * rlim_cur or rlim_max is not representable in 32-bit rlim_t. These
266  * values are just tokens which will be used in setrlimit to set the
267  * correct limits. The current limits are saved in the saved_rlimit members
268  * in user structures when the token is returned. setrlimit restores
269  * the limit values to these saved values when the token is passed.
270  * Consider the following common scenario of the apps:
271  *
272  * 		limit = getrlimit();
273  *		savedlimit = limit;
274  * 		limit = limit1;
275  *		setrlimit(limit)
276  *		// execute all processes in the new rlimit state.
277  *		setrlimit(savedlimit) // restore the old values.
278  *
279  * Most apps don't check error returns from getrlimit or setrlimit
280  * and this is why we return tokens when the correct value
281  * cannot be represented in rlim_t. For more discussion refer to
282  * the LFS API document.
283  *
284  * In the 64-bit kernel, all existing resource limits are treated in this
285  * manner.  In the 32-bit kernel, CPU time is treated equivalently to the
286  * file size limit above; the VM-related limits are not.  The macro,
287  * RLIM_SAVED(x), returns true if the resource limit should be handled in
288  * this way on the current kernel.
289  */
290 int
291 getrlimit32(int resource, struct rlimit32 *rlp)
292 {
293 	struct rlimit32 rlim32;
294 	struct rlimit64 rlim64;
295 	struct proc *p = curproc;
296 	struct user *up = PTOU(p);
297 	int savecur = 0;
298 	int savemax = 0;
299 
300 	if (resource < 0 || resource >= RLIM_NLIMITS)
301 		return (set_errno(EINVAL));
302 
303 	mutex_enter(&p->p_lock);
304 	(void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64);
305 	mutex_exit(&p->p_lock);
306 
307 	if (rlim64.rlim_max > (rlim64_t)UINT32_MAX) {
308 
309 		if (rlim64.rlim_max == RLIM64_INFINITY)
310 			rlim32.rlim_max = RLIM32_INFINITY;
311 		else {
312 			savemax = 1;
313 			rlim32.rlim_max = RLIM32_SAVED_MAX;
314 			/*CONSTCOND*/
315 			ASSERT(RLIM_SAVED(resource));
316 		}
317 
318 		if (rlim64.rlim_cur == RLIM64_INFINITY)
319 			rlim32.rlim_cur = RLIM32_INFINITY;
320 		else if (rlim64.rlim_cur == rlim64.rlim_max) {
321 			savecur = 1;
322 			rlim32.rlim_cur = RLIM32_SAVED_MAX;
323 			/*CONSTCOND*/
324 			ASSERT(RLIM_SAVED(resource));
325 		} else if (rlim64.rlim_cur > (rlim64_t)UINT32_MAX) {
326 			savecur = 1;
327 			rlim32.rlim_cur = RLIM32_SAVED_CUR;
328 			/*CONSTCOND*/
329 			ASSERT(RLIM_SAVED(resource));
330 		} else
331 			rlim32.rlim_cur = rlim64.rlim_cur;
332 
333 		/*
334 		 * save the current limits in user structure.
335 		 */
336 		/*CONSTCOND*/
337 		if (RLIM_SAVED(resource)) {
338 			mutex_enter(&p->p_lock);
339 			if (savemax)
340 				up->u_saved_rlimit[resource].rlim_max =
341 				    rlim64.rlim_max;
342 			if (savecur)
343 				up->u_saved_rlimit[resource].rlim_cur =
344 				    rlim64.rlim_cur;
345 			mutex_exit(&p->p_lock);
346 		}
347 	} else {
348 		ASSERT(rlim64.rlim_cur <= (rlim64_t)UINT32_MAX);
349 		rlim32.rlim_max = rlim64.rlim_max;
350 		rlim32.rlim_cur = rlim64.rlim_cur;
351 	}
352 
353 	if (copyout(&rlim32, rlp, sizeof (rlim32)))
354 		return (set_errno(EFAULT));
355 
356 	return (0);
357 }
358 
359 /*
360  * See comments above getrlimit32(). When the tokens are passed in the
361  * rlimit structure the values are considered equal to the values
362  * stored in saved_rlimit members of user structure.
363  * When the user passes RLIM_INFINITY to set the resource limit to
364  * unlimited internally understand this value as RLIM64_INFINITY and
365  * let rlimit() do the job.
366  */
367 int
368 setrlimit32(int resource, struct rlimit32 *rlp)
369 {
370 	struct rlimit32 rlim32;
371 	struct rlimit64 rlim64;
372 	struct rlimit64 saved_rlim;
373 	int	error;
374 	struct proc *p = ttoproc(curthread);
375 	struct user *up = PTOU(p);
376 	rctl_alloc_gp_t *gp;
377 
378 	if (resource < 0 || resource >= RLIM_NLIMITS)
379 		return (set_errno(EINVAL));
380 	if (copyin(rlp, &rlim32, sizeof (rlim32)))
381 		return (set_errno(EFAULT));
382 
383 	gp = rctl_rlimit_set_prealloc(1);
384 
385 	/*
386 	 * Disallow resource limit tunnelling
387 	 */
388 	/*CONSTCOND*/
389 	if (RLIM_SAVED(resource)) {
390 		mutex_enter(&p->p_lock);
391 		saved_rlim = up->u_saved_rlimit[resource];
392 		mutex_exit(&p->p_lock);
393 	} else {
394 		saved_rlim.rlim_max = (rlim64_t)rlim32.rlim_max;
395 		saved_rlim.rlim_cur = (rlim64_t)rlim32.rlim_cur;
396 	}
397 
398 	switch (rlim32.rlim_cur) {
399 	case RLIM32_INFINITY:
400 		rlim64.rlim_cur = RLIM64_INFINITY;
401 		break;
402 	case RLIM32_SAVED_CUR:
403 		rlim64.rlim_cur = saved_rlim.rlim_cur;
404 		break;
405 	case RLIM32_SAVED_MAX:
406 		rlim64.rlim_cur = saved_rlim.rlim_max;
407 		break;
408 	default:
409 		rlim64.rlim_cur = (rlim64_t)rlim32.rlim_cur;
410 		break;
411 	}
412 
413 	switch (rlim32.rlim_max) {
414 	case RLIM32_INFINITY:
415 		rlim64.rlim_max = RLIM64_INFINITY;
416 		break;
417 	case RLIM32_SAVED_MAX:
418 		rlim64.rlim_max = saved_rlim.rlim_max;
419 		break;
420 	case RLIM32_SAVED_CUR:
421 		rlim64.rlim_max = saved_rlim.rlim_cur;
422 		break;
423 	default:
424 		rlim64.rlim_max = (rlim64_t)rlim32.rlim_max;
425 		break;
426 	}
427 
428 	mutex_enter(&p->p_lock);
429 	if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp,
430 	    rctlproc_flags[resource], rctlproc_signals[resource], CRED())) {
431 		mutex_exit(&p->p_lock);
432 		rctl_prealloc_destroy(gp);
433 		return (set_errno(error));
434 	}
435 	mutex_exit(&p->p_lock);
436 	rctl_prealloc_destroy(gp);
437 
438 	return (0);
439 }
440 
441 #endif	/* _ILP32 && _SYSCALL32_IMPL */
442 
443 int
444 getrlimit64(int resource, struct rlimit64 *rlp)
445 {
446 	struct rlimit64 rlim64;
447 	struct proc *p = ttoproc(curthread);
448 
449 	if (resource < 0 || resource >= RLIM_NLIMITS)
450 		return (set_errno(EINVAL));
451 
452 	mutex_enter(&p->p_lock);
453 	(void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64);
454 	mutex_exit(&p->p_lock);
455 
456 	if (copyout(&rlim64, rlp, sizeof (rlim64)))
457 		return (set_errno(EFAULT));
458 	return (0);
459 }
460 
461 int
462 setrlimit64(int resource, struct rlimit64 *rlp)
463 {
464 	struct rlimit64 rlim64;
465 	struct proc *p = ttoproc(curthread);
466 	int	error;
467 	rctl_alloc_gp_t *gp;
468 
469 	if (resource < 0 || resource >= RLIM_NLIMITS)
470 		return (set_errno(EINVAL));
471 	if (copyin(rlp, &rlim64, sizeof (rlim64)))
472 		return (set_errno(EFAULT));
473 
474 	gp = rctl_rlimit_set_prealloc(1);
475 
476 	mutex_enter(&p->p_lock);
477 	if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp,
478 	    rctlproc_flags[resource], rctlproc_signals[resource], CRED())) {
479 		mutex_exit(&p->p_lock);
480 		rctl_prealloc_destroy(gp);
481 		return (set_errno(error));
482 	}
483 	mutex_exit(&p->p_lock);
484 	rctl_prealloc_destroy(gp);
485 	return (0);
486 
487 }
488