Home | History | Annotate | Download | only in syscall
      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