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 rc_process_sigqueue = rctl_register("process.max-sigqueue-size", 373 RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS | 374 RCTL_GLOBAL_COUNT, v.v_maxup, v.v_maxup, 375 &rctl_absolute_ops); 376 rctl_add_default_limit("process.max-sigqueue-size", 377 _SIGQUEUE_SIZE_BASIC, RCPRIV_BASIC, RCTL_LOCAL_DENY); 378 rctl_add_default_limit("process.max-sigqueue-size", 379 _SIGQUEUE_SIZE_PRIVILEGED, RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY); 380 381 /* 382 * Place minimal set of controls on "sched" process for inheritance by 383 * processes created via newproc(). 384 */ 385 set = rctl_set_create(); 386 gp = rctl_set_init_prealloc(RCENTITY_PROCESS); 387 mutex_enter(&curproc->p_lock); 388 e.rcep_p.proc = curproc; 389 e.rcep_t = RCENTITY_PROCESS; 390 curproc->p_rctls = rctl_set_init(RCENTITY_PROCESS, curproc, &e, 391 set, gp); 392 mutex_exit(&curproc->p_lock); 393 rctl_prealloc_destroy(gp); 394 }