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