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 *
corectl_content_alloc(core_content_t cc)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
corectl_content_value(corectl_content_t * ccp)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
corectl_content_set(corectl_content_t * ccp,core_content_t content)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
corectl_content_hold(corectl_content_t * ccp)106 corectl_content_hold(corectl_content_t *ccp)
107 {
108 atomic_add_32(&ccp->ccc_refcnt, 1);
109 }
110
111 void
corectl_content_rele(corectl_content_t * ccp)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 *
corectl_path_alloc(const char * path)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 *
corectl_path_value(corectl_path_t * ccp)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
corectl_path_set(corectl_path_t * ccp,const char * path)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
corectl_path_hold(corectl_path_t * ccp)155 corectl_path_hold(corectl_path_t *ccp)
156 {
157 atomic_add_32(&ccp->ccp_refcnt, 1);
158 }
159
160 void
corectl_path_rele(corectl_path_t * ccp)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 *
core_init_zone(zoneid_t zoneid)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
core_free_zone(zoneid_t zoneid,void * arg)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
init_core(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
corectl(int subcode,uintptr_t arg1,uintptr_t arg2,uintptr_t arg3)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
set_one_proc_info(proc_t * p,counter_t * counterp)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
set_proc_info(pid_t pid,const char * path,core_content_t content)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
set_core_defaults(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