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