xref: /illumos-gate/usr/src/uts/common/os/rctl_proc.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 (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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/cmn_err.h>
30 #include <sys/sysmacros.h>
31 #include <sys/proc.h>
32 #include <sys/rctl.h>
33 #include <sys/rctl_impl.h>
34 #include <sys/port_kernel.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 
69 /*
70  * process.max-cpu-time / RLIMIT_CPU
71  */
72 /*ARGSUSED*/
73 static int
74 proc_cpu_time_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
75     rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
76 {
77 	return (inc >= rval->rcv_value);
78 }
79 
80 static rctl_ops_t proc_cpu_time_ops = {
81 	rcop_no_action,
82 	rcop_no_usage,
83 	rcop_no_set,
84 	proc_cpu_time_test
85 };
86 
87 /*
88  * process.max-file-size / RLIMIT_FSIZE
89  */
90 static int
91 proc_filesize_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
92     rctl_qty_t nv)
93 {
94 	if (p->p_model == DATAMODEL_NATIVE)
95 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_native);
96 	else
97 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_ilp32);
98 
99 	ASSERT(e->rcep_t == RCENTITY_PROCESS);
100 	e->rcep_p.proc->p_fsz_ctl = nv;
101 
102 	return (0);
103 }
104 
105 static rctl_ops_t proc_filesize_ops = {
106 	rcop_no_action,
107 	rcop_no_usage,
108 	proc_filesize_set,
109 	rcop_no_test
110 };
111 
112 /*
113  * process.max-data / RLIMIT_DATA
114  */
115 
116 /*
117  * process.max-stack-size / RLIMIT_STACK
118  */
119 static int
120 proc_stack_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
121     rctl_qty_t nv)
122 {
123 	klwp_t *lwp = ttolwp(curthread);
124 
125 	if (p->p_model == DATAMODEL_NATIVE)
126 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_native);
127 	else
128 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_ilp32);
129 
130 	/*
131 	 * In the process of changing the rlimit, this function actually
132 	 * gets called a number of times. We only want to save the current
133 	 * rlimit the first time we come through here. In post_syscall(),
134 	 * we copyin() the lwp's ustack, and compare it to the rlimit we
135 	 * save here; if the two match, we adjust the ustack to reflect
136 	 * the new stack bounds.
137 	 *
138 	 * We check to make sure that we're changing the rlimit of our
139 	 * own process rather than on behalf of some other process. The
140 	 * notion of changing this resource limit on behalf of another
141 	 * process is problematic at best, and changing the amount of stack
142 	 * space a process is allowed to consume is a rather antiquated
143 	 * notion that has limited applicability in our multithreaded
144 	 * process model.
145 	 */
146 	ASSERT(e->rcep_t == RCENTITY_PROCESS);
147 	if (lwp != NULL && lwp->lwp_procp == e->rcep_p.proc &&
148 	    lwp->lwp_ustack && lwp->lwp_old_stk_ctl == 0) {
149 		lwp->lwp_old_stk_ctl = (size_t)e->rcep_p.proc->p_stk_ctl;
150 		curthread->t_post_sys = 1;
151 	}
152 
153 	e->rcep_p.proc->p_stk_ctl = nv;
154 
155 	return (0);
156 }
157 
158 static rctl_ops_t proc_stack_ops = {
159 	rcop_no_action,
160 	rcop_no_usage,
161 	proc_stack_set,
162 	rcop_no_test
163 };
164 
165 /*
166  * process.max-file-descriptors / RLIMIT_NOFILE
167  */
168 static int
169 proc_nofile_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, rctl_qty_t nv)
170 {
171 	ASSERT(e->rcep_t == RCENTITY_PROCESS);
172 	if (p->p_model == DATAMODEL_NATIVE)
173 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_native);
174 	else
175 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_ilp32);
176 
177 	e->rcep_p.proc->p_fno_ctl = nv;
178 
179 	return (0);
180 }
181 
182 static rctl_ops_t proc_nofile_ops = {
183 	rcop_no_action,
184 	rcop_no_usage,
185 	proc_nofile_set,
186 	rcop_absolute_test
187 };
188 
189 /*
190  * process.max-address-space / RLIMIT_VMEM
191  */
192 static int
193 proc_vmem_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, rctl_qty_t nv)
194 {
195 	ASSERT(e->rcep_t == RCENTITY_PROCESS);
196 	if (p->p_model == DATAMODEL_ILP32)
197 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_ilp32);
198 	else
199 		nv = MIN(nv, rctl->rc_dict_entry->rcd_max_native);
200 
201 	e->rcep_p.proc->p_vmem_ctl = nv;
202 
203 	return (0);
204 }
205 
206 static rctl_ops_t proc_vmem_ops = {
207 	rcop_no_action,
208 	rcop_no_usage,
209 	proc_vmem_set,
210 	rcop_no_test
211 };
212 
213 /*
214  * void rctlproc_default_init()
215  *
216  * Overview
217  *   Establish default basic and privileged control values on the init process.
218  *   These correspond to the soft and hard limits, respectively.
219  */
220 void
221 rctlproc_default_init(struct proc *initp, rctl_alloc_gp_t *gp)
222 {
223 	struct rlimit64 rlp64;
224 
225 	/*
226 	 * RLIMIT_CPU: deny never, sigtoproc(pp, NULL, SIGXCPU).
227 	 */
228 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
229 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_CPU], initp, &rlp64, gp,
230 	    RCTL_LOCAL_SIGNAL, SIGXCPU, kcred);
231 
232 	/*
233 	 * RLIMIT_FSIZE: deny always, sigtoproc(pp, NULL, SIGXFSZ).
234 	 */
235 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
236 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_FSIZE], initp, &rlp64, gp,
237 	    RCTL_LOCAL_SIGNAL | RCTL_LOCAL_DENY, SIGXFSZ, kcred);
238 
239 	/*
240 	 * RLIMIT_DATA: deny always, no default action.
241 	 */
242 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
243 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_DATA], initp, &rlp64, gp,
244 	    RCTL_LOCAL_DENY, 0, kcred);
245 
246 	/*
247 	 * RLIMIT_STACK: deny always, no default action.
248 	 */
249 #ifdef __sparc
250 	rlp64.rlim_cur = DFLSSIZ;
251 	rlp64.rlim_max = LONG_MAX;
252 #else
253 	rlp64.rlim_cur = DFLSSIZ;
254 	rlp64.rlim_max = MAXSSIZ;
255 #endif
256 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_STACK], initp, &rlp64, gp,
257 	    RCTL_LOCAL_DENY, 0, kcred);
258 
259 	/*
260 	 * RLIMIT_CORE: deny always, no default action.
261 	 */
262 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
263 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_CORE], initp, &rlp64, gp,
264 	    RCTL_LOCAL_DENY, 0, kcred);
265 
266 	/*
267 	 * RLIMIT_NOFILE: deny always, no action.
268 	 */
269 	rlp64.rlim_cur = rlim_fd_cur;
270 	rlp64.rlim_max = rlim_fd_max;
271 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_NOFILE], initp, &rlp64,
272 	    gp, RCTL_LOCAL_DENY, 0, kcred);
273 
274 	/*
275 	 * RLIMIT_VMEM
276 	 */
277 	rlp64.rlim_cur = rlp64.rlim_max = RLIM64_INFINITY;
278 	(void) rctl_rlimit_set(rctlproc_legacy[RLIMIT_VMEM], initp, &rlp64, gp,
279 	    RCTL_LOCAL_DENY, 0, kcred);
280 }
281 
282 /*
283  * void rctlproc_init()
284  *
285  * Overview
286  *   Register the various resource controls associated with process entities.
287  *   The historical rlim_infinity_map and rlim_infinity32_map are now encoded
288  *   here as the native and ILP32 infinite values for each resource control.
289  */
290 void
291 rctlproc_init()
292 {
293 	rctl_set_t *set;
294 	rctl_alloc_gp_t *gp;
295 	rctl_entity_p_t e;
296 
297 	rctlproc_legacy[RLIMIT_CPU] = rctl_register("process.max-cpu-time",
298 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_NEVER |
299 	    RCTL_GLOBAL_CPU_TIME | RCTL_GLOBAL_INFINITE | RCTL_GLOBAL_SECONDS,
300 	    UINT64_MAX, UINT64_MAX, &proc_cpu_time_ops);
301 	rctlproc_legacy[RLIMIT_FSIZE] = rctl_register("process.max-file-size",
302 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
303 	    RCTL_GLOBAL_FILE_SIZE | RCTL_GLOBAL_BYTES,
304 	    MAXOFFSET_T, MAXOFFSET_T, &proc_filesize_ops);
305 	rctlproc_legacy[RLIMIT_DATA] = rctl_register("process.max-data-size",
306 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
307 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
308 	    ULONG_MAX, UINT32_MAX, &rctl_default_ops);
309 #ifdef _LP64
310 #ifdef __sparc
311 	rctlproc_legacy[RLIMIT_STACK] = rctl_register("process.max-stack-size",
312 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
313 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
314 	    LONG_MAX, INT32_MAX, &proc_stack_ops);
315 #else	/* __sparc */
316 	rctlproc_legacy[RLIMIT_STACK] = rctl_register("process.max-stack-size",
317 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
318 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
319 	    MAXSSIZ, USRSTACK32 - PAGESIZE, &proc_stack_ops);
320 #endif	/* __sparc */
321 #else 	/* _LP64 */
322 	rctlproc_legacy[RLIMIT_STACK] = rctl_register("process.max-stack-size",
323 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
324 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
325 	    USRSTACK - PAGESIZE, USRSTACK - PAGESIZE, &proc_stack_ops);
326 #endif
327 	rctlproc_legacy[RLIMIT_CORE] = rctl_register("process.max-core-size",
328 	    RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
329 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
330 	    MIN(MAXOFFSET_T, ULONG_MAX), UINT32_MAX, &rctl_default_ops);
331 	rctlproc_legacy[RLIMIT_NOFILE] = rctl_register(
332 	    "process.max-file-descriptor", RCENTITY_PROCESS,
333 	    RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
334 	    RCTL_GLOBAL_COUNT, INT32_MAX, INT32_MAX, &proc_nofile_ops);
335 	rctlproc_legacy[RLIMIT_VMEM] =
336 	    rctl_register("process.max-address-space", RCENTITY_PROCESS,
337 	    RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
338 	    RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
339 	    ULONG_MAX, UINT32_MAX, &proc_vmem_ops);
340 
341 	rc_process_semmsl = rctl_register("process.max-sem-nsems",
342 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
343 	    SHRT_MAX, SHRT_MAX, &rctl_absolute_ops);
344 	rctl_add_legacy_limit("process.max-sem-nsems", "semsys",
345 	    "seminfo_semmsl", 512, SHRT_MAX);
346 
347 	rc_process_semopm = rctl_register("process.max-sem-ops",
348 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
349 	    INT_MAX, INT_MAX, &rctl_absolute_ops);
350 	rctl_add_legacy_limit("process.max-sem-ops", "semsys",
351 	    "seminfo_semopm", 512, INT_MAX);
352 
353 	rc_process_msgmnb = rctl_register("process.max-msg-qbytes",
354 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_BYTES,
355 	    ULONG_MAX, ULONG_MAX, &rctl_absolute_ops);
356 	rctl_add_legacy_limit("process.max-msg-qbytes", "msgsys",
357 	    "msginfo_msgmnb", 65536, ULONG_MAX);
358 
359 	rc_process_msgtql = rctl_register("process.max-msg-messages",
360 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
361 	    UINT_MAX, UINT_MAX, &rctl_absolute_ops);
362 	rctl_add_legacy_limit("process.max-msg-messages", "msgsys",
363 	    "msginfo_msgtql", 8192, UINT_MAX);
364 
365 	rc_process_portev = rctl_register("process.max-port-events",
366 	    RCENTITY_PROCESS, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT,
367 	    PORT_MAX_EVENTS, PORT_MAX_EVENTS, &rctl_absolute_ops);
368 	rctl_add_default_limit("process.max-port-events", PORT_DEFAULT_EVENTS,
369 	    RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
370 
371 	/*
372 	 * Place minimal set of controls on "sched" process for inheritance by
373 	 * processes created via newproc().
374 	 */
375 	set = rctl_set_create();
376 	gp = rctl_set_init_prealloc(RCENTITY_PROCESS);
377 	mutex_enter(&curproc->p_lock);
378 	e.rcep_p.proc = curproc;
379 	e.rcep_t = RCENTITY_PROCESS;
380 	curproc->p_rctls = rctl_set_init(RCENTITY_PROCESS, curproc, &e,
381 	    set, gp);
382 	mutex_exit(&curproc->p_lock);
383 	rctl_prealloc_destroy(gp);
384 }
385