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 35 #include <sys/vmparam.h> 36 #include <sys/machparam.h> 37 38 /* 39 * Process-based resource controls 40 * The structure of the kernel leaves us no particular place where the process 41 * abstraction can be declared--it is intertwined with the growth of the Unix 42 * kernel. Accordingly, we place all of the resource control logic associated 43 * with processes, both existing and future, in this file. 44 */ 45 46 rctl_hndl_t rctlproc_legacy[RLIM_NLIMITS]; 47 uint_t rctlproc_flags[RLIM_NLIMITS] = { 48 RCTL_LOCAL_SIGNAL, /* RLIMIT_CPU */ 49 RCTL_LOCAL_DENY | RCTL_LOCAL_SIGNAL, /* RLIMIT_FSIZE */ 50 RCTL_LOCAL_DENY, /* RLIMIT_DATA */ 51 RCTL_LOCAL_DENY, /* RLIMIT_STACK */ 52 RCTL_LOCAL_DENY, /* RLIMIT_CORE */ 53 RCTL_LOCAL_DENY, /* RLIMIT_NOFILE */ 54 RCTL_LOCAL_DENY /* RLIMIT_VMEM */ 55 }; 56 int rctlproc_signals[RLIM_NLIMITS] = { 57 SIGXCPU, /* RLIMIT_CPU */ 58 SIGXFSZ, /* RLIMIT_FSIZE */ 59 0, 0, 0, 0, 0 /* remainder do not signal */ 60 }; 61 62 rctl_hndl_t rc_process_msgmnb; 63 rctl_hndl_t rc_process_msgtql; 64 rctl_hndl_t rc_process_semmsl; 65 rctl_hndl_t rc_process_semopm; 66 rctl_hndl_t rc_process_portev; 67 rctl_hndl_t rc_process_sigqueue; 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(void) 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 rc_process_sigqueue = rctl_register("process.max-sigqueue-size", 372 RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS | 373 RCTL_GLOBAL_COUNT, _SIGQUEUE_SIZE_MAX, _SIGQUEUE_SIZE_MAX, 374 &rctl_absolute_ops); 375 rctl_add_default_limit("process.max-sigqueue-size", 376 _SIGQUEUE_SIZE_BASIC, RCPRIV_BASIC, RCTL_LOCAL_DENY); 377 rctl_add_default_limit("process.max-sigqueue-size", 378 _SIGQUEUE_SIZE_PRIVILEGED, RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY); 379 380 /* 381 * Place minimal set of controls on "sched" process for inheritance by 382 * processes created via newproc(). 383 */ 384 set = rctl_set_create(); 385 gp = rctl_set_init_prealloc(RCENTITY_PROCESS); 386 mutex_enter(&curproc->p_lock); 387 e.rcep_p.proc = curproc; 388 e.rcep_t = RCENTITY_PROCESS; 389 curproc->p_rctls = rctl_set_init(RCENTITY_PROCESS, curproc, &e, 390 set, gp); 391 mutex_exit(&curproc->p_lock); 392 rctl_prealloc_destroy(gp); 393 }