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
47rctl_hndl_t rctlproc_legacy[RLIM_NLIMITS];
48uint_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};
57int rctlproc_signals[RLIM_NLIMITS] = {
58	SIGXCPU,				/* RLIMIT_CPU	*/
59	SIGXFSZ,				/* RLIMIT_FSIZE	*/
60	0, 0, 0, 0, 0				/* remainder do not signal */
61};
62
63rctl_hndl_t rc_process_msgmnb;
64rctl_hndl_t rc_process_msgtql;
65rctl_hndl_t rc_process_semmsl;
66rctl_hndl_t rc_process_semopm;
67rctl_hndl_t rc_process_portev;
68
69/*
70 * process.max-cpu-time / RLIMIT_CPU
71 */
72/*ARGSUSED*/
73static int
74proc_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
80static 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 */
90static int
91proc_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
105static 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 */
119static int
120proc_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
158static 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 */
168static int
169proc_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
182static 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 */
192static int
193proc_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
206static 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 */
220void
221rctlproc_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 */
290void
291rctlproc_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