xref: /illumos-gate/usr/src/uts/common/os/rctl_proc.c (revision 3ce5372277f4657ad0e52d36c979527c4ca22de2)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/cmn_err.h>
28 #include <sys/sysmacros.h>
29 #include <sys/proc.h>
30 #include <sys/rctl.h>
31 #include <sys/rctl_impl.h>
32 #include <sys/port_kernel.h>
33 #include <sys/signal.h>
34 #include <sys/var.h>
35 
36 #include <sys/vmparam.h>
37 #include <sys/machparam.h>
38 
39 /*
40  * Process-based resource controls
41  *   The structure of the kernel leaves us no particular place where the process
42  *   abstraction can be declared--it is intertwined with the growth of the Unix
43  *   kernel.  Accordingly, we place all of the resource control logic associated
44  *   with processes, both existing and future, in this file.
45  */
46 
47 rctl_hndl_t rctlproc_legacy[RLIM_NLIMITS];
48 uint_t rctlproc_flags[RLIM_NLIMITS] = {
49 	RCTL_LOCAL_SIGNAL,			/* RLIMIT_CPU	*/
50 	RCTL_LOCAL_DENY | RCTL_LOCAL_SIGNAL,	/* RLIMIT_FSIZE */
51 	RCTL_LOCAL_DENY,				/* RLIMIT_DATA	*/
52 	RCTL_LOCAL_DENY,				/* RLIMIT_STACK */
53 	RCTL_LOCAL_DENY,				/* RLIMIT_CORE	*/
54 	RCTL_LOCAL_DENY,				/* RLIMIT_NOFILE */
55 	RCTL_LOCAL_DENY				/* RLIMIT_VMEM	*/
56 };
57 int rctlproc_signals[RLIM_NLIMITS] = {
58 	SIGXCPU,				/* RLIMIT_CPU	*/
59 	SIGXFSZ,				/* RLIMIT_FSIZE	*/
60 	0, 0, 0, 0, 0				/* remainder do not signal */
61 };
62 
63 rctl_hndl_t rc_process_msgmnb;
64 rctl_hndl_t rc_process_msgtql;
65 rctl_hndl_t rc_process_semmsl;
66 rctl_hndl_t rc_process_semopm;
67 rctl_hndl_t rc_process_portev;
68 rctl_hndl_t rc_process_sigqueue;
69 
70 /*
71  * process.max-cpu-time / RLIMIT_CPU
72  */
73 /*ARGSUSED*/
74 static int
75 proc_cpu_time_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
76     rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
77 {
78 	return (inc >= rval->rcv_value);
79 }
80 
81 static rctl_ops_t proc_cpu_time_ops = {
82 	rcop_no_action,
83 	rcop_no_usage,
84 	rcop_no_set,
85 	proc_cpu_time_test
86 };
87 
88 /*
89  * process.max-file-size / RLIMIT_FSIZE
90  */
91 static int
92 proc_filesize_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
93     rctl_qty_t nv)
94 {
95 	if (p->p_model == DATAMODEL_NATIVE)
96 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_native);
97 	else
98 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_ilp32);
99 
100 	ASSERT(e->rcep_t == RCENTITY_PROCESS);
101 	e->rcep_p.proc->p_fsz_ctl = nv;
102 
103 	return (0);
104 }
105 
106 static rctl_ops_t proc_filesize_ops = {
107 	rcop_no_action,
108 	rcop_no_usage,
109 	proc_filesize_set,
110 	rcop_no_test
111 };
112 
113 /*
114  * process.max-data / RLIMIT_DATA
115  */
116 
117 /*
118  * process.max-stack-size / RLIMIT_STACK
119  */
120 static int
121 proc_stack_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
122     rctl_qty_t nv)
123 {
124 	klwp_t *lwp = ttolwp(curthread);
125 
126 	if (p->p_model == DATAMODEL_NATIVE)
127 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_native);
128 	else
129 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_ilp32);
130 
131 	/*
132 	 * In the process of changing the rlimit, this function actually
133 	 * gets called a number of times. We only want to save the current
134 	 * rlimit the first time we come through here. In post_syscall(),
135 	 * we copyin() the lwp's ustack, and compare it to the rlimit we
136 	 * save here; if the two match, we adjust the ustack to reflect
137 	 * the new stack bounds.
138 	 *
139 	 * We check to make sure that we're changing the rlimit of our
140 	 * own process rather than on behalf of some other process. The
141 	 * notion of changing this resource limit on behalf of another
142 	 * process is problematic at best, and changing the amount of stack
143 	 * space a process is allowed to consume is a rather antiquated
144 	 * notion that has limited applicability in our multithreaded
145 	 * process model.
146 	 */
147 	ASSERT(e->rcep_t == RCENTITY_PROCESS);
148 	if (lwp != NULL && lwp->lwp_procp == e->rcep_p.proc &&
149 	    lwp->lwp_ustack && lwp->lwp_old_stk_ctl == 0) {
150 		lwp->lwp_old_stk_ctl = (size_t)e->rcep_p.proc->p_stk_ctl;
151 		curthread->t_post_sys = 1;
152 	}
153 
154 	e->rcep_p.proc->p_stk_ctl = nv;
155 
156 	return (0);
157 }
158 
159 static rctl_ops_t proc_stack_ops = {
160 	rcop_no_action,
161 	rcop_no_usage,
162 	proc_stack_set,
163 	rcop_no_test
164 };
165 
166 /*
167  * process.max-file-descriptors / RLIMIT_NOFILE
168  */
169 static int
170 proc_nofile_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, rctl_qty_t nv)
171 {
172 	ASSERT(e->rcep_t == RCENTITY_PROCESS);
173 	if (p->p_model == DATAMODEL_NATIVE)
174 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_native);
175 	else
176 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_ilp32);
177 
178 	e->rcep_p.proc->p_fno_ctl = nv;
179 
180 	return (0);
181 }
182 
183 static rctl_ops_t proc_nofile_ops = {
184 	rcop_no_action,
185 	rcop_no_usage,
186 	proc_nofile_set,
187 	rcop_absolute_test
188 };
189 
190 /*
191  * process.max-address-space / RLIMIT_VMEM
192  */
193 static int
194 proc_vmem_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, rctl_qty_t nv)
195 {
196 	ASSERT(e->rcep_t == RCENTITY_PROCESS);
197 	if (p->p_model == DATAMODEL_ILP32)
198 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_ilp32);
199 	else
200 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_native);
201 
202 	e->rcep_p.proc->p_vmem_ctl = nv;
203 
204 	return (0);
205 }
206 
207 static rctl_ops_t proc_vmem_ops = {
208 	rcop_no_action,
209 	rcop_no_usage,
210 	proc_vmem_set,
211 	rcop_no_test
212 };
213 
214 /*
215  * void rctlproc_default_init()
216  *
217  * Overview
218  *   Establish default basic and privileged control values on the init process.
219  *   These correspond to the soft and hard limits, respectively.
220  */
221 void
222 rctlproc_default_init(struct proc *initp, rctl_alloc_gp_t *gp)
223 {
224 	struct rlimit64 rlp64;
225 
226 	/*
227 	 * RLIMIT_CPU: deny never, sigtoproc(pp, NULL, SIGXCPU).
228 	 */
229 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
230 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_CPU], initp, &rlp64, gp,
231 	    RCTL_LOCAL_SIGNAL, SIGXCPU, kcred);
232 
233 	/*
234 	 * RLIMIT_FSIZE: deny always, sigtoproc(pp, NULL, SIGXFSZ).
235 	 */
236 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
237 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_FSIZE], initp, &rlp64, gp,
238 	    RCTL_LOCAL_SIGNAL | RCTL_LOCAL_DENY, SIGXFSZ, kcred);
239 
240 	/*
241 	 * RLIMIT_DATA: deny always, no default action.
242 	 */
243 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
244 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_DATA], initp, &rlp64, gp,
245 	    RCTL_LOCAL_DENY, 0, kcred);
246 
247 	/*
248 	 * RLIMIT_STACK: deny always, no default action.
249 	 */
250 #ifdef __sparc
251 	rlp64.rlim_cur = DFLSSIZ;
252 	rlp64.rlim_max = LONG_MAX;
253 #else
254 	rlp64.rlim_cur = DFLSSIZ;
255 	rlp64.rlim_max = MAXSSIZ;
256 #endif
257 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_STACK], initp, &rlp64, gp,
258 	    RCTL_LOCAL_DENY, 0, kcred);
259 
260 	/*
261 	 * RLIMIT_CORE: deny always, no default action.
262 	 */
263 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
264 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_CORE], initp, &rlp64, gp,
265 	    RCTL_LOCAL_DENY, 0, kcred);
266 
267 	/*
268 	 * RLIMIT_NOFILE: deny always, no action.
269 	 */
270 	rlp64.rlim_cur = rlim_fd_cur;
271 	rlp64.rlim_max = rlim_fd_max;
272 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_NOFILE], initp, &rlp64,
273 	    gp, RCTL_LOCAL_DENY, 0, kcred);
274 
275 	/*
276 	 * RLIMIT_VMEM
277 	 */
278 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
279 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_VMEM], initp, &rlp64, gp,
280 	    RCTL_LOCAL_DENY, 0, kcred);
281 }
282 
283 /*
284  * void rctlproc_init()
285  *
286  * Overview
287  *   Register the various resource controls associated with process entities.
288  *   The historical rlim_infinity_map and rlim_infinity32_map are now encoded
289  *   here as the native and ILP32 infinite values for each resource control.
290  */
291 void
292 rctlproc_init(void)
293 {
294 	rctl_set_t *set;
295 	rctl_alloc_gp_t *gp;
296 	rctl_entity_p_t e;
297 
298 	rctlproc_legacy[RLIMIT_CPU] = rctl_register("process.max-cpu-time",
299 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_NEVER |
300 	    RCTL_GLOBAL_CPU_TIME | RCTL_GLOBAL_INFINITE | RCTL_GLOBAL_SECONDS,
301 	    UINT64_MAX, UINT64_MAX, &proc_cpu_time_ops);
302 	rctlproc_legacy[RLIMIT_FSIZE] = rctl_register("process.max-file-size",
303 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
304 	    RCTL_GLOBAL_FILE_SIZE | RCTL_GLOBAL_BYTES,
305 	    MAXOFFSET_T, MAXOFFSET_T, &proc_filesize_ops);
306 	rctlproc_legacy[RLIMIT_DATA] = rctl_register("process.max-data-size",
307 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
308 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
309 	    ULONG_MAX, UINT32_MAX, &rctl_default_ops);
310 #ifdef _LP64
311 #ifdef __sparc
312 	rctlproc_legacy[RLIMIT_STACK] = rctl_register("process.max-stack-size",
313 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
314 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
315 	    LONG_MAX, INT32_MAX, &proc_stack_ops);
316 #else	/* __sparc */
317 	rctlproc_legacy[RLIMIT_STACK] = rctl_register("process.max-stack-size",
318 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
319 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
320 	    MAXSSIZ, USRSTACK32 - PAGESIZE, &proc_stack_ops);
321 #endif	/* __sparc */
322 #else 	/* _LP64 */
323 	rctlproc_legacy[RLIMIT_STACK] = rctl_register("process.max-stack-size",
324 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
325 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
326 	    USRSTACK - PAGESIZE, USRSTACK - PAGESIZE, &proc_stack_ops);
327 #endif
328 	rctlproc_legacy[RLIMIT_CORE] = rctl_register("process.max-core-size",
329 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
330 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
331 	    MIN(MAXOFFSET_T, ULONG_MAX), UINT32_MAX, &rctl_default_ops);
332 	rctlproc_legacy[RLIMIT_NOFILE] = rctl_register(
333 	    "process.max-file-descriptor", RCENTITY_PROCESS,
334 	    RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
335 	    RCTL_GLOBAL_COUNT, INT32_MAX, INT32_MAX, &proc_nofile_ops);
336 	rctlproc_legacy[RLIMIT_VMEM] =
337 	    rctl_register("process.max-address-space", RCENTITY_PROCESS,
338 	    RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
339 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
340 	    ULONG_MAX, UINT32_MAX, &proc_vmem_ops);
341 
342 	rc_process_semmsl = rctl_register("process.max-sem-nsems",
343 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
344 	    SHRT_MAX, SHRT_MAX, &rctl_absolute_ops);
345 	rctl_add_legacy_limit("process.max-sem-nsems", "semsys",
346 	    "seminfo_semmsl", 512, SHRT_MAX);
347 
348 	rc_process_semopm = rctl_register("process.max-sem-ops",
349 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
350 	    INT_MAX, INT_MAX, &rctl_absolute_ops);
351 	rctl_add_legacy_limit("process.max-sem-ops", "semsys",
352 	    "seminfo_semopm", 512, INT_MAX);
353 
354 	rc_process_msgmnb = rctl_register("process.max-msg-qbytes",
355 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_BYTES,
356 	    ULONG_MAX, ULONG_MAX, &rctl_absolute_ops);
357 	rctl_add_legacy_limit("process.max-msg-qbytes", "msgsys",
358 	    "msginfo_msgmnb", 65536, ULONG_MAX);
359 
360 	rc_process_msgtql = rctl_register("process.max-msg-messages",
361 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
362 	    UINT_MAX, UINT_MAX, &rctl_absolute_ops);
363 	rctl_add_legacy_limit("process.max-msg-messages", "msgsys",
364 	    "msginfo_msgtql", 8192, UINT_MAX);
365 
366 	rc_process_portev = rctl_register("process.max-port-events",
367 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
368 	    PORT_MAX_EVENTS, PORT_MAX_EVENTS, &rctl_absolute_ops);
369 	rctl_add_default_limit("process.max-port-events", PORT_DEFAULT_EVENTS,
370 	    RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
371 
372 	/*
373 	 * We set the upper limit to the maximum number of user processes to
374 	 * make it theoretically possible to deliver all SIGCHILD signals on
375 	 * child termination, but at least to 8k.
376 	 */
377 	rc_process_sigqueue = rctl_register("process.max-sigqueue-size",
378 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
379 	    RCTL_GLOBAL_COUNT, MAX(v.v_maxup, 8192), MAX(v.v_maxup, 8192),
380 	    &rctl_absolute_ops);
381 	rctl_add_default_limit("process.max-sigqueue-size",
382 	    _SIGQUEUE_SIZE_BASIC, RCPRIV_BASIC, RCTL_LOCAL_DENY);
383 	rctl_add_default_limit("process.max-sigqueue-size",
384 	    _SIGQUEUE_SIZE_PRIVILEGED, RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
385 
386 	/*
387 	 * Place minimal set of controls on "sched" process for inheritance by
388 	 * processes created via newproc().
389 	 */
390 	set = rctl_set_create();
391 	gp = rctl_set_init_prealloc(RCENTITY_PROCESS);
392 	mutex_enter(&curproc->p_lock);
393 	e.rcep_p.proc = curproc;
394 	e.rcep_t = RCENTITY_PROCESS;
395 	curproc->p_rctls = rctl_set_init(RCENTITY_PROCESS, curproc, &e,
396 	    set, gp);
397 	mutex_exit(&curproc->p_lock);
398 	rctl_prealloc_destroy(gp);
399 }
400