core.c revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/ucontext.h>
#include <sys/pathname.h>
#include <sys/schedctl.h>
/*
* Processes running within a zone potentially dump core in 3 locations,
* based on the per-process, per-zone, and the global zone's core settings.
*
* Per-zone and global zone settings are often referred to as "global"
* settings since they apply to the system (or zone) as a whole, as
* opposed to a particular process.
*/
enum core_types {
CORE_PROC, /* Use per-process settings */
CORE_ZONE, /* Use per-zone settings */
CORE_GLOBAL /* Use global zone settings */
};
/*
* Log information about "global" core dumps to syslog.
*/
static void
{
return;
else if (error == 0)
else
}
/*
* Private version of vn_remove().
* Refuse to unlink a directory or an unwritable file.
* Also allow the process to access files normally inaccessible due to
* chroot(2) or Zone limitations.
*/
static int
{
int error;
int in_crit = 0;
return (error);
/*
* Determine what rootvp to use.
*/
} else {
}
return (error);
}
/*
* Succeed if there is no file.
* Fail if the file is not a regular file.
* Fail if the filesystem is mounted read-only.
* Fail if the file is not writeable.
* Fail if the file has NBMAND share reservations.
*/
error = 0;
if (nbl_need_check(vp)) {
in_crit = 1;
}
}
if (!error) {
}
}
if (in_crit)
}
return (error);
}
/*
* Create the core file in a location that may be normally inaccessible due
* to chroot(2) or Zone limitations.
*/
static int
{
int error;
char *file;
} else {
/*
* This is tricky because we want to dump the core in
* a location which may normally be inaccessible
* to us (due to chroot(2) limitations, or zone
* membership), and hence need to overcome u_rdir
* restrictions. The basic idea is to separate
* the path from the filename, lookup the
* pathname separately (starting from the global
* zone's root directory), and then open the
* file starting at the directory vnode.
*/
return (error);
} else {
}
/*
* rootvp and startvp will be VN_RELE()'d by lookuppnvp() if
* necessary.
*/
/*
* Do a lookup on the full path, ignoring the actual file, but
* finding the vnode for the directory. It's OK if the file
* doesn't exist -- it most likely won't since we just removed
* it.
*/
if (error != 0)
return (error);
/*
* Now find the final component in the path (ie, the name of
* the core file).
*/
return (error);
}
pn_setlast(&pn);
}
}
/*
* Don't dump a core file owned by "nobody".
*/
if (error == 0 &&
}
return (error);
}
/*
* Install the specified held cred into the process, and return a pointer to
* the held cred which was previously the value of p->p_cred.
*/
static cred_t *
{
/*
* Place a hold on the existing cred, and then install the new
* cred into the proc structure.
*/
mutex_enter(&p->p_crlock);
mutex_exit(&p->p_crlock);
/*
* If the real uid is changing, keep the per-user process
* counts accurate.
*/
}
/*
* Broadcast the new cred to all the other threads. The old
* cred can be safely returned because we have a hold on it.
*/
return (oldcr);
}
static int
{
int error = 0;
int is_setid = 0;
} else {
mutex_enter(&p->p_lock);
p->p_rctls, p);
mutex_exit(&p->p_lock);
}
if (rlimit == 0)
return (EFBIG);
/*
* If SNOCD is set, or if the effective, real, and saved ids do
* not match up, no one but a privileged user is allowed to view
* this core file. Set the credentials and the owner to root.
*/
/*
* Because this is insecure against certain forms of file
* system attack, do it only if set-id core files have been
* enabled via corectl(CC_GLOBAL_SETID | CC_PROCESS_SETID).
*/
return (ENOTSUP);
is_setid = 1;
}
/*
* If we are doing a "global" core dump or a set-id core dump,
* use kcred to do the dumping.
*/
/*
* Use the zone's "kcred" to prevent privilege
* escalation.
*/
}
/*
* First remove any existing core file, then
* open the new core file with (O_EXCL|O_CREAT).
*
* The reasons for doing this are manifold:
*
* For security reasons, we don't want root processes
* to dump core through a symlink because that would
* allow a malicious user to clobber any file on
* to dump core in a directory writable by that user.
* Similar security reasons apply to hard links.
* For symmetry we do this unconditionally, not
* just for root processes.
*
* If the process has the core file mmap()d into the
* address space, we would be modifying the address
* space that we are trying to dump if we did not first
* remove the core file. (The command "file core"
* is the canonical example of this possibility.)
*
* Opening the core file with O_EXCL|O_CREAT ensures than
* two concurrent core dumps don't clobber each other.
* One is bound to lose; we don't want to make both lose.
*/
}
/*
* Now that vn_open is complete, reset the process's credentials if
* we changed them, and make 'credp' point to kcred used
* above. We use 'credp' to do i/o on the core file below, but leave
* p->p_cred set to the original credential to allow the core file
* to record this information.
*/
if (error == 0) {
int closerr;
#if defined(__sparc)
(void) flush_user_windows_to_stack(NULL);
#endif
#ifdef SUN_SRC_COMPAT
#endif
} else {
content);
}
if (error == 0)
}
return (error);
}
/*
* Convert a core name pattern to a pathname.
*/
static int
{
char buf[24];
int len, i;
char *s;
char c;
while ((c = *pat++) != '\0') {
if (size < 2)
return (ENAMETOOLONG);
if (c != '%') {
size--;
*fp++ = c;
continue;
}
if ((c = *pat++) == '\0') {
size--;
*fp++ = '%';
break;
}
switch (c) {
case 'p': /* pid */
break;
case 'u': /* effective uid */
break;
case 'g': /* effective gid */
break;
case 'f': /* exec'd filename */
break;
case 'd': /* exec'd dirname */
/*
* Even if pathname caching is disabled, we should
* be able to lookup the pathname for a directory.
*/
/*
* Strip off the leading slash.
*/
for (i = 0; i < len; i++) {
}
len--;
} else {
*fp = '\0';
}
continue;
case 'n': /* system nodename */
s = uts_nodename();
break;
case 'm': /* machine (sun4u, etc) */
break;
case 't': /* decimal value of time(2) */
break;
case 'z':
break;
case '%':
break;
default:
s = buf;
buf[0] = '%';
buf[1] = c;
break;
}
return (ENAMETOOLONG);
}
*fp = '\0';
return (0);
}
static int
{
int error;
char *fp;
zoneid);
return (1); /* core file not generated */
}
if (error != 0) {
if (rlimit == 0)
else
/*
* In addition to the core result logging, we
* may also have explicit actions defined on
* core file size violations via the resource
* control framework.
*/
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
} else {
}
else
return (error);
}
int
{
int error1 = 1;
int error2 = 1;
int error3 = 1;
/* core files suppressed? */
return (1);
}
/*
* Block all signals except SIGHUP, SIGINT, SIGKILL, and SIGTERM.
* These signals are allowed to interrupt the core dump.
* SIGQUIT is not allowed because it is supposed to make a core.
* Additionally, get current limit on core file size for handling later
* error reporting.
*/
mutex_enter(&p->p_lock);
p);
mutex_exit(&p->p_lock);
/*
* Undo any watchpoints.
*/
/*
* The presence of a current signal prevents file i/o
* from succeeding over a network. We copy the current
* signal information to the side and cancel the current
* signal so that the core dump will succeed.
*/
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
} else {
}
/*
* Convert the core file name patterns into path names
* and call do_core() to write the core files.
*/
mutex_enter(&p->p_lock);
if (p->p_corefile != NULL)
else
mutex_exit(&p->p_lock);
if (error1 == 0)
my_cg);
}
}
&fp_global);
&fp_zone);
/*
* Restore the signal hold mask.
*/
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
if (fp_process != NULL)
/*
* Return non-zero if no core file was created.
*/
}
/*
* Maximum chunk size for dumping core files,
*/
/*
* Common code to core dump process memory. The core_seg routine does i/o
* using core_write() below, and so it has the same failure semantics.
*/
int
{
int err = 0;
return (0);
/*
* Reduce len to a reasonable value so that we don't
* overwhelm the VM system with a monstrously large
* single write and cause pageout to stop running.
*/
if (err == 0) {
/*
* Give pageout a chance to run.
* Also allow core dumping to be interruptible.
*/
}
if (err)
return (err);
}
return (0);
}
/*
* Wrapper around vn_rdwr to perform writes to a core file. For core files,
* we always want to write as much as we possibly can, and then make sure to
* return either 0 to the caller (for success), or the actual errno value.
* By using this function, the caller can omit additional code for handling
* retries and errors for partial writes returned by vn_rdwr. If vn_rdwr
* unexpectedly returns zero but no progress has been made, we return ENOSPC.
*/
int
{
int error = 0;
while (len != 0) {
if (error != 0)
break;
return (ENOSPC);
}
return (error);
}