xref: /illumos-gate/usr/src/uts/common/os/core.c (revision 4e18e297)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5ae115bc7Smrj  * Common Development and Distribution License (the "License").
6ae115bc7Smrj  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21ee01d137Sraf 
227c478bd9Sstevel@tonic-gate /*
23794f0adbSRoger A. Faulkner  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
2448bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
25*4e18e297SPatrick Mooney  * Copyright 2019 Joyent Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29*4e18e297SPatrick Mooney /*	  All Rights Reserved	*/
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/param.h>
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate #include <sys/time.h>
347c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
357c478bd9Sstevel@tonic-gate #include <sys/proc.h>
367c478bd9Sstevel@tonic-gate #include <sys/systm.h>
377c478bd9Sstevel@tonic-gate #include <sys/cred.h>
387c478bd9Sstevel@tonic-gate #include <sys/user.h>
397c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
407c478bd9Sstevel@tonic-gate #include <sys/errno.h>
417c478bd9Sstevel@tonic-gate #include <sys/signal.h>
427c478bd9Sstevel@tonic-gate #include <sys/siginfo.h>
437c478bd9Sstevel@tonic-gate #include <sys/fault.h>
447c478bd9Sstevel@tonic-gate #include <sys/syscall.h>
457c478bd9Sstevel@tonic-gate #include <sys/ucontext.h>
467c478bd9Sstevel@tonic-gate #include <sys/prsystm.h>
477c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
487c478bd9Sstevel@tonic-gate #include <sys/var.h>
497c478bd9Sstevel@tonic-gate #include <sys/file.h>
507c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
517c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
527c478bd9Sstevel@tonic-gate #include <sys/exec.h>
537c478bd9Sstevel@tonic-gate #include <sys/debug.h>
547c478bd9Sstevel@tonic-gate #include <sys/stack.h>
557c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
567c478bd9Sstevel@tonic-gate #include <sys/schedctl.h>
577c478bd9Sstevel@tonic-gate #include <sys/core.h>
587c478bd9Sstevel@tonic-gate #include <sys/corectl.h>
597c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
607c478bd9Sstevel@tonic-gate #include <vm/as.h>
617c478bd9Sstevel@tonic-gate #include <sys/rctl.h>
627c478bd9Sstevel@tonic-gate #include <sys/nbmlock.h>
637c478bd9Sstevel@tonic-gate #include <sys/stat.h>
647c478bd9Sstevel@tonic-gate #include <sys/zone.h>
657c478bd9Sstevel@tonic-gate #include <sys/contract/process_impl.h>
66f8860408SJohn Harres #include <sys/ddi.h>
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate /*
697c478bd9Sstevel@tonic-gate  * Processes running within a zone potentially dump core in 3 locations,
707c478bd9Sstevel@tonic-gate  * based on the per-process, per-zone, and the global zone's core settings.
717c478bd9Sstevel@tonic-gate  *
727c478bd9Sstevel@tonic-gate  * Per-zone and global zone settings are often referred to as "global"
737c478bd9Sstevel@tonic-gate  * settings since they apply to the system (or zone) as a whole, as
747c478bd9Sstevel@tonic-gate  * opposed to a particular process.
757c478bd9Sstevel@tonic-gate  */
767c478bd9Sstevel@tonic-gate enum core_types {
777c478bd9Sstevel@tonic-gate 	CORE_PROC,	/* Use per-process settings */
787c478bd9Sstevel@tonic-gate 	CORE_ZONE,	/* Use per-zone settings */
797c478bd9Sstevel@tonic-gate 	CORE_GLOBAL	/* Use global zone settings */
807c478bd9Sstevel@tonic-gate };
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate  * Log information about "global" core dumps to syslog.
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate static void
core_log(struct core_globals * cg,int error,const char * why,const char * path,zoneid_t zoneid)867c478bd9Sstevel@tonic-gate core_log(struct core_globals *cg, int error, const char *why, const char *path,
877c478bd9Sstevel@tonic-gate     zoneid_t zoneid)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
907c478bd9Sstevel@tonic-gate 	pid_t pid = p->p_pid;
917c478bd9Sstevel@tonic-gate 	char *fn = PTOU(p)->u_comm;
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	if (!(cg->core_options & CC_GLOBAL_LOG))
947c478bd9Sstevel@tonic-gate 		return;
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	if (path == NULL)
977c478bd9Sstevel@tonic-gate 		zcmn_err(zoneid, CE_NOTE, "core_log: %s[%d] %s", fn, pid, why);
987c478bd9Sstevel@tonic-gate 	else if (error == 0)
997c478bd9Sstevel@tonic-gate 		zcmn_err(zoneid, CE_NOTE, "core_log: %s[%d] %s: %s", fn, pid,
1007c478bd9Sstevel@tonic-gate 		    why, path);
1017c478bd9Sstevel@tonic-gate 	else
1027c478bd9Sstevel@tonic-gate 		zcmn_err(zoneid, CE_NOTE, "core_log: %s[%d] %s, errno=%d: %s",
1037c478bd9Sstevel@tonic-gate 		    fn, pid, why, error, path);
1047c478bd9Sstevel@tonic-gate }
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate /*
1077c478bd9Sstevel@tonic-gate  * Private version of vn_remove().
1087c478bd9Sstevel@tonic-gate  * Refuse to unlink a directory or an unwritable file.
1097c478bd9Sstevel@tonic-gate  * Also allow the process to access files normally inaccessible due to
1107c478bd9Sstevel@tonic-gate  * chroot(2) or Zone limitations.
1117c478bd9Sstevel@tonic-gate  */
1127c478bd9Sstevel@tonic-gate static int
remove_core_file(char * fp,enum core_types core_type)1137c478bd9Sstevel@tonic-gate remove_core_file(char *fp, enum core_types core_type)
1147c478bd9Sstevel@tonic-gate {
1157c478bd9Sstevel@tonic-gate 	vnode_t *vp = NULL;		/* entry vnode */
1167c478bd9Sstevel@tonic-gate 	vnode_t *dvp;			/* ptr to parent dir vnode */
1177c478bd9Sstevel@tonic-gate 	vfs_t *dvfsp;
1187c478bd9Sstevel@tonic-gate 	int error;
1197c478bd9Sstevel@tonic-gate 	int in_crit = 0;
1207c478bd9Sstevel@tonic-gate 	pathname_t pn;			/* name of entry */
1217c478bd9Sstevel@tonic-gate 	vnode_t *startvp, *rootvp;
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 	if ((error = pn_get(fp, UIO_SYSSPACE, &pn)) != 0)
1247c478bd9Sstevel@tonic-gate 		return (error);
1257c478bd9Sstevel@tonic-gate 	/*
1267c478bd9Sstevel@tonic-gate 	 * Determine what rootvp to use.
1277c478bd9Sstevel@tonic-gate 	 */
1287c478bd9Sstevel@tonic-gate 	if (core_type == CORE_PROC) {
1297c478bd9Sstevel@tonic-gate 		rootvp = (PTOU(curproc)->u_rdir == NULL ?
1307c478bd9Sstevel@tonic-gate 		    curproc->p_zone->zone_rootvp : PTOU(curproc)->u_rdir);
1317c478bd9Sstevel@tonic-gate 		startvp = (fp[0] == '/' ? rootvp : PTOU(curproc)->u_cdir);
1327c478bd9Sstevel@tonic-gate 	} else if (core_type == CORE_ZONE) {
1337c478bd9Sstevel@tonic-gate 		startvp = curproc->p_zone->zone_rootvp;
1347c478bd9Sstevel@tonic-gate 		rootvp = curproc->p_zone->zone_rootvp;
1357c478bd9Sstevel@tonic-gate 	} else {
1367c478bd9Sstevel@tonic-gate 		ASSERT(core_type == CORE_GLOBAL);
1377c478bd9Sstevel@tonic-gate 		startvp = rootdir;
1387c478bd9Sstevel@tonic-gate 		rootvp = rootdir;
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate 	VN_HOLD(startvp);
1417c478bd9Sstevel@tonic-gate 	if (rootvp != rootdir)
1427c478bd9Sstevel@tonic-gate 		VN_HOLD(rootvp);
1437c478bd9Sstevel@tonic-gate 	if ((error = lookuppnvp(&pn, NULL, NO_FOLLOW, &dvp, &vp, rootvp,
1447c478bd9Sstevel@tonic-gate 	    startvp, CRED())) != 0) {
1457c478bd9Sstevel@tonic-gate 		pn_free(&pn);
1467c478bd9Sstevel@tonic-gate 		return (error);
1477c478bd9Sstevel@tonic-gate 	}
1487c478bd9Sstevel@tonic-gate 	/*
1497c478bd9Sstevel@tonic-gate 	 * Succeed if there is no file.
1507c478bd9Sstevel@tonic-gate 	 * Fail if the file is not a regular file.
1517c478bd9Sstevel@tonic-gate 	 * Fail if the filesystem is mounted read-only.
1527c478bd9Sstevel@tonic-gate 	 * Fail if the file is not writeable.
1537c478bd9Sstevel@tonic-gate 	 * Fail if the file has NBMAND share reservations.
1547c478bd9Sstevel@tonic-gate 	 */
1557c478bd9Sstevel@tonic-gate 	if (vp == NULL)
1567c478bd9Sstevel@tonic-gate 		error = 0;
1577c478bd9Sstevel@tonic-gate 	else if (vp->v_type != VREG)
1587c478bd9Sstevel@tonic-gate 		error = EACCES;
1597c478bd9Sstevel@tonic-gate 	else if ((dvfsp = dvp->v_vfsp) != NULL &&
1607c478bd9Sstevel@tonic-gate 	    (dvfsp->vfs_flag & VFS_RDONLY))
1617c478bd9Sstevel@tonic-gate 		error = EROFS;
162da6c28aaSamw 	else if ((error = VOP_ACCESS(vp, VWRITE, 0, CRED(), NULL)) == 0) {
1637c478bd9Sstevel@tonic-gate 		if (nbl_need_check(vp)) {
1647c478bd9Sstevel@tonic-gate 			nbl_start_crit(vp, RW_READER);
1657c478bd9Sstevel@tonic-gate 			in_crit = 1;
166da6c28aaSamw 			if (nbl_share_conflict(vp, NBL_REMOVE, NULL)) {
1677c478bd9Sstevel@tonic-gate 				error = EACCES;
1687c478bd9Sstevel@tonic-gate 			}
1697c478bd9Sstevel@tonic-gate 		}
1707c478bd9Sstevel@tonic-gate 		if (!error) {
171da6c28aaSamw 			error = VOP_REMOVE(dvp, pn.pn_path, CRED(), NULL, 0);
1727c478bd9Sstevel@tonic-gate 		}
1737c478bd9Sstevel@tonic-gate 	}
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	pn_free(&pn);
1767c478bd9Sstevel@tonic-gate 	if (vp != NULL) {
1777c478bd9Sstevel@tonic-gate 		if (in_crit)
1787c478bd9Sstevel@tonic-gate 			nbl_end_crit(vp);
1797c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
1807c478bd9Sstevel@tonic-gate 	}
1817c478bd9Sstevel@tonic-gate 	VN_RELE(dvp);
1827c478bd9Sstevel@tonic-gate 	return (error);
1837c478bd9Sstevel@tonic-gate }
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate /*
1867c478bd9Sstevel@tonic-gate  * Create the core file in a location that may be normally inaccessible due
1877c478bd9Sstevel@tonic-gate  * to chroot(2) or Zone limitations.
1887c478bd9Sstevel@tonic-gate  */
1897c478bd9Sstevel@tonic-gate static int
create_core_file(char * fp,enum core_types core_type,vnode_t ** vpp)1907c478bd9Sstevel@tonic-gate create_core_file(char *fp, enum core_types core_type, vnode_t **vpp)
1917c478bd9Sstevel@tonic-gate {
1927c478bd9Sstevel@tonic-gate 	int error;
1937c478bd9Sstevel@tonic-gate 	mode_t perms = (S_IRUSR | S_IWUSR);
1947c478bd9Sstevel@tonic-gate 	pathname_t pn;
1957c478bd9Sstevel@tonic-gate 	char *file;
1967c478bd9Sstevel@tonic-gate 	vnode_t *vp;
1977c478bd9Sstevel@tonic-gate 	vnode_t *dvp;
1987c478bd9Sstevel@tonic-gate 	vattr_t vattr;
1997c478bd9Sstevel@tonic-gate 	cred_t *credp = CRED();
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	if (core_type == CORE_PROC) {
2027c478bd9Sstevel@tonic-gate 		file = fp;
2037c478bd9Sstevel@tonic-gate 		dvp = NULL;	/* regular lookup */
2047c478bd9Sstevel@tonic-gate 	} else {
2057c478bd9Sstevel@tonic-gate 		vnode_t *startvp, *rootvp;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 		ASSERT(core_type == CORE_ZONE || core_type == CORE_GLOBAL);
2087c478bd9Sstevel@tonic-gate 		/*
2097c478bd9Sstevel@tonic-gate 		 * This is tricky because we want to dump the core in
2107c478bd9Sstevel@tonic-gate 		 * a location which may normally be inaccessible
2117c478bd9Sstevel@tonic-gate 		 * to us (due to chroot(2) limitations, or zone
2127c478bd9Sstevel@tonic-gate 		 * membership), and hence need to overcome u_rdir
2137c478bd9Sstevel@tonic-gate 		 * restrictions.  The basic idea is to separate
2147c478bd9Sstevel@tonic-gate 		 * the path from the filename, lookup the
2157c478bd9Sstevel@tonic-gate 		 * pathname separately (starting from the global
2167c478bd9Sstevel@tonic-gate 		 * zone's root directory), and then open the
2177c478bd9Sstevel@tonic-gate 		 * file starting at the directory vnode.
2187c478bd9Sstevel@tonic-gate 		 */
2197c478bd9Sstevel@tonic-gate 		if (error = pn_get(fp, UIO_SYSSPACE, &pn))
2207c478bd9Sstevel@tonic-gate 			return (error);
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 		if (core_type == CORE_ZONE) {
2237c478bd9Sstevel@tonic-gate 			startvp = rootvp = curproc->p_zone->zone_rootvp;
2247c478bd9Sstevel@tonic-gate 		} else {
2257c478bd9Sstevel@tonic-gate 			startvp = rootvp = rootdir;
2267c478bd9Sstevel@tonic-gate 		}
2277c478bd9Sstevel@tonic-gate 		/*
2287c478bd9Sstevel@tonic-gate 		 * rootvp and startvp will be VN_RELE()'d by lookuppnvp() if
2297c478bd9Sstevel@tonic-gate 		 * necessary.
2307c478bd9Sstevel@tonic-gate 		 */
2317c478bd9Sstevel@tonic-gate 		VN_HOLD(startvp);
2327c478bd9Sstevel@tonic-gate 		if (rootvp != rootdir)
2337c478bd9Sstevel@tonic-gate 			VN_HOLD(rootvp);
2347c478bd9Sstevel@tonic-gate 		/*
2357c478bd9Sstevel@tonic-gate 		 * Do a lookup on the full path, ignoring the actual file, but
2367c478bd9Sstevel@tonic-gate 		 * finding the vnode for the directory.  It's OK if the file
2377c478bd9Sstevel@tonic-gate 		 * doesn't exist -- it most likely won't since we just removed
2387c478bd9Sstevel@tonic-gate 		 * it.
2397c478bd9Sstevel@tonic-gate 		 */
2407c478bd9Sstevel@tonic-gate 		error = lookuppnvp(&pn, NULL, FOLLOW, &dvp, NULLVPP,
2417c478bd9Sstevel@tonic-gate 		    rootvp, startvp, credp);
2427c478bd9Sstevel@tonic-gate 		pn_free(&pn);
2437c478bd9Sstevel@tonic-gate 		if (error != 0)
2447c478bd9Sstevel@tonic-gate 			return (error);
2457c478bd9Sstevel@tonic-gate 		ASSERT(dvp != NULL);
2467c478bd9Sstevel@tonic-gate 		/*
2477c478bd9Sstevel@tonic-gate 		 * Now find the final component in the path (ie, the name of
2487c478bd9Sstevel@tonic-gate 		 * the core file).
2497c478bd9Sstevel@tonic-gate 		 */
2507c478bd9Sstevel@tonic-gate 		if (error = pn_get(fp, UIO_SYSSPACE, &pn)) {
2517c478bd9Sstevel@tonic-gate 			VN_RELE(dvp);
2527c478bd9Sstevel@tonic-gate 			return (error);
2537c478bd9Sstevel@tonic-gate 		}
2547c478bd9Sstevel@tonic-gate 		pn_setlast(&pn);
2557c478bd9Sstevel@tonic-gate 		file = pn.pn_path;
2567c478bd9Sstevel@tonic-gate 	}
257da6c28aaSamw 	error =  vn_openat(file, UIO_SYSSPACE,
258da6c28aaSamw 	    FWRITE | FTRUNC | FEXCL | FCREAT | FOFFMAX,
259da6c28aaSamw 	    perms, &vp, CRCREAT, PTOU(curproc)->u_cmask, dvp, -1);
2607c478bd9Sstevel@tonic-gate 	if (core_type != CORE_PROC) {
2617c478bd9Sstevel@tonic-gate 		VN_RELE(dvp);
2627c478bd9Sstevel@tonic-gate 		pn_free(&pn);
2637c478bd9Sstevel@tonic-gate 	}
2647c478bd9Sstevel@tonic-gate 	/*
2657c478bd9Sstevel@tonic-gate 	 * Don't dump a core file owned by "nobody".
2667c478bd9Sstevel@tonic-gate 	 */
2677c478bd9Sstevel@tonic-gate 	vattr.va_mask = AT_UID;
2687c478bd9Sstevel@tonic-gate 	if (error == 0 &&
269da6c28aaSamw 	    (VOP_GETATTR(vp, &vattr, 0, credp, NULL) != 0 ||
2707c478bd9Sstevel@tonic-gate 	    vattr.va_uid != crgetuid(credp))) {
2717c478bd9Sstevel@tonic-gate 		(void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0,
272da6c28aaSamw 		    credp, NULL);
2737c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
2747c478bd9Sstevel@tonic-gate 		(void) remove_core_file(fp, core_type);
2757c478bd9Sstevel@tonic-gate 		error = EACCES;
2767c478bd9Sstevel@tonic-gate 	}
2777c478bd9Sstevel@tonic-gate 	*vpp = vp;
2787c478bd9Sstevel@tonic-gate 	return (error);
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  * Install the specified held cred into the process, and return a pointer to
2837c478bd9Sstevel@tonic-gate  * the held cred which was previously the value of p->p_cred.
2847c478bd9Sstevel@tonic-gate  */
2857c478bd9Sstevel@tonic-gate static cred_t *
set_cred(proc_t * p,cred_t * newcr)2867c478bd9Sstevel@tonic-gate set_cred(proc_t *p, cred_t *newcr)
2877c478bd9Sstevel@tonic-gate {
2887c478bd9Sstevel@tonic-gate 	cred_t *oldcr;
2897c478bd9Sstevel@tonic-gate 	uid_t olduid, newuid;
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	/*
2927c478bd9Sstevel@tonic-gate 	 * Place a hold on the existing cred, and then install the new
2937c478bd9Sstevel@tonic-gate 	 * cred into the proc structure.
2947c478bd9Sstevel@tonic-gate 	 */
2957c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_crlock);
2967c478bd9Sstevel@tonic-gate 	oldcr = p->p_cred;
2977c478bd9Sstevel@tonic-gate 	crhold(oldcr);
2987c478bd9Sstevel@tonic-gate 	p->p_cred = newcr;
2997c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_crlock);
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 	ASSERT(crgetzoneid(oldcr) == crgetzoneid(newcr));
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	/*
3047c478bd9Sstevel@tonic-gate 	 * If the real uid is changing, keep the per-user process
3057c478bd9Sstevel@tonic-gate 	 * counts accurate.
3067c478bd9Sstevel@tonic-gate 	 */
3077c478bd9Sstevel@tonic-gate 	olduid = crgetruid(oldcr);
3087c478bd9Sstevel@tonic-gate 	newuid = crgetruid(newcr);
3097c478bd9Sstevel@tonic-gate 	if (olduid != newuid) {
3107c478bd9Sstevel@tonic-gate 		zoneid_t zoneid = crgetzoneid(newcr);
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 		mutex_enter(&pidlock);
3137c478bd9Sstevel@tonic-gate 		upcount_dec(olduid, zoneid);
3147c478bd9Sstevel@tonic-gate 		upcount_inc(newuid, zoneid);
3157c478bd9Sstevel@tonic-gate 		mutex_exit(&pidlock);
3167c478bd9Sstevel@tonic-gate 	}
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	/*
3197c478bd9Sstevel@tonic-gate 	 * Broadcast the new cred to all the other threads.  The old
3207c478bd9Sstevel@tonic-gate 	 * cred can be safely returned because we have a hold on it.
3217c478bd9Sstevel@tonic-gate 	 */
3227c478bd9Sstevel@tonic-gate 	crset(p, newcr);
3237c478bd9Sstevel@tonic-gate 	return (oldcr);
3247c478bd9Sstevel@tonic-gate }
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate static int
do_core(char * fp,int sig,enum core_types core_type,struct core_globals * cg)3277c478bd9Sstevel@tonic-gate do_core(char *fp, int sig, enum core_types core_type, struct core_globals *cg)
3287c478bd9Sstevel@tonic-gate {
3297c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
3307c478bd9Sstevel@tonic-gate 	cred_t *credp = CRED();
3317c478bd9Sstevel@tonic-gate 	rlim64_t rlimit;
3327c478bd9Sstevel@tonic-gate 	vnode_t *vp;
3337c478bd9Sstevel@tonic-gate 	int error = 0;
3347c478bd9Sstevel@tonic-gate 	struct execsw *eswp;
3357c478bd9Sstevel@tonic-gate 	cred_t *ocredp = NULL;
3367c478bd9Sstevel@tonic-gate 	int is_setid = 0;
3377c478bd9Sstevel@tonic-gate 	core_content_t content;
3387c478bd9Sstevel@tonic-gate 	uid_t uid;
3397c478bd9Sstevel@tonic-gate 	gid_t gid;
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	if (core_type == CORE_GLOBAL || core_type == CORE_ZONE) {
3427c478bd9Sstevel@tonic-gate 		mutex_enter(&cg->core_lock);
3437c478bd9Sstevel@tonic-gate 		content = cg->core_content;
3447c478bd9Sstevel@tonic-gate 		mutex_exit(&cg->core_lock);
3457c478bd9Sstevel@tonic-gate 		rlimit = cg->core_rlimit;
3467c478bd9Sstevel@tonic-gate 	} else {
3477c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
3487c478bd9Sstevel@tonic-gate 		rlimit = rctl_enforced_value(rctlproc_legacy[RLIMIT_CORE],
3497c478bd9Sstevel@tonic-gate 		    p->p_rctls, p);
3507c478bd9Sstevel@tonic-gate 		content = corectl_content_value(p->p_content);
3517c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
3527c478bd9Sstevel@tonic-gate 	}
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 	if (rlimit == 0)
3557c478bd9Sstevel@tonic-gate 		return (EFBIG);
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	/*
3587c478bd9Sstevel@tonic-gate 	 * If SNOCD is set, or if the effective, real, and saved ids do
3597c478bd9Sstevel@tonic-gate 	 * not match up, no one but a privileged user is allowed to view
3607c478bd9Sstevel@tonic-gate 	 * this core file.  Set the credentials and the owner to root.
3617c478bd9Sstevel@tonic-gate 	 */
3627c478bd9Sstevel@tonic-gate 	if ((p->p_flag & SNOCD) ||
3637c478bd9Sstevel@tonic-gate 	    (uid = crgetuid(credp)) != crgetruid(credp) ||
3647c478bd9Sstevel@tonic-gate 	    uid != crgetsuid(credp) ||
3657c478bd9Sstevel@tonic-gate 	    (gid = crgetgid(credp)) != crgetrgid(credp) ||
3667c478bd9Sstevel@tonic-gate 	    gid != crgetsgid(credp)) {
3677c478bd9Sstevel@tonic-gate 		/*
3687c478bd9Sstevel@tonic-gate 		 * Because this is insecure against certain forms of file
3697c478bd9Sstevel@tonic-gate 		 * system attack, do it only if set-id core files have been
3707c478bd9Sstevel@tonic-gate 		 * enabled via corectl(CC_GLOBAL_SETID | CC_PROCESS_SETID).
3717c478bd9Sstevel@tonic-gate 		 */
3727c478bd9Sstevel@tonic-gate 		if (((core_type == CORE_GLOBAL || core_type == CORE_ZONE) &&
3737c478bd9Sstevel@tonic-gate 		    !(cg->core_options & CC_GLOBAL_SETID)) ||
3747c478bd9Sstevel@tonic-gate 		    (core_type == CORE_PROC &&
3757c478bd9Sstevel@tonic-gate 		    !(cg->core_options & CC_PROCESS_SETID)))
3767c478bd9Sstevel@tonic-gate 			return (ENOTSUP);
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 		is_setid = 1;
3797c478bd9Sstevel@tonic-gate 	}
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	/*
3827c478bd9Sstevel@tonic-gate 	 * If we are doing a "global" core dump or a set-id core dump,
3837c478bd9Sstevel@tonic-gate 	 * use kcred to do the dumping.
3847c478bd9Sstevel@tonic-gate 	 */
3857c478bd9Sstevel@tonic-gate 	if (core_type == CORE_GLOBAL || core_type == CORE_ZONE || is_setid) {
3867c478bd9Sstevel@tonic-gate 		/*
3877c478bd9Sstevel@tonic-gate 		 * Use the zone's "kcred" to prevent privilege
3887c478bd9Sstevel@tonic-gate 		 * escalation.
3897c478bd9Sstevel@tonic-gate 		 */
3907c478bd9Sstevel@tonic-gate 		credp = zone_get_kcred(getzoneid());
3917c478bd9Sstevel@tonic-gate 		ASSERT(credp != NULL);
3927c478bd9Sstevel@tonic-gate 		ocredp = set_cred(p, credp);
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	/*
3967c478bd9Sstevel@tonic-gate 	 * First remove any existing core file, then
3977c478bd9Sstevel@tonic-gate 	 * open the new core file with (O_EXCL|O_CREAT).
3987c478bd9Sstevel@tonic-gate 	 *
3997c478bd9Sstevel@tonic-gate 	 * The reasons for doing this are manifold:
4007c478bd9Sstevel@tonic-gate 	 *
4017c478bd9Sstevel@tonic-gate 	 * For security reasons, we don't want root processes
4027c478bd9Sstevel@tonic-gate 	 * to dump core through a symlink because that would
4037c478bd9Sstevel@tonic-gate 	 * allow a malicious user to clobber any file on
40448bbca81SDaniel Hoffman 	 * the system if they could convince a root process,
40548bbca81SDaniel Hoffman 	 * perhaps a set-uid root process that they started,
4067c478bd9Sstevel@tonic-gate 	 * to dump core in a directory writable by that user.
4077c478bd9Sstevel@tonic-gate 	 * Similar security reasons apply to hard links.
4087c478bd9Sstevel@tonic-gate 	 * For symmetry we do this unconditionally, not
4097c478bd9Sstevel@tonic-gate 	 * just for root processes.
4107c478bd9Sstevel@tonic-gate 	 *
4117c478bd9Sstevel@tonic-gate 	 * If the process has the core file mmap()d into the
4127c478bd9Sstevel@tonic-gate 	 * address space, we would be modifying the address
4137c478bd9Sstevel@tonic-gate 	 * space that we are trying to dump if we did not first
4147c478bd9Sstevel@tonic-gate 	 * remove the core file.  (The command "file core"
4157c478bd9Sstevel@tonic-gate 	 * is the canonical example of this possibility.)
4167c478bd9Sstevel@tonic-gate 	 *
4177c478bd9Sstevel@tonic-gate 	 * Opening the core file with O_EXCL|O_CREAT ensures than
4187c478bd9Sstevel@tonic-gate 	 * two concurrent core dumps don't clobber each other.
4197c478bd9Sstevel@tonic-gate 	 * One is bound to lose; we don't want to make both lose.
4207c478bd9Sstevel@tonic-gate 	 */
4217c478bd9Sstevel@tonic-gate 	if ((error = remove_core_file(fp, core_type)) == 0) {
4227c478bd9Sstevel@tonic-gate 		error = create_core_file(fp, core_type, &vp);
4237c478bd9Sstevel@tonic-gate 	}
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	/*
4267c478bd9Sstevel@tonic-gate 	 * Now that vn_open is complete, reset the process's credentials if
4277c478bd9Sstevel@tonic-gate 	 * we changed them, and make 'credp' point to kcred used
4287c478bd9Sstevel@tonic-gate 	 * above.  We use 'credp' to do i/o on the core file below, but leave
4297c478bd9Sstevel@tonic-gate 	 * p->p_cred set to the original credential to allow the core file
4307c478bd9Sstevel@tonic-gate 	 * to record this information.
4317c478bd9Sstevel@tonic-gate 	 */
4327c478bd9Sstevel@tonic-gate 	if (ocredp != NULL)
4337c478bd9Sstevel@tonic-gate 		credp = set_cred(p, ocredp);
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	if (error == 0) {
4367c478bd9Sstevel@tonic-gate 		int closerr;
4377c478bd9Sstevel@tonic-gate #if defined(__sparc)
4387c478bd9Sstevel@tonic-gate 		(void) flush_user_windows_to_stack(NULL);
4397c478bd9Sstevel@tonic-gate #endif
440ae115bc7Smrj 		if ((eswp = PTOU(curproc)->u_execsw) == NULL ||
4417c478bd9Sstevel@tonic-gate 		    (eswp = findexec_by_magic(eswp->exec_magic)) == NULL) {
4427c478bd9Sstevel@tonic-gate 			error = ENOSYS;
4437c478bd9Sstevel@tonic-gate 		} else {
4447c478bd9Sstevel@tonic-gate 			error = eswp->exec_core(vp, p, credp, rlimit, sig,
4457c478bd9Sstevel@tonic-gate 			    content);
4467c478bd9Sstevel@tonic-gate 			rw_exit(eswp->exec_lock);
4477c478bd9Sstevel@tonic-gate 		}
4487c478bd9Sstevel@tonic-gate 
449da6c28aaSamw 		closerr = VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, credp, NULL);
4507c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
4517c478bd9Sstevel@tonic-gate 		if (error == 0)
4527c478bd9Sstevel@tonic-gate 			error = closerr;
4537c478bd9Sstevel@tonic-gate 	}
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	if (ocredp != NULL)
4567c478bd9Sstevel@tonic-gate 		crfree(credp);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	return (error);
4597c478bd9Sstevel@tonic-gate }
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate /*
4627c478bd9Sstevel@tonic-gate  * Convert a core name pattern to a pathname.
4637c478bd9Sstevel@tonic-gate  */
4647c478bd9Sstevel@tonic-gate static int
expand_string(const char * pat,char * fp,int size,cred_t * cr)4657c478bd9Sstevel@tonic-gate expand_string(const char *pat, char *fp, int size, cred_t *cr)
4667c478bd9Sstevel@tonic-gate {
4677c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
4687c478bd9Sstevel@tonic-gate 	char buf[24];
4697c478bd9Sstevel@tonic-gate 	int len, i;
4707c478bd9Sstevel@tonic-gate 	char *s;
4717c478bd9Sstevel@tonic-gate 	char c;
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	while ((c = *pat++) != '\0') {
4747c478bd9Sstevel@tonic-gate 		if (size < 2)
4757c478bd9Sstevel@tonic-gate 			return (ENAMETOOLONG);
4767c478bd9Sstevel@tonic-gate 		if (c != '%') {
4777c478bd9Sstevel@tonic-gate 			size--;
4787c478bd9Sstevel@tonic-gate 			*fp++ = c;
4797c478bd9Sstevel@tonic-gate 			continue;
4807c478bd9Sstevel@tonic-gate 		}
4817c478bd9Sstevel@tonic-gate 		if ((c = *pat++) == '\0') {
4827c478bd9Sstevel@tonic-gate 			size--;
4837c478bd9Sstevel@tonic-gate 			*fp++ = '%';
4847c478bd9Sstevel@tonic-gate 			break;
4857c478bd9Sstevel@tonic-gate 		}
4867c478bd9Sstevel@tonic-gate 		switch (c) {
4877c478bd9Sstevel@tonic-gate 		case 'p':	/* pid */
4887c478bd9Sstevel@tonic-gate 			(void) sprintf((s = buf), "%d", p->p_pid);
4897c478bd9Sstevel@tonic-gate 			break;
4907c478bd9Sstevel@tonic-gate 		case 'u':	/* effective uid */
491f48205beScasper 			(void) sprintf((s = buf), "%u", crgetuid(p->p_cred));
4927c478bd9Sstevel@tonic-gate 			break;
4937c478bd9Sstevel@tonic-gate 		case 'g':	/* effective gid */
494f48205beScasper 			(void) sprintf((s = buf), "%u", crgetgid(p->p_cred));
4957c478bd9Sstevel@tonic-gate 			break;
4967c478bd9Sstevel@tonic-gate 		case 'f':	/* exec'd filename */
4977c478bd9Sstevel@tonic-gate 			s = PTOU(p)->u_comm;
4987c478bd9Sstevel@tonic-gate 			break;
4997c478bd9Sstevel@tonic-gate 		case 'd':	/* exec'd dirname */
5007c478bd9Sstevel@tonic-gate 			/*
5017c478bd9Sstevel@tonic-gate 			 * Even if pathname caching is disabled, we should
5027c478bd9Sstevel@tonic-gate 			 * be able to lookup the pathname for a directory.
5037c478bd9Sstevel@tonic-gate 			 */
5047c478bd9Sstevel@tonic-gate 			if (p->p_execdir != NULL && vnodetopath(NULL,
5057c478bd9Sstevel@tonic-gate 			    p->p_execdir, fp, size, cr) == 0) {
5067c478bd9Sstevel@tonic-gate 				len = (int)strlen(fp);
5077c478bd9Sstevel@tonic-gate 				ASSERT(len < size);
5087c478bd9Sstevel@tonic-gate 				ASSERT(len >= 1);
5097c478bd9Sstevel@tonic-gate 				ASSERT(fp[0] == '/');
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 				/*
5127c478bd9Sstevel@tonic-gate 				 * Strip off the leading slash.
5137c478bd9Sstevel@tonic-gate 				 */
5147c478bd9Sstevel@tonic-gate 				for (i = 0; i < len; i++) {
5157c478bd9Sstevel@tonic-gate 					fp[i] = fp[i + 1];
5167c478bd9Sstevel@tonic-gate 				}
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 				len--;
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 				size -= len;
5217c478bd9Sstevel@tonic-gate 				fp += len;
5227c478bd9Sstevel@tonic-gate 			} else {
5237c478bd9Sstevel@tonic-gate 				*fp = '\0';
5247c478bd9Sstevel@tonic-gate 			}
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 			continue;
5277c478bd9Sstevel@tonic-gate 		case 'n':	/* system nodename */
5287c478bd9Sstevel@tonic-gate 			s = uts_nodename();
5297c478bd9Sstevel@tonic-gate 			break;
5307c478bd9Sstevel@tonic-gate 		case 'm':	/* machine (sun4u, etc) */
5317c478bd9Sstevel@tonic-gate 			s = utsname.machine;
5327c478bd9Sstevel@tonic-gate 			break;
5337c478bd9Sstevel@tonic-gate 		case 't':	/* decimal value of time(2) */
5347c478bd9Sstevel@tonic-gate 			(void) sprintf((s = buf), "%ld", gethrestime_sec());
5357c478bd9Sstevel@tonic-gate 			break;
5367c478bd9Sstevel@tonic-gate 		case 'z':
5377c478bd9Sstevel@tonic-gate 			s = p->p_zone->zone_name;
5387c478bd9Sstevel@tonic-gate 			break;
539e6163a88SJerry Jelinek 		case 'Z':
540e6163a88SJerry Jelinek 			/* This is zonepath + "/root/", except for GZ */
541e6163a88SJerry Jelinek 			s = p->p_zone->zone_rootpath;
542e6163a88SJerry Jelinek 			break;
5437c478bd9Sstevel@tonic-gate 		case '%':
5447c478bd9Sstevel@tonic-gate 			(void) strcpy((s = buf), "%");
5457c478bd9Sstevel@tonic-gate 			break;
5467c478bd9Sstevel@tonic-gate 		default:
5477c478bd9Sstevel@tonic-gate 			s = buf;
5487c478bd9Sstevel@tonic-gate 			buf[0] = '%';
5497c478bd9Sstevel@tonic-gate 			buf[1] = c;
5507c478bd9Sstevel@tonic-gate 			buf[2] = '\0';
5517c478bd9Sstevel@tonic-gate 			break;
5527c478bd9Sstevel@tonic-gate 		}
5537c478bd9Sstevel@tonic-gate 		len = (int)strlen(s);
5547c478bd9Sstevel@tonic-gate 		if ((size -= len) <= 0)
5557c478bd9Sstevel@tonic-gate 			return (ENAMETOOLONG);
5567c478bd9Sstevel@tonic-gate 		(void) strcpy(fp, s);
557e6163a88SJerry Jelinek 		/* strip trailing "/root/" from non-GZ zonepath string */
558e6163a88SJerry Jelinek 		if (c == 'Z' && len > 6) {
559e6163a88SJerry Jelinek 			len -= 6;
560e6163a88SJerry Jelinek 			ASSERT(strncmp(fp + len, "/root/", 6) == 0);
561e6163a88SJerry Jelinek 		}
5627c478bd9Sstevel@tonic-gate 		fp += len;
5637c478bd9Sstevel@tonic-gate 	}
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	*fp = '\0';
5667c478bd9Sstevel@tonic-gate 	return (0);
5677c478bd9Sstevel@tonic-gate }
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate static int
dump_one_core(int sig,rlim64_t rlimit,enum core_types core_type,struct core_globals * cg,char ** name)5707c478bd9Sstevel@tonic-gate dump_one_core(int sig, rlim64_t rlimit, enum core_types core_type,
5717c478bd9Sstevel@tonic-gate     struct core_globals *cg, char **name)
5727c478bd9Sstevel@tonic-gate {
5737c478bd9Sstevel@tonic-gate 	refstr_t *rp;
5747c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
5757c478bd9Sstevel@tonic-gate 	zoneid_t zoneid;
5767c478bd9Sstevel@tonic-gate 	int error;
5777c478bd9Sstevel@tonic-gate 	char *fp;
5787c478bd9Sstevel@tonic-gate 	cred_t *cr;
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	ASSERT(core_type == CORE_ZONE || core_type == CORE_GLOBAL);
5817c478bd9Sstevel@tonic-gate 	zoneid = (core_type == CORE_ZONE ? getzoneid() : GLOBAL_ZONEID);
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	mutex_enter(&cg->core_lock);
5847c478bd9Sstevel@tonic-gate 	if ((rp = cg->core_file) != NULL)
5857c478bd9Sstevel@tonic-gate 		refstr_hold(rp);
5867c478bd9Sstevel@tonic-gate 	mutex_exit(&cg->core_lock);
5877c478bd9Sstevel@tonic-gate 	if (rp == NULL) {
5887c478bd9Sstevel@tonic-gate 		core_log(cg, 0, "no global core file pattern exists", NULL,
5897c478bd9Sstevel@tonic-gate 		    zoneid);
5907c478bd9Sstevel@tonic-gate 		return (1);	/* core file not generated */
5917c478bd9Sstevel@tonic-gate 	}
5927c478bd9Sstevel@tonic-gate 	fp = kmem_alloc(MAXPATHLEN, KM_SLEEP);
5937c478bd9Sstevel@tonic-gate 	cr = zone_get_kcred(getzoneid());
5947c478bd9Sstevel@tonic-gate 	error = expand_string(refstr_value(rp), fp, MAXPATHLEN, cr);
5957c478bd9Sstevel@tonic-gate 	crfree(cr);
5967c478bd9Sstevel@tonic-gate 	if (error != 0) {
5977c478bd9Sstevel@tonic-gate 		core_log(cg, 0, "global core file pattern too long",
5987c478bd9Sstevel@tonic-gate 		    refstr_value(rp), zoneid);
5997c478bd9Sstevel@tonic-gate 	} else if ((error = do_core(fp, sig, core_type, cg)) == 0) {
6007c478bd9Sstevel@tonic-gate 		core_log(cg, 0, "core dumped", fp, zoneid);
6017c478bd9Sstevel@tonic-gate 	} else if (error == ENOTSUP) {
6027c478bd9Sstevel@tonic-gate 		core_log(cg, 0, "setid process, core not dumped", fp, zoneid);
6037c478bd9Sstevel@tonic-gate 	} else if (error == ENOSPC) {
6047c478bd9Sstevel@tonic-gate 		core_log(cg, 0, "no space left on device, core truncated",
6057c478bd9Sstevel@tonic-gate 		    fp, zoneid);
6067c478bd9Sstevel@tonic-gate 	} else if (error == EFBIG) {
6077c478bd9Sstevel@tonic-gate 		if (rlimit == 0)
6087c478bd9Sstevel@tonic-gate 			core_log(cg, 0, "core rlimit is zero, core not dumped",
6097c478bd9Sstevel@tonic-gate 			    fp, zoneid);
6107c478bd9Sstevel@tonic-gate 		else
6117c478bd9Sstevel@tonic-gate 			core_log(cg, 0, "core rlimit exceeded, core truncated",
6127c478bd9Sstevel@tonic-gate 			    fp, zoneid);
6137c478bd9Sstevel@tonic-gate 		/*
6147c478bd9Sstevel@tonic-gate 		 * In addition to the core result logging, we
6157c478bd9Sstevel@tonic-gate 		 * may also have explicit actions defined on
6167c478bd9Sstevel@tonic-gate 		 * core file size violations via the resource
6177c478bd9Sstevel@tonic-gate 		 * control framework.
6187c478bd9Sstevel@tonic-gate 		 */
6197c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
6207c478bd9Sstevel@tonic-gate 		(void) rctl_action(rctlproc_legacy[RLIMIT_CORE],
6217c478bd9Sstevel@tonic-gate 		    p->p_rctls, p, RCA_SAFE);
6227c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
6237c478bd9Sstevel@tonic-gate 	} else {
6247c478bd9Sstevel@tonic-gate 		core_log(cg, error, "core dump failed", fp, zoneid);
6257c478bd9Sstevel@tonic-gate 	}
6267c478bd9Sstevel@tonic-gate 	refstr_rele(rp);
6277c478bd9Sstevel@tonic-gate 	if (name != NULL)
6287c478bd9Sstevel@tonic-gate 		*name = fp;
6297c478bd9Sstevel@tonic-gate 	else
6307c478bd9Sstevel@tonic-gate 		kmem_free(fp, MAXPATHLEN);
6317c478bd9Sstevel@tonic-gate 	return (error);
6327c478bd9Sstevel@tonic-gate }
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate int
core(int sig,int ext)6357c478bd9Sstevel@tonic-gate core(int sig, int ext)
6367c478bd9Sstevel@tonic-gate {
6377c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
6387c478bd9Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
6397c478bd9Sstevel@tonic-gate 	refstr_t *rp;
6407c478bd9Sstevel@tonic-gate 	char *fp_process = NULL, *fp_global = NULL, *fp_zone = NULL;
6417c478bd9Sstevel@tonic-gate 	int error1 = 1;
6427c478bd9Sstevel@tonic-gate 	int error2 = 1;
6437c478bd9Sstevel@tonic-gate 	int error3 = 1;
6447c478bd9Sstevel@tonic-gate 	k_sigset_t sigmask;
6457c478bd9Sstevel@tonic-gate 	k_sigset_t sighold;
6467c478bd9Sstevel@tonic-gate 	rlim64_t rlimit;
6477c478bd9Sstevel@tonic-gate 	struct core_globals *my_cg, *global_cg;
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	global_cg = zone_getspecific(core_zone_key, global_zone);
6507c478bd9Sstevel@tonic-gate 	ASSERT(global_cg != NULL);
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	my_cg = zone_getspecific(core_zone_key, curproc->p_zone);
6537c478bd9Sstevel@tonic-gate 	ASSERT(my_cg != NULL);
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	/* core files suppressed? */
6567c478bd9Sstevel@tonic-gate 	if (!(my_cg->core_options & (CC_PROCESS_PATH|CC_GLOBAL_PATH)) &&
6577c478bd9Sstevel@tonic-gate 	    !(global_cg->core_options & CC_GLOBAL_PATH)) {
6587c478bd9Sstevel@tonic-gate 		if (!ext && p->p_ct_process != NULL)
6597c478bd9Sstevel@tonic-gate 			contract_process_core(p->p_ct_process, p, sig,
6607c478bd9Sstevel@tonic-gate 			    NULL, NULL, NULL);
6617c478bd9Sstevel@tonic-gate 		return (1);
6627c478bd9Sstevel@tonic-gate 	}
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	/*
665f971a346SBryan Cantrill 	 * Block all signals except SIGHUP, SIGINT, SIGKILL, and SIGTERM; no
666f971a346SBryan Cantrill 	 * other signal may interrupt a core dump.  For each signal, we
667f971a346SBryan Cantrill 	 * explicitly unblock it and set it in p_siginfo to allow for some
668f971a346SBryan Cantrill 	 * minimal error reporting.  Additionally, we get the current limit on
669f971a346SBryan Cantrill 	 * core file size for handling later error reporting.
6707c478bd9Sstevel@tonic-gate 	 */
6717c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	p->p_flag |= SDOCORE;
6747c478bd9Sstevel@tonic-gate 	schedctl_finish_sigblock(curthread);
6757c478bd9Sstevel@tonic-gate 	sigmask = curthread->t_hold;	/* remember for later */
6767c478bd9Sstevel@tonic-gate 	sigfillset(&sighold);
6777c478bd9Sstevel@tonic-gate 	if (!sigismember(&sigmask, SIGHUP))
6787c478bd9Sstevel@tonic-gate 		sigdelset(&sighold, SIGHUP);
6797c478bd9Sstevel@tonic-gate 	if (!sigismember(&sigmask, SIGINT))
6807c478bd9Sstevel@tonic-gate 		sigdelset(&sighold, SIGINT);
6817c478bd9Sstevel@tonic-gate 	if (!sigismember(&sigmask, SIGKILL))
6827c478bd9Sstevel@tonic-gate 		sigdelset(&sighold, SIGKILL);
6837c478bd9Sstevel@tonic-gate 	if (!sigismember(&sigmask, SIGTERM))
6847c478bd9Sstevel@tonic-gate 		sigdelset(&sighold, SIGTERM);
685f971a346SBryan Cantrill 
686f971a346SBryan Cantrill 	sigaddset(&p->p_siginfo, SIGHUP);
687f971a346SBryan Cantrill 	sigaddset(&p->p_siginfo, SIGINT);
688f971a346SBryan Cantrill 	sigaddset(&p->p_siginfo, SIGKILL);
689f971a346SBryan Cantrill 	sigaddset(&p->p_siginfo, SIGTERM);
690f971a346SBryan Cantrill 
6917c478bd9Sstevel@tonic-gate 	curthread->t_hold = sighold;
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	rlimit = rctl_enforced_value(rctlproc_legacy[RLIMIT_CORE], p->p_rctls,
6947c478bd9Sstevel@tonic-gate 	    p);
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate 	/*
6997c478bd9Sstevel@tonic-gate 	 * Undo any watchpoints.
7007c478bd9Sstevel@tonic-gate 	 */
7017c478bd9Sstevel@tonic-gate 	pr_free_watched_pages(p);
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 	/*
7047c478bd9Sstevel@tonic-gate 	 * The presence of a current signal prevents file i/o
7057c478bd9Sstevel@tonic-gate 	 * from succeeding over a network.  We copy the current
7067c478bd9Sstevel@tonic-gate 	 * signal information to the side and cancel the current
7077c478bd9Sstevel@tonic-gate 	 * signal so that the core dump will succeed.
7087c478bd9Sstevel@tonic-gate 	 */
7097c478bd9Sstevel@tonic-gate 	ASSERT(lwp->lwp_cursig == sig);
7107c478bd9Sstevel@tonic-gate 	lwp->lwp_cursig = 0;
7117c478bd9Sstevel@tonic-gate 	lwp->lwp_extsig = 0;
712ee01d137Sraf 	if (lwp->lwp_curinfo == NULL) {
7137c478bd9Sstevel@tonic-gate 		bzero(&lwp->lwp_siginfo, sizeof (k_siginfo_t));
714ee01d137Sraf 		lwp->lwp_siginfo.si_signo = sig;
715ee01d137Sraf 		lwp->lwp_siginfo.si_code = SI_NOINFO;
716ee01d137Sraf 	} else {
7177c478bd9Sstevel@tonic-gate 		bcopy(&lwp->lwp_curinfo->sq_info,
7187c478bd9Sstevel@tonic-gate 		    &lwp->lwp_siginfo, sizeof (k_siginfo_t));
7197c478bd9Sstevel@tonic-gate 		siginfofree(lwp->lwp_curinfo);
7207c478bd9Sstevel@tonic-gate 		lwp->lwp_curinfo = NULL;
7217c478bd9Sstevel@tonic-gate 	}
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 	/*
7247c478bd9Sstevel@tonic-gate 	 * Convert the core file name patterns into path names
7257c478bd9Sstevel@tonic-gate 	 * and call do_core() to write the core files.
7267c478bd9Sstevel@tonic-gate 	 */
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 	if (my_cg->core_options & CC_PROCESS_PATH) {
7297c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
7307c478bd9Sstevel@tonic-gate 		if (p->p_corefile != NULL)
7317c478bd9Sstevel@tonic-gate 			rp = corectl_path_value(p->p_corefile);
7327c478bd9Sstevel@tonic-gate 		else
7337c478bd9Sstevel@tonic-gate 			rp = NULL;
7347c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
7357c478bd9Sstevel@tonic-gate 		if (rp != NULL) {
7367c478bd9Sstevel@tonic-gate 			fp_process = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7377c478bd9Sstevel@tonic-gate 			error1 = expand_string(refstr_value(rp),
738f8860408SJohn Harres 			    fp_process, MAXPATHLEN, p->p_cred);
7397c478bd9Sstevel@tonic-gate 			if (error1 == 0)
7407c478bd9Sstevel@tonic-gate 				error1 = do_core(fp_process, sig, CORE_PROC,
7417c478bd9Sstevel@tonic-gate 				    my_cg);
7427c478bd9Sstevel@tonic-gate 			refstr_rele(rp);
7437c478bd9Sstevel@tonic-gate 		}
7447c478bd9Sstevel@tonic-gate 	}
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 	if (my_cg->core_options & CC_GLOBAL_PATH)
7477c478bd9Sstevel@tonic-gate 		error2 = dump_one_core(sig, rlimit, CORE_ZONE, my_cg,
7487c478bd9Sstevel@tonic-gate 		    &fp_global);
7497c478bd9Sstevel@tonic-gate 	if (global_cg != my_cg && (global_cg->core_options & CC_GLOBAL_PATH))
7507c478bd9Sstevel@tonic-gate 		error3 = dump_one_core(sig, rlimit, CORE_GLOBAL, global_cg,
7517c478bd9Sstevel@tonic-gate 		    &fp_zone);
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 	/*
7547c478bd9Sstevel@tonic-gate 	 * Restore the signal hold mask.
7557c478bd9Sstevel@tonic-gate 	 */
7567c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
7577c478bd9Sstevel@tonic-gate 	curthread->t_hold = sigmask;
7587c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 	if (!ext && p->p_ct_process != NULL)
7617c478bd9Sstevel@tonic-gate 		contract_process_core(p->p_ct_process, p, sig,
7627c478bd9Sstevel@tonic-gate 		    error1 == 0 ? fp_process : NULL,
7637c478bd9Sstevel@tonic-gate 		    error2 == 0 ? fp_global : NULL,
7647c478bd9Sstevel@tonic-gate 		    error3 == 0 ? fp_zone : NULL);
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	if (fp_process != NULL)
7677c478bd9Sstevel@tonic-gate 		kmem_free(fp_process, MAXPATHLEN);
7687c478bd9Sstevel@tonic-gate 	if (fp_global != NULL)
7697c478bd9Sstevel@tonic-gate 		kmem_free(fp_global, MAXPATHLEN);
7707c478bd9Sstevel@tonic-gate 	if (fp_zone != NULL)
7717c478bd9Sstevel@tonic-gate 		kmem_free(fp_zone, MAXPATHLEN);
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	/*
7747c478bd9Sstevel@tonic-gate 	 * Return non-zero if no core file was created.
7757c478bd9Sstevel@tonic-gate 	 */
7767c478bd9Sstevel@tonic-gate 	return (error1 != 0 && error2 != 0 && error3 != 0);
7777c478bd9Sstevel@tonic-gate }
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate /*
7807c478bd9Sstevel@tonic-gate  * Maximum chunk size for dumping core files,
7817c478bd9Sstevel@tonic-gate  * size in pages, patchable in /etc/system
7827c478bd9Sstevel@tonic-gate  */
7837c478bd9Sstevel@tonic-gate uint_t	core_chunk = 32;
7847c478bd9Sstevel@tonic-gate 
785f8860408SJohn Harres /*
786f8860408SJohn Harres  * The delay between core_write() calls, in microseconds.  The default
787f8860408SJohn Harres  * matches one "normal" clock tick, or 10 milliseconds.
788f8860408SJohn Harres  */
789f8860408SJohn Harres clock_t	core_delay_usec = 10000;
790f8860408SJohn Harres 
7917c478bd9Sstevel@tonic-gate /*
7927c478bd9Sstevel@tonic-gate  * Common code to core dump process memory.  The core_seg routine does i/o
7937c478bd9Sstevel@tonic-gate  * using core_write() below, and so it has the same failure semantics.
7947c478bd9Sstevel@tonic-gate  */
7957c478bd9Sstevel@tonic-gate int
core_seg(proc_t * p,vnode_t * vp,u_offset_t offset,caddr_t addr,size_t size,rlim64_t rlimit,cred_t * credp)796*4e18e297SPatrick Mooney core_seg(proc_t *p, vnode_t *vp, u_offset_t offset, caddr_t addr, size_t size,
7977c478bd9Sstevel@tonic-gate     rlim64_t rlimit, cred_t *credp)
7987c478bd9Sstevel@tonic-gate {
7997c478bd9Sstevel@tonic-gate 	caddr_t eaddr;
8007c478bd9Sstevel@tonic-gate 	caddr_t base;
8017c478bd9Sstevel@tonic-gate 	size_t len;
8027c478bd9Sstevel@tonic-gate 	int err = 0;
8037c478bd9Sstevel@tonic-gate 
804*4e18e297SPatrick Mooney 	if (offset > OFF_MAX || offset + size > OFF_MAX ||
805*4e18e297SPatrick Mooney 	    offset + size < offset) {
806*4e18e297SPatrick Mooney 		return (EOVERFLOW);
807*4e18e297SPatrick Mooney 	}
808*4e18e297SPatrick Mooney 
8097c478bd9Sstevel@tonic-gate 	eaddr = addr + size;
8107c478bd9Sstevel@tonic-gate 	for (base = addr; base < eaddr; base += len) {
8117c478bd9Sstevel@tonic-gate 		len = eaddr - base;
8127c478bd9Sstevel@tonic-gate 		if (as_memory(p->p_as, &base, &len) != 0)
8137c478bd9Sstevel@tonic-gate 			return (0);
814f971a346SBryan Cantrill 
8157c478bd9Sstevel@tonic-gate 		/*
8167c478bd9Sstevel@tonic-gate 		 * Reduce len to a reasonable value so that we don't
8177c478bd9Sstevel@tonic-gate 		 * overwhelm the VM system with a monstrously large
8187c478bd9Sstevel@tonic-gate 		 * single write and cause pageout to stop running.
8197c478bd9Sstevel@tonic-gate 		 */
8207c478bd9Sstevel@tonic-gate 		if (len > (size_t)core_chunk * PAGESIZE)
821f8860408SJohn Harres 			len = (size_t)core_chunk * PAGESIZE;
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 		err = core_write(vp, UIO_USERSPACE,
8247c478bd9Sstevel@tonic-gate 		    offset + (size_t)(base - addr), base, len, rlimit, credp);
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 		if (err)
8277c478bd9Sstevel@tonic-gate 			return (err);
828f971a346SBryan Cantrill 
829f971a346SBryan Cantrill 		/*
830f971a346SBryan Cantrill 		 * If we have taken a signal, return EINTR to allow the dump
831f971a346SBryan Cantrill 		 * to be aborted.
832f971a346SBryan Cantrill 		 */
833f971a346SBryan Cantrill 		if (issig(JUSTLOOKING) && issig(FORREAL))
834f971a346SBryan Cantrill 			return (EINTR);
8357c478bd9Sstevel@tonic-gate 	}
836f971a346SBryan Cantrill 
8377c478bd9Sstevel@tonic-gate 	return (0);
8387c478bd9Sstevel@tonic-gate }
8397c478bd9Sstevel@tonic-gate 
8407c478bd9Sstevel@tonic-gate /*
8417c478bd9Sstevel@tonic-gate  * Wrapper around vn_rdwr to perform writes to a core file.  For core files,
8427c478bd9Sstevel@tonic-gate  * we always want to write as much as we possibly can, and then make sure to
8437c478bd9Sstevel@tonic-gate  * return either 0 to the caller (for success), or the actual errno value.
8447c478bd9Sstevel@tonic-gate  * By using this function, the caller can omit additional code for handling
8457c478bd9Sstevel@tonic-gate  * retries and errors for partial writes returned by vn_rdwr.  If vn_rdwr
8467c478bd9Sstevel@tonic-gate  * unexpectedly returns zero but no progress has been made, we return ENOSPC.
8477c478bd9Sstevel@tonic-gate  */
8487c478bd9Sstevel@tonic-gate int
core_write(vnode_t * vp,enum uio_seg segflg,u_offset_t offset,const void * buf,size_t len,rlim64_t rlimit,cred_t * credp)849*4e18e297SPatrick Mooney core_write(vnode_t *vp, enum uio_seg segflg, u_offset_t offset,
8507c478bd9Sstevel@tonic-gate     const void *buf, size_t len, rlim64_t rlimit, cred_t *credp)
8517c478bd9Sstevel@tonic-gate {
8527c478bd9Sstevel@tonic-gate 	ssize_t resid = len;
8537c478bd9Sstevel@tonic-gate 	int error = 0;
8547c478bd9Sstevel@tonic-gate 
855*4e18e297SPatrick Mooney 	if (offset > OFF_MAX || offset + len > OFF_MAX ||
856*4e18e297SPatrick Mooney 	    offset + len < offset) {
857*4e18e297SPatrick Mooney 		return (EOVERFLOW);
858*4e18e297SPatrick Mooney 	}
859*4e18e297SPatrick Mooney 
8607c478bd9Sstevel@tonic-gate 	while (len != 0) {
861*4e18e297SPatrick Mooney 		error = vn_rdwr(UIO_WRITE, vp, (caddr_t)buf, len,
862*4e18e297SPatrick Mooney 		    (offset_t)offset, segflg, 0, rlimit, credp, &resid);
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 		if (error != 0)
8657c478bd9Sstevel@tonic-gate 			break;
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 		if (resid >= len)
8687c478bd9Sstevel@tonic-gate 			return (ENOSPC);
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 		buf = (const char *)buf + len - resid;
8717c478bd9Sstevel@tonic-gate 		offset += len - resid;
8727c478bd9Sstevel@tonic-gate 		len = resid;
8737c478bd9Sstevel@tonic-gate 	}
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	return (error);
8767c478bd9Sstevel@tonic-gate }
877