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