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