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/proc.h> 29 #include <sys/systm.h> 30 #include <sys/param.h> 31 #include <sys/atomic.h> 32 #include <sys/kmem.h> 33 #include <sys/sysmacros.h> 34 #include <sys/procset.h> 35 #include <sys/corectl.h> 36 #include <sys/zone.h> 37 #include <sys/cmn_err.h> 38 #include <sys/policy.h> 39 40 /* 41 * Core File Settings 42 * ------------------ 43 * 44 * A process's core file path and content live in separate reference-counted 45 * structures. The corectl_content_t structure is fairly straightforward -- 46 * the only subtlety is that we only really _need_ the mutex on architectures 47 * on which 64-bit memory operations are not atomic. The corectl_path_t 48 * structure is slightly trickier in that it contains a refstr_t rather than 49 * just a char * string. This is to allow consumers of the data in that 50 * structure (the core dumping sub-system for example) to safely use the 51 * string without holding any locks on it in light of updates. 52 * 53 * At system and zone boot, init_core() sets init(1M)'s core file path and 54 * content to the same value as the fields core_default_path and 55 * core_default_content respectively (for the global zone). All subsequent 56 * children of init(1M) reference those same settings. During boot coreadm(1M) 57 * is invoked with the -u option to update the system settings from 58 * /etc/coreadm.conf. This has the effect of also changing the values in 59 * core_default_path and core_default_content which updates the core file 60 * settings for all processes in the zone. Each zone has different default 61 * settings; when processes enter a non-global zone, their core file path and 62 * content are set to the zone's default path and content. 63 * 64 * Processes that have their core file settings explicitly overridden using 65 * coreadm(1M) no longer reference core_default_path or core_default_content 66 * so subsequent changes to the default will not affect them. 67 */ 68 69 zone_key_t core_zone_key; 70 71 static int set_proc_info(pid_t pid, const char *path, core_content_t content); 72 73 static corectl_content_t * 74 corectl_content_alloc(core_content_t cc) 75 { 76 corectl_content_t *ccp; 77 78 ccp = kmem_zalloc(sizeof (corectl_content_t), KM_SLEEP); 79 ccp->ccc_content = cc; 80 ccp->ccc_refcnt = 1; 81 82 return (ccp); 83 } 84 85 core_content_t 86 corectl_content_value(corectl_content_t *ccp) 87 { 88 core_content_t content; 89 90 mutex_enter(&ccp->ccc_mtx); 91 content = ccp->ccc_content; 92 mutex_exit(&ccp->ccc_mtx); 93 94 return (content); 95 } 96 97 static void 98 corectl_content_set(corectl_content_t *ccp, core_content_t content) 99 { 100 mutex_enter(&ccp->ccc_mtx); 101 ccp->ccc_content = content; 102 mutex_exit(&ccp->ccc_mtx); 103 } 104 105 void 106 corectl_content_hold(corectl_content_t *ccp) 107 { 108 atomic_add_32(&ccp->ccc_refcnt, 1); 109 } 110 111 void 112 corectl_content_rele(corectl_content_t *ccp) 113 { 114 if (atomic_add_32_nv(&ccp->ccc_refcnt, -1) == 0) 115 kmem_free(ccp, sizeof (corectl_content_t)); 116 } 117 118 119 static corectl_path_t * 120 corectl_path_alloc(const char *path) 121 { 122 corectl_path_t *ccp; 123 124 ccp = kmem_zalloc(sizeof (corectl_path_t), KM_SLEEP); 125 ccp->ccp_path = refstr_alloc(path); 126 ccp->ccp_refcnt = 1; 127 128 return (ccp); 129 } 130 131 refstr_t * 132 corectl_path_value(corectl_path_t *ccp) 133 { 134 refstr_t *path; 135 136 mutex_enter(&ccp->ccp_mtx); 137 refstr_hold(path = ccp->ccp_path); 138 mutex_exit(&ccp->ccp_mtx); 139 140 return (path); 141 } 142 143 static void 144 corectl_path_set(corectl_path_t *ccp, const char *path) 145 { 146 refstr_t *npath = refstr_alloc(path); 147 148 mutex_enter(&ccp->ccp_mtx); 149 refstr_rele(ccp->ccp_path); 150 ccp->ccp_path = npath; 151 mutex_exit(&ccp->ccp_mtx); 152 } 153 154 void 155 corectl_path_hold(corectl_path_t *ccp) 156 { 157 atomic_add_32(&ccp->ccp_refcnt, 1); 158 } 159 160 void 161 corectl_path_rele(corectl_path_t *ccp) 162 { 163 if (atomic_add_32_nv(&ccp->ccp_refcnt, -1) == 0) { 164 refstr_rele(ccp->ccp_path); 165 kmem_free(ccp, sizeof (corectl_path_t)); 166 } 167 } 168 169 /* 170 * Constructor routine to be called when a zone is created. 171 */ 172 /*ARGSUSED*/ 173 static void * 174 core_init_zone(zoneid_t zoneid) 175 { 176 struct core_globals *cg; 177 178 cg = kmem_alloc(sizeof (*cg), KM_SLEEP); 179 mutex_init(&cg->core_lock, NULL, MUTEX_DEFAULT, NULL); 180 cg->core_file = NULL; 181 cg->core_options = CC_PROCESS_PATH; 182 cg->core_content = CC_CONTENT_DEFAULT; 183 cg->core_rlimit = RLIM64_INFINITY; 184 cg->core_default_path = corectl_path_alloc("core"); 185 cg->core_default_content = corectl_content_alloc(CC_CONTENT_DEFAULT); 186 187 return (cg); 188 } 189 190 /* 191 * Destructor routine to be called when a zone is destroyed. 192 */ 193 /*ARGSUSED*/ 194 static void 195 core_free_zone(zoneid_t zoneid, void *arg) 196 { 197 struct core_globals *cg = arg; 198 199 if (cg == NULL) 200 return; 201 if (cg->core_file != NULL) 202 refstr_rele(cg->core_file); 203 corectl_path_rele(cg->core_default_path); 204 corectl_content_rele(cg->core_default_content); 205 kmem_free(cg, sizeof (*cg)); 206 } 207 208 /* 209 * Called from start_init_common(), to set init's core file path and content. 210 */ 211 void 212 init_core(void) 213 { 214 struct core_globals *cg; 215 216 /* 217 * The first time we hit this, in the global zone, we have to 218 * initialize the zsd key. 219 */ 220 if (INGLOBALZONE(curproc)) { 221 zone_key_create(&core_zone_key, core_init_zone, NULL, 222 core_free_zone); 223 } 224 225 /* 226 * zone_key_create will have called core_init_zone for the 227 * global zone, which sets up the default path and content 228 * variables. 229 */ 230 VERIFY((cg = zone_getspecific(core_zone_key, curproc->p_zone)) != NULL); 231 232 corectl_path_hold(cg->core_default_path); 233 corectl_content_hold(cg->core_default_content); 234 235 curproc->p_corefile = cg->core_default_path; 236 curproc->p_content = cg->core_default_content; 237 } 238 239 int 240 corectl(int subcode, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) 241 { 242 int error = 0; 243 proc_t *p; 244 refstr_t *rp; 245 size_t size; 246 char *path; 247 core_content_t content = CC_CONTENT_INVALID; 248 struct core_globals *cg; 249 zone_t *zone = curproc->p_zone; 250 251 cg = zone_getspecific(core_zone_key, zone); 252 ASSERT(cg != NULL); 253 254 switch (subcode) { 255 case CC_SET_OPTIONS: 256 if ((error = secpolicy_coreadm(CRED())) == 0) { 257 if (arg1 & ~CC_OPTIONS) 258 error = EINVAL; 259 else 260 cg->core_options = (uint32_t)arg1; 261 } 262 break; 263 264 case CC_GET_OPTIONS: 265 return (cg->core_options); 266 267 case CC_GET_GLOBAL_PATH: 268 case CC_GET_DEFAULT_PATH: 269 case CC_GET_PROCESS_PATH: 270 if (subcode == CC_GET_GLOBAL_PATH) { 271 mutex_enter(&cg->core_lock); 272 if ((rp = cg->core_file) != NULL) 273 refstr_hold(rp); 274 mutex_exit(&cg->core_lock); 275 } else if (subcode == CC_GET_DEFAULT_PATH) { 276 rp = corectl_path_value(cg->core_default_path); 277 } else { 278 rp = NULL; 279 mutex_enter(&pidlock); 280 if ((p = prfind((pid_t)arg3)) == NULL || 281 p->p_stat == SIDL) { 282 mutex_exit(&pidlock); 283 error = ESRCH; 284 } else { 285 mutex_enter(&p->p_lock); 286 mutex_exit(&pidlock); 287 mutex_enter(&p->p_crlock); 288 if (!hasprocperm(p->p_cred, CRED())) 289 error = EPERM; 290 else if (p->p_corefile != NULL) 291 rp = corectl_path_value(p->p_corefile); 292 mutex_exit(&p->p_crlock); 293 mutex_exit(&p->p_lock); 294 } 295 } 296 if (rp == NULL) { 297 if (error == 0 && suword8((void *)arg1, 0)) 298 error = EFAULT; 299 } else { 300 error = copyoutstr(refstr_value(rp), (char *)arg1, 301 (size_t)arg2, NULL); 302 refstr_rele(rp); 303 } 304 break; 305 306 case CC_SET_GLOBAL_PATH: 307 case CC_SET_DEFAULT_PATH: 308 if ((error = secpolicy_coreadm(CRED())) != 0) 309 break; 310 311 /* FALLTHROUGH */ 312 case CC_SET_PROCESS_PATH: 313 if ((size = MIN((size_t)arg2, MAXPATHLEN)) == 0) { 314 error = EINVAL; 315 break; 316 } 317 path = kmem_alloc(size, KM_SLEEP); 318 error = copyinstr((char *)arg1, path, size, NULL); 319 if (error == 0) { 320 if (subcode == CC_SET_PROCESS_PATH) { 321 error = set_proc_info((pid_t)arg3, path, 0); 322 } else if (subcode == CC_SET_DEFAULT_PATH) { 323 corectl_path_set(cg->core_default_path, path); 324 } else if (*path != '\0' && *path != '/') { 325 error = EINVAL; 326 } else { 327 refstr_t *nrp = refstr_alloc(path); 328 329 mutex_enter(&cg->core_lock); 330 rp = cg->core_file; 331 if (*path == '\0') 332 cg->core_file = NULL; 333 else 334 refstr_hold(cg->core_file = nrp); 335 mutex_exit(&cg->core_lock); 336 337 if (rp != NULL) 338 refstr_rele(rp); 339 340 refstr_rele(nrp); 341 } 342 } 343 kmem_free(path, size); 344 break; 345 346 case CC_SET_GLOBAL_CONTENT: 347 case CC_SET_DEFAULT_CONTENT: 348 if ((error = secpolicy_coreadm(CRED())) != 0) 349 break; 350 351 /* FALLTHROUGH */ 352 case CC_SET_PROCESS_CONTENT: 353 error = copyin((void *)arg1, &content, sizeof (content)); 354 if (error != 0) 355 break; 356 357 /* 358 * If any unknown bits are set, don't let this charade 359 * continue. 360 */ 361 if (content & ~CC_CONTENT_ALL) { 362 error = EINVAL; 363 break; 364 } 365 366 if (subcode == CC_SET_PROCESS_CONTENT) { 367 error = set_proc_info((pid_t)arg2, NULL, content); 368 } else if (subcode == CC_SET_DEFAULT_CONTENT) { 369 corectl_content_set(cg->core_default_content, content); 370 } else { 371 mutex_enter(&cg->core_lock); 372 cg->core_content = content; 373 mutex_exit(&cg->core_lock); 374 } 375 376 break; 377 378 case CC_GET_GLOBAL_CONTENT: 379 content = cg->core_content; 380 error = copyout(&content, (void *)arg1, sizeof (content)); 381 break; 382 383 case CC_GET_DEFAULT_CONTENT: 384 content = corectl_content_value(cg->core_default_content); 385 error = copyout(&content, (void *)arg1, sizeof (content)); 386 break; 387 388 case CC_GET_PROCESS_CONTENT: 389 mutex_enter(&pidlock); 390 if ((p = prfind((pid_t)arg2)) == NULL || p->p_stat == SIDL) { 391 mutex_exit(&pidlock); 392 error = ESRCH; 393 break; 394 } 395 396 mutex_enter(&p->p_lock); 397 mutex_exit(&pidlock); 398 mutex_enter(&p->p_crlock); 399 if (!hasprocperm(p->p_cred, CRED())) 400 error = EPERM; 401 else if (p->p_content == NULL) 402 content = CC_CONTENT_NONE; 403 else 404 content = corectl_content_value(p->p_content); 405 mutex_exit(&p->p_crlock); 406 mutex_exit(&p->p_lock); 407 408 if (error == 0) 409 error = copyout(&content, (void *)arg1, 410 sizeof (content)); 411 break; 412 413 default: 414 error = EINVAL; 415 break; 416 } 417 418 if (error) 419 return (set_errno(error)); 420 return (0); 421 } 422 423 typedef struct { 424 int cc_count; 425 corectl_path_t *cc_path; 426 corectl_content_t *cc_content; 427 } counter_t; 428 429 static int 430 set_one_proc_info(proc_t *p, counter_t *counterp) 431 { 432 corectl_path_t *corefile; 433 corectl_content_t *content; 434 435 mutex_enter(&p->p_crlock); 436 437 if (!(p->p_flag & SSYS) && hasprocperm(p->p_cred, CRED())) { 438 mutex_exit(&p->p_crlock); 439 counterp->cc_count++; 440 if (counterp->cc_path != NULL) { 441 corectl_path_hold(counterp->cc_path); 442 mutex_enter(&p->p_lock); 443 corefile = p->p_corefile; 444 p->p_corefile = counterp->cc_path; 445 mutex_exit(&p->p_lock); 446 if (corefile != NULL) 447 corectl_path_rele(corefile); 448 } else { 449 corectl_content_hold(counterp->cc_content); 450 mutex_enter(&p->p_lock); 451 content = p->p_content; 452 p->p_content = counterp->cc_content; 453 mutex_exit(&p->p_lock); 454 if (content != NULL) 455 corectl_content_rele(content); 456 } 457 } else { 458 mutex_exit(&p->p_crlock); 459 } 460 461 return (0); 462 } 463 464 static int 465 set_proc_info(pid_t pid, const char *path, core_content_t content) 466 { 467 proc_t *p; 468 counter_t counter; 469 int error = 0; 470 471 counter.cc_count = 0; 472 /* 473 * Only one of the core file path or content can be set at a time. 474 */ 475 if (path != NULL) { 476 counter.cc_path = corectl_path_alloc(path); 477 counter.cc_content = NULL; 478 } else { 479 counter.cc_path = NULL; 480 counter.cc_content = corectl_content_alloc(content); 481 } 482 483 if (pid == -1) { 484 procset_t set; 485 486 setprocset(&set, POP_AND, P_ALL, P_MYID, P_ALL, P_MYID); 487 error = dotoprocs(&set, set_one_proc_info, (char *)&counter); 488 if (error == 0 && counter.cc_count == 0) 489 error = EPERM; 490 } else if (pid > 0) { 491 mutex_enter(&pidlock); 492 if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) { 493 error = ESRCH; 494 } else { 495 (void) set_one_proc_info(p, &counter); 496 if (counter.cc_count == 0) 497 error = EPERM; 498 } 499 mutex_exit(&pidlock); 500 } else { 501 int nfound = 0; 502 pid_t pgid; 503 504 if (pid == 0) 505 pgid = curproc->p_pgrp; 506 else 507 pgid = -pid; 508 509 mutex_enter(&pidlock); 510 for (p = pgfind(pgid); p != NULL; p = p->p_pglink) { 511 if (p->p_stat != SIDL) { 512 nfound++; 513 (void) set_one_proc_info(p, &counter); 514 } 515 } 516 mutex_exit(&pidlock); 517 if (nfound == 0) 518 error = ESRCH; 519 else if (counter.cc_count == 0) 520 error = EPERM; 521 } 522 523 if (path != NULL) 524 corectl_path_rele(counter.cc_path); 525 else 526 corectl_content_rele(counter.cc_content); 527 528 if (error) 529 return (set_errno(error)); 530 return (0); 531 } 532 533 /* 534 * Give current process the default core settings for its current zone; 535 * used for processes entering a zone via zone_enter. 536 */ 537 void 538 set_core_defaults(void) 539 { 540 proc_t *p = curproc; 541 struct core_globals *cg; 542 corectl_path_t *oldpath, *newpath; 543 corectl_content_t *oldcontent, *newcontent; 544 545 cg = zone_getspecific(core_zone_key, p->p_zone); 546 547 /* make local copies of default values to protect against change */ 548 newpath = cg->core_default_path; 549 newcontent = cg->core_default_content; 550 551 corectl_path_hold(newpath); 552 corectl_content_hold(newcontent); 553 mutex_enter(&p->p_lock); 554 oldpath = p->p_corefile; 555 p->p_corefile = newpath; 556 oldcontent = p->p_content; 557 p->p_content = newcontent; 558 mutex_exit(&p->p_lock); 559 if (oldpath != NULL) 560 corectl_path_rele(oldpath); 561 if (oldcontent != NULL) 562 corectl_content_rele(oldcontent); 563 } 564