xref: /illumos-gate/usr/src/uts/common/syscall/corectl.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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