xref: /onnv-gate/usr/src/uts/common/os/core.c (revision 12789:82cffaae72d5)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
214626Sraf 
220Sstevel@tonic-gate /*
23*12789SRoger.Faulkner@Oracle.COM  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/param.h>
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/time.h>
320Sstevel@tonic-gate #include <sys/sysmacros.h>
330Sstevel@tonic-gate #include <sys/proc.h>
340Sstevel@tonic-gate #include <sys/systm.h>
350Sstevel@tonic-gate #include <sys/cred.h>
360Sstevel@tonic-gate #include <sys/user.h>
370Sstevel@tonic-gate #include <sys/utsname.h>
380Sstevel@tonic-gate #include <sys/errno.h>
390Sstevel@tonic-gate #include <sys/signal.h>
400Sstevel@tonic-gate #include <sys/siginfo.h>
410Sstevel@tonic-gate #include <sys/fault.h>
420Sstevel@tonic-gate #include <sys/syscall.h>
430Sstevel@tonic-gate #include <sys/ucontext.h>
440Sstevel@tonic-gate #include <sys/prsystm.h>
450Sstevel@tonic-gate #include <sys/vnode.h>
460Sstevel@tonic-gate #include <sys/var.h>
470Sstevel@tonic-gate #include <sys/file.h>
480Sstevel@tonic-gate #include <sys/pathname.h>
490Sstevel@tonic-gate #include <sys/vfs.h>
500Sstevel@tonic-gate #include <sys/exec.h>
510Sstevel@tonic-gate #include <sys/debug.h>
520Sstevel@tonic-gate #include <sys/stack.h>
530Sstevel@tonic-gate #include <sys/kmem.h>
540Sstevel@tonic-gate #include <sys/schedctl.h>
550Sstevel@tonic-gate #include <sys/core.h>
560Sstevel@tonic-gate #include <sys/corectl.h>
570Sstevel@tonic-gate #include <sys/cmn_err.h>
580Sstevel@tonic-gate #include <vm/as.h>
590Sstevel@tonic-gate #include <sys/rctl.h>
600Sstevel@tonic-gate #include <sys/nbmlock.h>
610Sstevel@tonic-gate #include <sys/stat.h>
620Sstevel@tonic-gate #include <sys/zone.h>
630Sstevel@tonic-gate #include <sys/contract/process_impl.h>
648359SJohn.Harres@Sun.COM #include <sys/ddi.h>
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /*
670Sstevel@tonic-gate  * Processes running within a zone potentially dump core in 3 locations,
680Sstevel@tonic-gate  * based on the per-process, per-zone, and the global zone's core settings.
690Sstevel@tonic-gate  *
700Sstevel@tonic-gate  * Per-zone and global zone settings are often referred to as "global"
710Sstevel@tonic-gate  * settings since they apply to the system (or zone) as a whole, as
720Sstevel@tonic-gate  * opposed to a particular process.
730Sstevel@tonic-gate  */
740Sstevel@tonic-gate enum core_types {
750Sstevel@tonic-gate 	CORE_PROC,	/* Use per-process settings */
760Sstevel@tonic-gate 	CORE_ZONE,	/* Use per-zone settings */
770Sstevel@tonic-gate 	CORE_GLOBAL	/* Use global zone settings */
780Sstevel@tonic-gate };
790Sstevel@tonic-gate 
800Sstevel@tonic-gate /*
810Sstevel@tonic-gate  * Log information about "global" core dumps to syslog.
820Sstevel@tonic-gate  */
830Sstevel@tonic-gate static void
core_log(struct core_globals * cg,int error,const char * why,const char * path,zoneid_t zoneid)840Sstevel@tonic-gate core_log(struct core_globals *cg, int error, const char *why, const char *path,
850Sstevel@tonic-gate     zoneid_t zoneid)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate 	proc_t *p = curproc;
880Sstevel@tonic-gate 	pid_t pid = p->p_pid;
890Sstevel@tonic-gate 	char *fn = PTOU(p)->u_comm;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	if (!(cg->core_options & CC_GLOBAL_LOG))
920Sstevel@tonic-gate 		return;
930Sstevel@tonic-gate 
940Sstevel@tonic-gate 	if (path == NULL)
950Sstevel@tonic-gate 		zcmn_err(zoneid, CE_NOTE, "core_log: %s[%d] %s", fn, pid, why);
960Sstevel@tonic-gate 	else if (error == 0)
970Sstevel@tonic-gate 		zcmn_err(zoneid, CE_NOTE, "core_log: %s[%d] %s: %s", fn, pid,
980Sstevel@tonic-gate 		    why, path);
990Sstevel@tonic-gate 	else
1000Sstevel@tonic-gate 		zcmn_err(zoneid, CE_NOTE, "core_log: %s[%d] %s, errno=%d: %s",
1010Sstevel@tonic-gate 		    fn, pid, why, error, path);
1020Sstevel@tonic-gate }
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate /*
1050Sstevel@tonic-gate  * Private version of vn_remove().
1060Sstevel@tonic-gate  * Refuse to unlink a directory or an unwritable file.
1070Sstevel@tonic-gate  * Also allow the process to access files normally inaccessible due to
1080Sstevel@tonic-gate  * chroot(2) or Zone limitations.
1090Sstevel@tonic-gate  */
1100Sstevel@tonic-gate static int
remove_core_file(char * fp,enum core_types core_type)1110Sstevel@tonic-gate remove_core_file(char *fp, enum core_types core_type)
1120Sstevel@tonic-gate {
1130Sstevel@tonic-gate 	vnode_t *vp = NULL;		/* entry vnode */
1140Sstevel@tonic-gate 	vnode_t *dvp;			/* ptr to parent dir vnode */
1150Sstevel@tonic-gate 	vfs_t *dvfsp;
1160Sstevel@tonic-gate 	int error;
1170Sstevel@tonic-gate 	int in_crit = 0;
1180Sstevel@tonic-gate 	pathname_t pn;			/* name of entry */
1190Sstevel@tonic-gate 	vnode_t *startvp, *rootvp;
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	if ((error = pn_get(fp, UIO_SYSSPACE, &pn)) != 0)
1220Sstevel@tonic-gate 		return (error);
1230Sstevel@tonic-gate 	/*
1240Sstevel@tonic-gate 	 * Determine what rootvp to use.
1250Sstevel@tonic-gate 	 */
1260Sstevel@tonic-gate 	if (core_type == CORE_PROC) {
1270Sstevel@tonic-gate 		rootvp = (PTOU(curproc)->u_rdir == NULL ?
1280Sstevel@tonic-gate 		    curproc->p_zone->zone_rootvp : PTOU(curproc)->u_rdir);
1290Sstevel@tonic-gate 		startvp = (fp[0] == '/' ? rootvp : PTOU(curproc)->u_cdir);
1300Sstevel@tonic-gate 	} else if (core_type == CORE_ZONE) {
1310Sstevel@tonic-gate 		startvp = curproc->p_zone->zone_rootvp;
1320Sstevel@tonic-gate 		rootvp = curproc->p_zone->zone_rootvp;
1330Sstevel@tonic-gate 	} else {
1340Sstevel@tonic-gate 		ASSERT(core_type == CORE_GLOBAL);
1350Sstevel@tonic-gate 		startvp = rootdir;
1360Sstevel@tonic-gate 		rootvp = rootdir;
1370Sstevel@tonic-gate 	}
1380Sstevel@tonic-gate 	VN_HOLD(startvp);
1390Sstevel@tonic-gate 	if (rootvp != rootdir)
1400Sstevel@tonic-gate 		VN_HOLD(rootvp);
1410Sstevel@tonic-gate 	if ((error = lookuppnvp(&pn, NULL, NO_FOLLOW, &dvp, &vp, rootvp,
1420Sstevel@tonic-gate 	    startvp, CRED())) != 0) {
1430Sstevel@tonic-gate 		pn_free(&pn);
1440Sstevel@tonic-gate 		return (error);
1450Sstevel@tonic-gate 	}
1460Sstevel@tonic-gate 	/*
1470Sstevel@tonic-gate 	 * Succeed if there is no file.
1480Sstevel@tonic-gate 	 * Fail if the file is not a regular file.
1490Sstevel@tonic-gate 	 * Fail if the filesystem is mounted read-only.
1500Sstevel@tonic-gate 	 * Fail if the file is not writeable.
1510Sstevel@tonic-gate 	 * Fail if the file has NBMAND share reservations.
1520Sstevel@tonic-gate 	 */
1530Sstevel@tonic-gate 	if (vp == NULL)
1540Sstevel@tonic-gate 		error = 0;
1550Sstevel@tonic-gate 	else if (vp->v_type != VREG)
1560Sstevel@tonic-gate 		error = EACCES;
1570Sstevel@tonic-gate 	else if ((dvfsp = dvp->v_vfsp) != NULL &&
1580Sstevel@tonic-gate 	    (dvfsp->vfs_flag & VFS_RDONLY))
1590Sstevel@tonic-gate 		error = EROFS;
1605331Samw 	else if ((error = VOP_ACCESS(vp, VWRITE, 0, CRED(), NULL)) == 0) {
1610Sstevel@tonic-gate 		if (nbl_need_check(vp)) {
1620Sstevel@tonic-gate 			nbl_start_crit(vp, RW_READER);
1630Sstevel@tonic-gate 			in_crit = 1;
1645331Samw 			if (nbl_share_conflict(vp, NBL_REMOVE, NULL)) {
1650Sstevel@tonic-gate 				error = EACCES;
1660Sstevel@tonic-gate 			}
1670Sstevel@tonic-gate 		}
1680Sstevel@tonic-gate 		if (!error) {
1695331Samw 			error = VOP_REMOVE(dvp, pn.pn_path, CRED(), NULL, 0);
1700Sstevel@tonic-gate 		}
1710Sstevel@tonic-gate 	}
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	pn_free(&pn);
1740Sstevel@tonic-gate 	if (vp != NULL) {
1750Sstevel@tonic-gate 		if (in_crit)
1760Sstevel@tonic-gate 			nbl_end_crit(vp);
1770Sstevel@tonic-gate 		VN_RELE(vp);
1780Sstevel@tonic-gate 	}
1790Sstevel@tonic-gate 	VN_RELE(dvp);
1800Sstevel@tonic-gate 	return (error);
1810Sstevel@tonic-gate }
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /*
1840Sstevel@tonic-gate  * Create the core file in a location that may be normally inaccessible due
1850Sstevel@tonic-gate  * to chroot(2) or Zone limitations.
1860Sstevel@tonic-gate  */
1870Sstevel@tonic-gate static int
create_core_file(char * fp,enum core_types core_type,vnode_t ** vpp)1880Sstevel@tonic-gate create_core_file(char *fp, enum core_types core_type, vnode_t **vpp)
1890Sstevel@tonic-gate {
1900Sstevel@tonic-gate 	int error;
1910Sstevel@tonic-gate 	mode_t perms = (S_IRUSR | S_IWUSR);
1920Sstevel@tonic-gate 	pathname_t pn;
1930Sstevel@tonic-gate 	char *file;
1940Sstevel@tonic-gate 	vnode_t *vp;
1950Sstevel@tonic-gate 	vnode_t *dvp;
1960Sstevel@tonic-gate 	vattr_t vattr;
1970Sstevel@tonic-gate 	cred_t *credp = CRED();
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	if (core_type == CORE_PROC) {
2000Sstevel@tonic-gate 		file = fp;
2010Sstevel@tonic-gate 		dvp = NULL;	/* regular lookup */
2020Sstevel@tonic-gate 	} else {
2030Sstevel@tonic-gate 		vnode_t *startvp, *rootvp;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 		ASSERT(core_type == CORE_ZONE || core_type == CORE_GLOBAL);
2060Sstevel@tonic-gate 		/*
2070Sstevel@tonic-gate 		 * This is tricky because we want to dump the core in
2080Sstevel@tonic-gate 		 * a location which may normally be inaccessible
2090Sstevel@tonic-gate 		 * to us (due to chroot(2) limitations, or zone
2100Sstevel@tonic-gate 		 * membership), and hence need to overcome u_rdir
2110Sstevel@tonic-gate 		 * restrictions.  The basic idea is to separate
2120Sstevel@tonic-gate 		 * the path from the filename, lookup the
2130Sstevel@tonic-gate 		 * pathname separately (starting from the global
2140Sstevel@tonic-gate 		 * zone's root directory), and then open the
2150Sstevel@tonic-gate 		 * file starting at the directory vnode.
2160Sstevel@tonic-gate 		 */
2170Sstevel@tonic-gate 		if (error = pn_get(fp, UIO_SYSSPACE, &pn))
2180Sstevel@tonic-gate 			return (error);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 		if (core_type == CORE_ZONE) {
2210Sstevel@tonic-gate 			startvp = rootvp = curproc->p_zone->zone_rootvp;
2220Sstevel@tonic-gate 		} else {
2230Sstevel@tonic-gate 			startvp = rootvp = rootdir;
2240Sstevel@tonic-gate 		}
2250Sstevel@tonic-gate 		/*
2260Sstevel@tonic-gate 		 * rootvp and startvp will be VN_RELE()'d by lookuppnvp() if
2270Sstevel@tonic-gate 		 * necessary.
2280Sstevel@tonic-gate 		 */
2290Sstevel@tonic-gate 		VN_HOLD(startvp);
2300Sstevel@tonic-gate 		if (rootvp != rootdir)
2310Sstevel@tonic-gate 			VN_HOLD(rootvp);
2320Sstevel@tonic-gate 		/*
2330Sstevel@tonic-gate 		 * Do a lookup on the full path, ignoring the actual file, but
2340Sstevel@tonic-gate 		 * finding the vnode for the directory.  It's OK if the file
2350Sstevel@tonic-gate 		 * doesn't exist -- it most likely won't since we just removed
2360Sstevel@tonic-gate 		 * it.
2370Sstevel@tonic-gate 		 */
2380Sstevel@tonic-gate 		error = lookuppnvp(&pn, NULL, FOLLOW, &dvp, NULLVPP,
2390Sstevel@tonic-gate 		    rootvp, startvp, credp);
2400Sstevel@tonic-gate 		pn_free(&pn);
2410Sstevel@tonic-gate 		if (error != 0)
2420Sstevel@tonic-gate 			return (error);
2430Sstevel@tonic-gate 		ASSERT(dvp != NULL);
2440Sstevel@tonic-gate 		/*
2450Sstevel@tonic-gate 		 * Now find the final component in the path (ie, the name of
2460Sstevel@tonic-gate 		 * the core file).
2470Sstevel@tonic-gate 		 */
2480Sstevel@tonic-gate 		if (error = pn_get(fp, UIO_SYSSPACE, &pn)) {
2490Sstevel@tonic-gate 			VN_RELE(dvp);
2500Sstevel@tonic-gate 			return (error);
2510Sstevel@tonic-gate 		}
2520Sstevel@tonic-gate 		pn_setlast(&pn);
2530Sstevel@tonic-gate 		file = pn.pn_path;
2540Sstevel@tonic-gate 	}
2555331Samw 	error =  vn_openat(file, UIO_SYSSPACE,
2565331Samw 	    FWRITE | FTRUNC | FEXCL | FCREAT | FOFFMAX,
2575331Samw 	    perms, &vp, CRCREAT, PTOU(curproc)->u_cmask, dvp, -1);
2580Sstevel@tonic-gate 	if (core_type != CORE_PROC) {
2590Sstevel@tonic-gate 		VN_RELE(dvp);
2600Sstevel@tonic-gate 		pn_free(&pn);
2610Sstevel@tonic-gate 	}
2620Sstevel@tonic-gate 	/*
2630Sstevel@tonic-gate 	 * Don't dump a core file owned by "nobody".
2640Sstevel@tonic-gate 	 */
2650Sstevel@tonic-gate 	vattr.va_mask = AT_UID;
2660Sstevel@tonic-gate 	if (error == 0 &&
2675331Samw 	    (VOP_GETATTR(vp, &vattr, 0, credp, NULL) != 0 ||
2680Sstevel@tonic-gate 	    vattr.va_uid != crgetuid(credp))) {
2690Sstevel@tonic-gate 		(void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0,
2705331Samw 		    credp, NULL);
2710Sstevel@tonic-gate 		VN_RELE(vp);
2720Sstevel@tonic-gate 		(void) remove_core_file(fp, core_type);
2730Sstevel@tonic-gate 		error = EACCES;
2740Sstevel@tonic-gate 	}
2750Sstevel@tonic-gate 	*vpp = vp;
2760Sstevel@tonic-gate 	return (error);
2770Sstevel@tonic-gate }
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate /*
2800Sstevel@tonic-gate  * Install the specified held cred into the process, and return a pointer to
2810Sstevel@tonic-gate  * the held cred which was previously the value of p->p_cred.
2820Sstevel@tonic-gate  */
2830Sstevel@tonic-gate static cred_t *
set_cred(proc_t * p,cred_t * newcr)2840Sstevel@tonic-gate set_cred(proc_t *p, cred_t *newcr)
2850Sstevel@tonic-gate {
2860Sstevel@tonic-gate 	cred_t *oldcr;
2870Sstevel@tonic-gate 	uid_t olduid, newuid;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	/*
2900Sstevel@tonic-gate 	 * Place a hold on the existing cred, and then install the new
2910Sstevel@tonic-gate 	 * cred into the proc structure.
2920Sstevel@tonic-gate 	 */
2930Sstevel@tonic-gate 	mutex_enter(&p->p_crlock);
2940Sstevel@tonic-gate 	oldcr = p->p_cred;
2950Sstevel@tonic-gate 	crhold(oldcr);
2960Sstevel@tonic-gate 	p->p_cred = newcr;
2970Sstevel@tonic-gate 	mutex_exit(&p->p_crlock);
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	ASSERT(crgetzoneid(oldcr) == crgetzoneid(newcr));
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 	/*
3020Sstevel@tonic-gate 	 * If the real uid is changing, keep the per-user process
3030Sstevel@tonic-gate 	 * counts accurate.
3040Sstevel@tonic-gate 	 */
3050Sstevel@tonic-gate 	olduid = crgetruid(oldcr);
3060Sstevel@tonic-gate 	newuid = crgetruid(newcr);
3070Sstevel@tonic-gate 	if (olduid != newuid) {
3080Sstevel@tonic-gate 		zoneid_t zoneid = crgetzoneid(newcr);
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 		mutex_enter(&pidlock);
3110Sstevel@tonic-gate 		upcount_dec(olduid, zoneid);
3120Sstevel@tonic-gate 		upcount_inc(newuid, zoneid);
3130Sstevel@tonic-gate 		mutex_exit(&pidlock);
3140Sstevel@tonic-gate 	}
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	/*
3170Sstevel@tonic-gate 	 * Broadcast the new cred to all the other threads.  The old
3180Sstevel@tonic-gate 	 * cred can be safely returned because we have a hold on it.
3190Sstevel@tonic-gate 	 */
3200Sstevel@tonic-gate 	crset(p, newcr);
3210Sstevel@tonic-gate 	return (oldcr);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate static int
do_core(char * fp,int sig,enum core_types core_type,struct core_globals * cg)3250Sstevel@tonic-gate do_core(char *fp, int sig, enum core_types core_type, struct core_globals *cg)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	proc_t *p = curproc;
3280Sstevel@tonic-gate 	cred_t *credp = CRED();
3290Sstevel@tonic-gate 	rlim64_t rlimit;
3300Sstevel@tonic-gate 	vnode_t *vp;
3310Sstevel@tonic-gate 	int error = 0;
3320Sstevel@tonic-gate 	struct execsw *eswp;
3330Sstevel@tonic-gate 	cred_t *ocredp = NULL;
3340Sstevel@tonic-gate 	int is_setid = 0;
3350Sstevel@tonic-gate 	core_content_t content;
3360Sstevel@tonic-gate 	uid_t uid;
3370Sstevel@tonic-gate 	gid_t gid;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	if (core_type == CORE_GLOBAL || core_type == CORE_ZONE) {
3400Sstevel@tonic-gate 		mutex_enter(&cg->core_lock);
3410Sstevel@tonic-gate 		content = cg->core_content;
3420Sstevel@tonic-gate 		mutex_exit(&cg->core_lock);
3430Sstevel@tonic-gate 		rlimit = cg->core_rlimit;
3440Sstevel@tonic-gate 	} else {
3450Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
3460Sstevel@tonic-gate 		rlimit = rctl_enforced_value(rctlproc_legacy[RLIMIT_CORE],
3470Sstevel@tonic-gate 		    p->p_rctls, p);
3480Sstevel@tonic-gate 		content = corectl_content_value(p->p_content);
3490Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
3500Sstevel@tonic-gate 	}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	if (rlimit == 0)
3530Sstevel@tonic-gate 		return (EFBIG);
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	/*
3560Sstevel@tonic-gate 	 * If SNOCD is set, or if the effective, real, and saved ids do
3570Sstevel@tonic-gate 	 * not match up, no one but a privileged user is allowed to view
3580Sstevel@tonic-gate 	 * this core file.  Set the credentials and the owner to root.
3590Sstevel@tonic-gate 	 */
3600Sstevel@tonic-gate 	if ((p->p_flag & SNOCD) ||
3610Sstevel@tonic-gate 	    (uid = crgetuid(credp)) != crgetruid(credp) ||
3620Sstevel@tonic-gate 	    uid != crgetsuid(credp) ||
3630Sstevel@tonic-gate 	    (gid = crgetgid(credp)) != crgetrgid(credp) ||
3640Sstevel@tonic-gate 	    gid != crgetsgid(credp)) {
3650Sstevel@tonic-gate 		/*
3660Sstevel@tonic-gate 		 * Because this is insecure against certain forms of file
3670Sstevel@tonic-gate 		 * system attack, do it only if set-id core files have been
3680Sstevel@tonic-gate 		 * enabled via corectl(CC_GLOBAL_SETID | CC_PROCESS_SETID).
3690Sstevel@tonic-gate 		 */
3700Sstevel@tonic-gate 		if (((core_type == CORE_GLOBAL || core_type == CORE_ZONE) &&
3710Sstevel@tonic-gate 		    !(cg->core_options & CC_GLOBAL_SETID)) ||
3720Sstevel@tonic-gate 		    (core_type == CORE_PROC &&
3730Sstevel@tonic-gate 		    !(cg->core_options & CC_PROCESS_SETID)))
3740Sstevel@tonic-gate 			return (ENOTSUP);
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 		is_setid = 1;
3770Sstevel@tonic-gate 	}
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	/*
3800Sstevel@tonic-gate 	 * If we are doing a "global" core dump or a set-id core dump,
3810Sstevel@tonic-gate 	 * use kcred to do the dumping.
3820Sstevel@tonic-gate 	 */
3830Sstevel@tonic-gate 	if (core_type == CORE_GLOBAL || core_type == CORE_ZONE || is_setid) {
3840Sstevel@tonic-gate 		/*
3850Sstevel@tonic-gate 		 * Use the zone's "kcred" to prevent privilege
3860Sstevel@tonic-gate 		 * escalation.
3870Sstevel@tonic-gate 		 */
3880Sstevel@tonic-gate 		credp = zone_get_kcred(getzoneid());
3890Sstevel@tonic-gate 		ASSERT(credp != NULL);
3900Sstevel@tonic-gate 		ocredp = set_cred(p, credp);
3910Sstevel@tonic-gate 	}
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	/*
3940Sstevel@tonic-gate 	 * First remove any existing core file, then
3950Sstevel@tonic-gate 	 * open the new core file with (O_EXCL|O_CREAT).
3960Sstevel@tonic-gate 	 *
3970Sstevel@tonic-gate 	 * The reasons for doing this are manifold:
3980Sstevel@tonic-gate 	 *
3990Sstevel@tonic-gate 	 * For security reasons, we don't want root processes
4000Sstevel@tonic-gate 	 * to dump core through a symlink because that would
4010Sstevel@tonic-gate 	 * allow a malicious user to clobber any file on
4020Sstevel@tonic-gate 	 * the system if s/he could convince a root process,
4030Sstevel@tonic-gate 	 * perhaps a set-uid root process that s/he started,
4040Sstevel@tonic-gate 	 * to dump core in a directory writable by that user.
4050Sstevel@tonic-gate 	 * Similar security reasons apply to hard links.
4060Sstevel@tonic-gate 	 * For symmetry we do this unconditionally, not
4070Sstevel@tonic-gate 	 * just for root processes.
4080Sstevel@tonic-gate 	 *
4090Sstevel@tonic-gate 	 * If the process has the core file mmap()d into the
4100Sstevel@tonic-gate 	 * address space, we would be modifying the address
4110Sstevel@tonic-gate 	 * space that we are trying to dump if we did not first
4120Sstevel@tonic-gate 	 * remove the core file.  (The command "file core"
4130Sstevel@tonic-gate 	 * is the canonical example of this possibility.)
4140Sstevel@tonic-gate 	 *
4150Sstevel@tonic-gate 	 * Opening the core file with O_EXCL|O_CREAT ensures than
4160Sstevel@tonic-gate 	 * two concurrent core dumps don't clobber each other.
4170Sstevel@tonic-gate 	 * One is bound to lose; we don't want to make both lose.
4180Sstevel@tonic-gate 	 */
4190Sstevel@tonic-gate 	if ((error = remove_core_file(fp, core_type)) == 0) {
4200Sstevel@tonic-gate 		error = create_core_file(fp, core_type, &vp);
4210Sstevel@tonic-gate 	}
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	/*
4240Sstevel@tonic-gate 	 * Now that vn_open is complete, reset the process's credentials if
4250Sstevel@tonic-gate 	 * we changed them, and make 'credp' point to kcred used
4260Sstevel@tonic-gate 	 * above.  We use 'credp' to do i/o on the core file below, but leave
4270Sstevel@tonic-gate 	 * p->p_cred set to the original credential to allow the core file
4280Sstevel@tonic-gate 	 * to record this information.
4290Sstevel@tonic-gate 	 */
4300Sstevel@tonic-gate 	if (ocredp != NULL)
4310Sstevel@tonic-gate 		credp = set_cred(p, ocredp);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	if (error == 0) {
4340Sstevel@tonic-gate 		int closerr;
4350Sstevel@tonic-gate #if defined(__sparc)
4360Sstevel@tonic-gate 		(void) flush_user_windows_to_stack(NULL);
4370Sstevel@tonic-gate #endif
4383446Smrj 		if ((eswp = PTOU(curproc)->u_execsw) == NULL ||
4390Sstevel@tonic-gate 		    (eswp = findexec_by_magic(eswp->exec_magic)) == NULL) {
4400Sstevel@tonic-gate 			error = ENOSYS;
4410Sstevel@tonic-gate 		} else {
4420Sstevel@tonic-gate 			error = eswp->exec_core(vp, p, credp, rlimit, sig,
4430Sstevel@tonic-gate 			    content);
4440Sstevel@tonic-gate 			rw_exit(eswp->exec_lock);
4450Sstevel@tonic-gate 		}
4460Sstevel@tonic-gate 
4475331Samw 		closerr = VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, credp, NULL);
4480Sstevel@tonic-gate 		VN_RELE(vp);
4490Sstevel@tonic-gate 		if (error == 0)
4500Sstevel@tonic-gate 			error = closerr;
4510Sstevel@tonic-gate 	}
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	if (ocredp != NULL)
4540Sstevel@tonic-gate 		crfree(credp);
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	return (error);
4570Sstevel@tonic-gate }
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate /*
4600Sstevel@tonic-gate  * Convert a core name pattern to a pathname.
4610Sstevel@tonic-gate  */
4620Sstevel@tonic-gate static int
expand_string(const char * pat,char * fp,int size,cred_t * cr)4630Sstevel@tonic-gate expand_string(const char *pat, char *fp, int size, cred_t *cr)
4640Sstevel@tonic-gate {
4650Sstevel@tonic-gate 	proc_t *p = curproc;
4660Sstevel@tonic-gate 	char buf[24];
4670Sstevel@tonic-gate 	int len, i;
4680Sstevel@tonic-gate 	char *s;
4690Sstevel@tonic-gate 	char c;
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	while ((c = *pat++) != '\0') {
4720Sstevel@tonic-gate 		if (size < 2)
4730Sstevel@tonic-gate 			return (ENAMETOOLONG);
4740Sstevel@tonic-gate 		if (c != '%') {
4750Sstevel@tonic-gate 			size--;
4760Sstevel@tonic-gate 			*fp++ = c;
4770Sstevel@tonic-gate 			continue;
4780Sstevel@tonic-gate 		}
4790Sstevel@tonic-gate 		if ((c = *pat++) == '\0') {
4800Sstevel@tonic-gate 			size--;
4810Sstevel@tonic-gate 			*fp++ = '%';
4820Sstevel@tonic-gate 			break;
4830Sstevel@tonic-gate 		}
4840Sstevel@tonic-gate 		switch (c) {
4850Sstevel@tonic-gate 		case 'p':	/* pid */
4860Sstevel@tonic-gate 			(void) sprintf((s = buf), "%d", p->p_pid);
4870Sstevel@tonic-gate 			break;
4880Sstevel@tonic-gate 		case 'u':	/* effective uid */
4894321Scasper 			(void) sprintf((s = buf), "%u", crgetuid(p->p_cred));
4900Sstevel@tonic-gate 			break;
4910Sstevel@tonic-gate 		case 'g':	/* effective gid */
4924321Scasper 			(void) sprintf((s = buf), "%u", crgetgid(p->p_cred));
4930Sstevel@tonic-gate 			break;
4940Sstevel@tonic-gate 		case 'f':	/* exec'd filename */
4950Sstevel@tonic-gate 			s = PTOU(p)->u_comm;
4960Sstevel@tonic-gate 			break;
4970Sstevel@tonic-gate 		case 'd':	/* exec'd dirname */
4980Sstevel@tonic-gate 			/*
4990Sstevel@tonic-gate 			 * Even if pathname caching is disabled, we should
5000Sstevel@tonic-gate 			 * be able to lookup the pathname for a directory.
5010Sstevel@tonic-gate 			 */
5020Sstevel@tonic-gate 			if (p->p_execdir != NULL && vnodetopath(NULL,
5030Sstevel@tonic-gate 			    p->p_execdir, fp, size, cr) == 0) {
5040Sstevel@tonic-gate 				len = (int)strlen(fp);
5050Sstevel@tonic-gate 				ASSERT(len < size);
5060Sstevel@tonic-gate 				ASSERT(len >= 1);
5070Sstevel@tonic-gate 				ASSERT(fp[0] == '/');
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 				/*
5100Sstevel@tonic-gate 				 * Strip off the leading slash.
5110Sstevel@tonic-gate 				 */
5120Sstevel@tonic-gate 				for (i = 0; i < len; i++) {
5130Sstevel@tonic-gate 					fp[i] = fp[i + 1];
5140Sstevel@tonic-gate 				}
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 				len--;
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 				size -= len;
5190Sstevel@tonic-gate 				fp += len;
5200Sstevel@tonic-gate 			} else {
5210Sstevel@tonic-gate 				*fp = '\0';
5220Sstevel@tonic-gate 			}
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 			continue;
5250Sstevel@tonic-gate 		case 'n':	/* system nodename */
5260Sstevel@tonic-gate 			s = uts_nodename();
5270Sstevel@tonic-gate 			break;
5280Sstevel@tonic-gate 		case 'm':	/* machine (sun4u, etc) */
5290Sstevel@tonic-gate 			s = utsname.machine;
5300Sstevel@tonic-gate 			break;
5310Sstevel@tonic-gate 		case 't':	/* decimal value of time(2) */
5320Sstevel@tonic-gate 			(void) sprintf((s = buf), "%ld", gethrestime_sec());
5330Sstevel@tonic-gate 			break;
5340Sstevel@tonic-gate 		case 'z':
5350Sstevel@tonic-gate 			s = p->p_zone->zone_name;
5360Sstevel@tonic-gate 			break;
5370Sstevel@tonic-gate 		case '%':
5380Sstevel@tonic-gate 			(void) strcpy((s = buf), "%");
5390Sstevel@tonic-gate 			break;
5400Sstevel@tonic-gate 		default:
5410Sstevel@tonic-gate 			s = buf;
5420Sstevel@tonic-gate 			buf[0] = '%';
5430Sstevel@tonic-gate 			buf[1] = c;
5440Sstevel@tonic-gate 			buf[2] = '\0';
5450Sstevel@tonic-gate 			break;
5460Sstevel@tonic-gate 		}
5470Sstevel@tonic-gate 		len = (int)strlen(s);
5480Sstevel@tonic-gate 		if ((size -= len) <= 0)
5490Sstevel@tonic-gate 			return (ENAMETOOLONG);
5500Sstevel@tonic-gate 		(void) strcpy(fp, s);
5510Sstevel@tonic-gate 		fp += len;
5520Sstevel@tonic-gate 	}
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	*fp = '\0';
5550Sstevel@tonic-gate 	return (0);
5560Sstevel@tonic-gate }
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate static int
dump_one_core(int sig,rlim64_t rlimit,enum core_types core_type,struct core_globals * cg,char ** name)5590Sstevel@tonic-gate dump_one_core(int sig, rlim64_t rlimit, enum core_types core_type,
5600Sstevel@tonic-gate     struct core_globals *cg, char **name)
5610Sstevel@tonic-gate {
5620Sstevel@tonic-gate 	refstr_t *rp;
5630Sstevel@tonic-gate 	proc_t *p = curproc;
5640Sstevel@tonic-gate 	zoneid_t zoneid;
5650Sstevel@tonic-gate 	int error;
5660Sstevel@tonic-gate 	char *fp;
5670Sstevel@tonic-gate 	cred_t *cr;
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	ASSERT(core_type == CORE_ZONE || core_type == CORE_GLOBAL);
5700Sstevel@tonic-gate 	zoneid = (core_type == CORE_ZONE ? getzoneid() : GLOBAL_ZONEID);
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 	mutex_enter(&cg->core_lock);
5730Sstevel@tonic-gate 	if ((rp = cg->core_file) != NULL)
5740Sstevel@tonic-gate 		refstr_hold(rp);
5750Sstevel@tonic-gate 	mutex_exit(&cg->core_lock);
5760Sstevel@tonic-gate 	if (rp == NULL) {
5770Sstevel@tonic-gate 		core_log(cg, 0, "no global core file pattern exists", NULL,
5780Sstevel@tonic-gate 		    zoneid);
5790Sstevel@tonic-gate 		return (1);	/* core file not generated */
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate 	fp = kmem_alloc(MAXPATHLEN, KM_SLEEP);
5820Sstevel@tonic-gate 	cr = zone_get_kcred(getzoneid());
5830Sstevel@tonic-gate 	error = expand_string(refstr_value(rp), fp, MAXPATHLEN, cr);
5840Sstevel@tonic-gate 	crfree(cr);
5850Sstevel@tonic-gate 	if (error != 0) {
5860Sstevel@tonic-gate 		core_log(cg, 0, "global core file pattern too long",
5870Sstevel@tonic-gate 		    refstr_value(rp), zoneid);
5880Sstevel@tonic-gate 	} else if ((error = do_core(fp, sig, core_type, cg)) == 0) {
5890Sstevel@tonic-gate 		core_log(cg, 0, "core dumped", fp, zoneid);
5900Sstevel@tonic-gate 	} else if (error == ENOTSUP) {
5910Sstevel@tonic-gate 		core_log(cg, 0, "setid process, core not dumped", fp, zoneid);
5920Sstevel@tonic-gate 	} else if (error == ENOSPC) {
5930Sstevel@tonic-gate 		core_log(cg, 0, "no space left on device, core truncated",
5940Sstevel@tonic-gate 		    fp, zoneid);
5950Sstevel@tonic-gate 	} else if (error == EFBIG) {
5960Sstevel@tonic-gate 		if (rlimit == 0)
5970Sstevel@tonic-gate 			core_log(cg, 0, "core rlimit is zero, core not dumped",
5980Sstevel@tonic-gate 			    fp, zoneid);
5990Sstevel@tonic-gate 		else
6000Sstevel@tonic-gate 			core_log(cg, 0, "core rlimit exceeded, core truncated",
6010Sstevel@tonic-gate 			    fp, zoneid);
6020Sstevel@tonic-gate 		/*
6030Sstevel@tonic-gate 		 * In addition to the core result logging, we
6040Sstevel@tonic-gate 		 * may also have explicit actions defined on
6050Sstevel@tonic-gate 		 * core file size violations via the resource
6060Sstevel@tonic-gate 		 * control framework.
6070Sstevel@tonic-gate 		 */
6080Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
6090Sstevel@tonic-gate 		(void) rctl_action(rctlproc_legacy[RLIMIT_CORE],
6100Sstevel@tonic-gate 		    p->p_rctls, p, RCA_SAFE);
6110Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
6120Sstevel@tonic-gate 	} else {
6130Sstevel@tonic-gate 		core_log(cg, error, "core dump failed", fp, zoneid);
6140Sstevel@tonic-gate 	}
6150Sstevel@tonic-gate 	refstr_rele(rp);
6160Sstevel@tonic-gate 	if (name != NULL)
6170Sstevel@tonic-gate 		*name = fp;
6180Sstevel@tonic-gate 	else
6190Sstevel@tonic-gate 		kmem_free(fp, MAXPATHLEN);
6200Sstevel@tonic-gate 	return (error);
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate int
core(int sig,int ext)6240Sstevel@tonic-gate core(int sig, int ext)
6250Sstevel@tonic-gate {
6260Sstevel@tonic-gate 	proc_t *p = curproc;
6270Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
6280Sstevel@tonic-gate 	refstr_t *rp;
6290Sstevel@tonic-gate 	char *fp_process = NULL, *fp_global = NULL, *fp_zone = NULL;
6300Sstevel@tonic-gate 	int error1 = 1;
6310Sstevel@tonic-gate 	int error2 = 1;
6320Sstevel@tonic-gate 	int error3 = 1;
6330Sstevel@tonic-gate 	k_sigset_t sigmask;
6340Sstevel@tonic-gate 	k_sigset_t sighold;
6350Sstevel@tonic-gate 	rlim64_t rlimit;
6360Sstevel@tonic-gate 	struct core_globals *my_cg, *global_cg;
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	global_cg = zone_getspecific(core_zone_key, global_zone);
6390Sstevel@tonic-gate 	ASSERT(global_cg != NULL);
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	my_cg = zone_getspecific(core_zone_key, curproc->p_zone);
6420Sstevel@tonic-gate 	ASSERT(my_cg != NULL);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	/* core files suppressed? */
6450Sstevel@tonic-gate 	if (!(my_cg->core_options & (CC_PROCESS_PATH|CC_GLOBAL_PATH)) &&
6460Sstevel@tonic-gate 	    !(global_cg->core_options & CC_GLOBAL_PATH)) {
6470Sstevel@tonic-gate 		if (!ext && p->p_ct_process != NULL)
6480Sstevel@tonic-gate 			contract_process_core(p->p_ct_process, p, sig,
6490Sstevel@tonic-gate 			    NULL, NULL, NULL);
6500Sstevel@tonic-gate 		return (1);
6510Sstevel@tonic-gate 	}
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	/*
6540Sstevel@tonic-gate 	 * Block all signals except SIGHUP, SIGINT, SIGKILL, and SIGTERM.
6550Sstevel@tonic-gate 	 * These signals are allowed to interrupt the core dump.
6560Sstevel@tonic-gate 	 * SIGQUIT is not allowed because it is supposed to make a core.
6570Sstevel@tonic-gate 	 * Additionally, get current limit on core file size for handling later
6580Sstevel@tonic-gate 	 * error reporting.
6590Sstevel@tonic-gate 	 */
6600Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	p->p_flag |= SDOCORE;
6630Sstevel@tonic-gate 	schedctl_finish_sigblock(curthread);
6640Sstevel@tonic-gate 	sigmask = curthread->t_hold;	/* remember for later */
6650Sstevel@tonic-gate 	sigfillset(&sighold);
6660Sstevel@tonic-gate 	if (!sigismember(&sigmask, SIGHUP))
6670Sstevel@tonic-gate 		sigdelset(&sighold, SIGHUP);
6680Sstevel@tonic-gate 	if (!sigismember(&sigmask, SIGINT))
6690Sstevel@tonic-gate 		sigdelset(&sighold, SIGINT);
6700Sstevel@tonic-gate 	if (!sigismember(&sigmask, SIGKILL))
6710Sstevel@tonic-gate 		sigdelset(&sighold, SIGKILL);
6720Sstevel@tonic-gate 	if (!sigismember(&sigmask, SIGTERM))
6730Sstevel@tonic-gate 		sigdelset(&sighold, SIGTERM);
6740Sstevel@tonic-gate 	curthread->t_hold = sighold;
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	rlimit = rctl_enforced_value(rctlproc_legacy[RLIMIT_CORE], p->p_rctls,
6770Sstevel@tonic-gate 	    p);
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	/*
6820Sstevel@tonic-gate 	 * Undo any watchpoints.
6830Sstevel@tonic-gate 	 */
6840Sstevel@tonic-gate 	pr_free_watched_pages(p);
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	/*
6870Sstevel@tonic-gate 	 * The presence of a current signal prevents file i/o
6880Sstevel@tonic-gate 	 * from succeeding over a network.  We copy the current
6890Sstevel@tonic-gate 	 * signal information to the side and cancel the current
6900Sstevel@tonic-gate 	 * signal so that the core dump will succeed.
6910Sstevel@tonic-gate 	 */
6920Sstevel@tonic-gate 	ASSERT(lwp->lwp_cursig == sig);
6930Sstevel@tonic-gate 	lwp->lwp_cursig = 0;
6940Sstevel@tonic-gate 	lwp->lwp_extsig = 0;
6954626Sraf 	if (lwp->lwp_curinfo == NULL) {
6960Sstevel@tonic-gate 		bzero(&lwp->lwp_siginfo, sizeof (k_siginfo_t));
6974626Sraf 		lwp->lwp_siginfo.si_signo = sig;
6984626Sraf 		lwp->lwp_siginfo.si_code = SI_NOINFO;
6994626Sraf 	} else {
7000Sstevel@tonic-gate 		bcopy(&lwp->lwp_curinfo->sq_info,
7010Sstevel@tonic-gate 		    &lwp->lwp_siginfo, sizeof (k_siginfo_t));
7020Sstevel@tonic-gate 		siginfofree(lwp->lwp_curinfo);
7030Sstevel@tonic-gate 		lwp->lwp_curinfo = NULL;
7040Sstevel@tonic-gate 	}
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 	/*
7070Sstevel@tonic-gate 	 * Convert the core file name patterns into path names
7080Sstevel@tonic-gate 	 * and call do_core() to write the core files.
7090Sstevel@tonic-gate 	 */
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	if (my_cg->core_options & CC_PROCESS_PATH) {
7120Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
7130Sstevel@tonic-gate 		if (p->p_corefile != NULL)
7140Sstevel@tonic-gate 			rp = corectl_path_value(p->p_corefile);
7150Sstevel@tonic-gate 		else
7160Sstevel@tonic-gate 			rp = NULL;
7170Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
7180Sstevel@tonic-gate 		if (rp != NULL) {
7190Sstevel@tonic-gate 			fp_process = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7200Sstevel@tonic-gate 			error1 = expand_string(refstr_value(rp),
7218359SJohn.Harres@Sun.COM 			    fp_process, MAXPATHLEN, p->p_cred);
7220Sstevel@tonic-gate 			if (error1 == 0)
7230Sstevel@tonic-gate 				error1 = do_core(fp_process, sig, CORE_PROC,
7240Sstevel@tonic-gate 				    my_cg);
7250Sstevel@tonic-gate 			refstr_rele(rp);
7260Sstevel@tonic-gate 		}
7270Sstevel@tonic-gate 	}
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 	if (my_cg->core_options & CC_GLOBAL_PATH)
7300Sstevel@tonic-gate 		error2 = dump_one_core(sig, rlimit, CORE_ZONE, my_cg,
7310Sstevel@tonic-gate 		    &fp_global);
7320Sstevel@tonic-gate 	if (global_cg != my_cg && (global_cg->core_options & CC_GLOBAL_PATH))
7330Sstevel@tonic-gate 		error3 = dump_one_core(sig, rlimit, CORE_GLOBAL, global_cg,
7340Sstevel@tonic-gate 		    &fp_zone);
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 	/*
7370Sstevel@tonic-gate 	 * Restore the signal hold mask.
7380Sstevel@tonic-gate 	 */
7390Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
7400Sstevel@tonic-gate 	curthread->t_hold = sigmask;
7410Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	if (!ext && p->p_ct_process != NULL)
7440Sstevel@tonic-gate 		contract_process_core(p->p_ct_process, p, sig,
7450Sstevel@tonic-gate 		    error1 == 0 ? fp_process : NULL,
7460Sstevel@tonic-gate 		    error2 == 0 ? fp_global : NULL,
7470Sstevel@tonic-gate 		    error3 == 0 ? fp_zone : NULL);
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	if (fp_process != NULL)
7500Sstevel@tonic-gate 		kmem_free(fp_process, MAXPATHLEN);
7510Sstevel@tonic-gate 	if (fp_global != NULL)
7520Sstevel@tonic-gate 		kmem_free(fp_global, MAXPATHLEN);
7530Sstevel@tonic-gate 	if (fp_zone != NULL)
7540Sstevel@tonic-gate 		kmem_free(fp_zone, MAXPATHLEN);
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 	/*
7570Sstevel@tonic-gate 	 * Return non-zero if no core file was created.
7580Sstevel@tonic-gate 	 */
7590Sstevel@tonic-gate 	return (error1 != 0 && error2 != 0 && error3 != 0);
7600Sstevel@tonic-gate }
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate /*
7630Sstevel@tonic-gate  * Maximum chunk size for dumping core files,
7640Sstevel@tonic-gate  * size in pages, patchable in /etc/system
7650Sstevel@tonic-gate  */
7660Sstevel@tonic-gate uint_t	core_chunk = 32;
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate /*
7698359SJohn.Harres@Sun.COM  * The delay between core_write() calls, in microseconds.  The default
7708359SJohn.Harres@Sun.COM  * matches one "normal" clock tick, or 10 milliseconds.
7718359SJohn.Harres@Sun.COM  */
7728359SJohn.Harres@Sun.COM clock_t	core_delay_usec = 10000;
7738359SJohn.Harres@Sun.COM 
7748359SJohn.Harres@Sun.COM /*
7750Sstevel@tonic-gate  * Common code to core dump process memory.  The core_seg routine does i/o
7760Sstevel@tonic-gate  * using core_write() below, and so it has the same failure semantics.
7770Sstevel@tonic-gate  */
7780Sstevel@tonic-gate int
core_seg(proc_t * p,vnode_t * vp,offset_t offset,caddr_t addr,size_t size,rlim64_t rlimit,cred_t * credp)7790Sstevel@tonic-gate core_seg(proc_t *p, vnode_t *vp, offset_t offset, caddr_t addr, size_t size,
7800Sstevel@tonic-gate     rlim64_t rlimit, cred_t *credp)
7810Sstevel@tonic-gate {
7820Sstevel@tonic-gate 	caddr_t eaddr;
7830Sstevel@tonic-gate 	caddr_t base;
7840Sstevel@tonic-gate 	size_t len;
7850Sstevel@tonic-gate 	int err = 0;
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate 	eaddr = addr + size;
7880Sstevel@tonic-gate 	for (base = addr; base < eaddr; base += len) {
7890Sstevel@tonic-gate 		len = eaddr - base;
7900Sstevel@tonic-gate 		if (as_memory(p->p_as, &base, &len) != 0)
7910Sstevel@tonic-gate 			return (0);
7920Sstevel@tonic-gate 		/*
7930Sstevel@tonic-gate 		 * Reduce len to a reasonable value so that we don't
7940Sstevel@tonic-gate 		 * overwhelm the VM system with a monstrously large
7950Sstevel@tonic-gate 		 * single write and cause pageout to stop running.
7960Sstevel@tonic-gate 		 */
7970Sstevel@tonic-gate 		if (len > (size_t)core_chunk * PAGESIZE)
7988359SJohn.Harres@Sun.COM 			len = (size_t)core_chunk * PAGESIZE;
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 		err = core_write(vp, UIO_USERSPACE,
8010Sstevel@tonic-gate 		    offset + (size_t)(base - addr), base, len, rlimit, credp);
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 		if (err == 0) {
8040Sstevel@tonic-gate 			/*
8050Sstevel@tonic-gate 			 * Give pageout a chance to run.
8060Sstevel@tonic-gate 			 * Also allow core dumping to be interruptible.
8070Sstevel@tonic-gate 			 */
8088359SJohn.Harres@Sun.COM 			err = delay_sig(drv_usectohz(core_delay_usec));
8090Sstevel@tonic-gate 		}
8100Sstevel@tonic-gate 		if (err)
8110Sstevel@tonic-gate 			return (err);
8120Sstevel@tonic-gate 	}
8130Sstevel@tonic-gate 	return (0);
8140Sstevel@tonic-gate }
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate /*
8170Sstevel@tonic-gate  * Wrapper around vn_rdwr to perform writes to a core file.  For core files,
8180Sstevel@tonic-gate  * we always want to write as much as we possibly can, and then make sure to
8190Sstevel@tonic-gate  * return either 0 to the caller (for success), or the actual errno value.
8200Sstevel@tonic-gate  * By using this function, the caller can omit additional code for handling
8210Sstevel@tonic-gate  * retries and errors for partial writes returned by vn_rdwr.  If vn_rdwr
8220Sstevel@tonic-gate  * unexpectedly returns zero but no progress has been made, we return ENOSPC.
8230Sstevel@tonic-gate  */
8240Sstevel@tonic-gate int
core_write(vnode_t * vp,enum uio_seg segflg,offset_t offset,const void * buf,size_t len,rlim64_t rlimit,cred_t * credp)8250Sstevel@tonic-gate core_write(vnode_t *vp, enum uio_seg segflg, offset_t offset,
8260Sstevel@tonic-gate     const void *buf, size_t len, rlim64_t rlimit, cred_t *credp)
8270Sstevel@tonic-gate {
8280Sstevel@tonic-gate 	ssize_t resid = len;
8290Sstevel@tonic-gate 	int error = 0;
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 	while (len != 0) {
8320Sstevel@tonic-gate 		error = vn_rdwr(UIO_WRITE, vp, (caddr_t)buf, len, offset,
8330Sstevel@tonic-gate 		    segflg, 0, rlimit, credp, &resid);
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate 		if (error != 0)
8360Sstevel@tonic-gate 			break;
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate 		if (resid >= len)
8390Sstevel@tonic-gate 			return (ENOSPC);
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 		buf = (const char *)buf + len - resid;
8420Sstevel@tonic-gate 		offset += len - resid;
8430Sstevel@tonic-gate 		len = resid;
8440Sstevel@tonic-gate 	}
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 	return (error);
8470Sstevel@tonic-gate }
848