unbreak, deprivilege dumpcore(1)

This commit is contained in:
David van Moolenbroek 2009-12-29 21:34:06 +00:00
parent e423c86009
commit 0bafee3d78
4 changed files with 184 additions and 125 deletions

View File

@ -363,7 +363,7 @@ du: du.c
@install -S 256kw $@ @install -S 256kw $@
dumpcore: dumpcore.c dumpcore: dumpcore.c
$(CCLD) -D_SYSTEM=1 -o $@ $< -lsys $(CCLD) -o $@ $<
@install -S 32k $@ @install -S 32k $@
ed: ed.c ed: ed.c

View File

@ -1,18 +1,14 @@
/* dumpcore - create core file of running process */
#include <fcntl.h> #include <fcntl.h>
#include <assert.h>
#include <unistd.h> #include <unistd.h>
#include <minix/config.h> #include <minix/config.h>
#include <minix/type.h> #include <minix/type.h>
#include <minix/callnr.h> #include <minix/ipc.h>
#include <minix/safecopies.h>
#include <minix/endpoint.h>
#include <minix/com.h>
#include <minix/syslib.h>
#include <minix/const.h> #include <minix/const.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include <sys/svrctl.h> #include <sys/wait.h>
#include <dirent.h> #include <signal.h>
#include <timers.h> #include <timers.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
@ -20,33 +16,82 @@
#include <stdlib.h> #include <stdlib.h>
#include "../../kernel/arch/i386/include/archtypes.h" #include "../../kernel/arch/i386/include/archtypes.h"
#include "../../kernel/const.h"
#include "../../kernel/type.h"
#include "../../kernel/config.h"
#include "../../kernel/debug.h"
#include "../../kernel/proc.h" #include "../../kernel/proc.h"
#include "../../kernel/ipc.h"
#define SLOTS (NR_TASKS + NR_PROCS) #define CLICK_WORDS (CLICK_SIZE / sizeof(unsigned long))
struct proc proc[SLOTS];
int adjust_stack(pid_t pid, struct mem_map *seg)
int write_seg(int fd, off_t off, endpoint_t proc_e, int seg,
off_t seg_off, phys_bytes seg_bytes)
{ {
int r, block_size, fl; static unsigned long buf[CLICK_WORDS];
off_t n, o, b_off; struct ptrace_range pr;
block_t b; size_t off, top, bottom;
struct buf *bp; int i;
ssize_t w;
static char buf[1024];
for (o= seg_off; o < seg_off+seg_bytes; o += sizeof(buf)) /* FIXME: kernel/VM strangeness */
seg->mem_vir -= seg->mem_len - 1;
/* Scan the stack, top to bottom, to find the lowest accessible region.
* In practice that will be at 64MB, so we also scan for the lowest non-zero
* region in order to keep the core file size managable.
* Portability note: this code assumes that the stack grows down.
*/
top = seg->mem_vir + seg->mem_len;
pr.pr_space = TS_DATA;
pr.pr_addr = (top - 1) << CLICK_SHIFT;
pr.pr_size = sizeof(buf);
pr.pr_ptr = buf;
for (off = top - 1; off >= seg->mem_vir; off--) {
if (ptrace(T_GETRANGE, pid, (long) &pr, 0)) {
if (errno == EFAULT)
break;
perror("ptrace(T_GETRANGE)");
return 1;
}
for (i = 0; i < CLICK_WORDS; i += sizeof(buf[0]))
if (buf[i] != 0)
bottom = off;
pr.pr_addr -= sizeof(buf);
}
/* Add one extra zero page as margin. */
if (bottom > off && bottom > seg->mem_vir)
bottom--;
seg->mem_len -= bottom - seg->mem_vir;
seg->mem_vir = bottom;
return 0;
}
int write_seg(int fd, pid_t pid, int seg, off_t seg_off, phys_bytes seg_bytes)
{
int r;
off_t off;
ssize_t w;
static char buf[CLICK_SIZE];
struct ptrace_range pr;
pr.pr_space = (seg == T) ? TS_INS : TS_DATA;
pr.pr_addr = seg_off;
pr.pr_size = sizeof(buf);
pr.pr_ptr = buf;
for ( ; pr.pr_addr < seg_off + seg_bytes; pr.pr_addr += sizeof(buf))
{ {
/* Copy a chunk from user space to the block buffer. */ /* Copy a chunk from user space to the block buffer. */
if(sys_vircopy(proc_e, seg, (phys_bytes) o, if (ptrace(T_GETRANGE, pid, (long) &pr, 0)) {
SELF, D, (vir_bytes) buf, (phys_bytes) sizeof(buf)) != OK) { /* Create holes for inaccessible areas. */
printf("write_seg: sys_vircopy failed\n"); if (errno == EFAULT) {
lseek(fd, sizeof(buf), SEEK_CUR);
continue;
}
perror("ptrace(T_GETRANGE)");
return 1; return 1;
} }
@ -57,111 +102,135 @@ int write_seg(int fd, off_t off, endpoint_t proc_e, int seg,
} }
} }
return OK; return 0;
} }
int dumpcore(pid_t pid)
int dumpcore(endpoint_t proc_e)
{ {
int r, seg, exists, fd; int r, seg, fd;
mode_t omode;
vir_bytes len; vir_bytes len;
off_t off, seg_off; off_t off, seg_off;
long trace_off, trace_data; long data;
struct mem_map segs[NR_LOCAL_SEGS]; struct mem_map segs[NR_LOCAL_SEGS];
struct proc procentry; struct proc procentry;
int proc_s;
ssize_t w; ssize_t w;
char core_name[200]; char core_name[PATH_MAX];
if(sys_getproctab(proc) != OK) { /* Get the process table entry for this process. */
printf( "Couldn't get proc tab.\n"); len = sizeof(struct proc) / sizeof(long);
for (off = 0; off < len; off++)
{
errno = 0;
data = ptrace(T_GETUSER, pid, off * sizeof(long), 0);
if (data == -1 && errno != 0)
{
perror("ptrace(T_GETUSER)");
return 1; return 1;
} }
for(proc_s = 0; proc_s < SLOTS; proc_s++) ((long *) &procentry)[off] = data;
if(proc[proc_s].p_endpoint == proc_e && }
!isemptyp(&proc[proc_s]))
break;
if(proc_s >= SLOTS) { memcpy(segs, procentry.p_memmap, sizeof(segs));
printf( "endpoint %d not found.\n", proc_e);
/* Correct and reduce the stack segment. */
r = adjust_stack(pid, &segs[S]);
if (r != 0)
goto error;
/* Create a core file with a temporary, unique name. */
sprintf(core_name, "core.%d", pid);
if((fd = open(core_name, O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0) {
fprintf(stderr, "couldn't open %s (%s)\n", core_name,
strerror(errno));
return 1; return 1;
} }
if(proc_s < 0 || proc_s >= SLOTS) { /* Write out the process's segments. */
printf( "Slot out of range (internal error).\n");
return 1;
}
if(isemptyp(&proc[proc_s])) {
printf( "slot %d is no process (internal error).\n",
proc_s);
return 1;
}
sprintf(core_name, "/tmp/core.%d", proc_e);
if((fd = open(core_name,
O_CREAT|O_WRONLY|O_EXCL|O_NONBLOCK, 0600)) < 0) {
printf("couldn't open %s (%s)\n",
core_name, strerror(errno));
return 1;
}
proc[proc_s].p_name[P_NAME_LEN-1] = '\0';
memcpy(segs, proc[proc_s].p_memmap, sizeof(segs));
off= 0;
if((w=write(fd, segs, sizeof(segs))) != sizeof(segs)) { if((w=write(fd, segs, sizeof(segs))) != sizeof(segs)) {
if(w < 0) printf("write error: %s\n", strerror(errno)); if(w < 0) printf("write error: %s\n", strerror(errno));
printf( "segs write failed: %d/%d\n", w, sizeof(segs)); printf( "segs write failed: %d/%d\n", w, sizeof(segs));
return 1; goto error;
} }
off += sizeof(segs);
/* Write out the whole kernel process table entry to get the regs. */ /* Write out the whole kernel process table entry to get the regs. */
for (trace_off= 0;; trace_off += sizeof(long)) if((w=write(fd, &procentry, sizeof(procentry))) != sizeof(procentry)) {
{ if(w < 0) printf("write error: %s\n", strerror(errno));
r= sys_trace(T_GETUSER, proc_e, trace_off, &trace_data); printf( "proc write failed: %d/%d\n", w, sizeof(procentry));
if (r != OK) goto error;
{
break;
}
r= write(fd, &trace_data, sizeof(trace_data));
if (r != sizeof(trace_data)) {
printf( "trace_data write failed\n");
return 1;
}
off += sizeof(trace_data);
} }
/* Loop through segments and write the segments themselves out. */ /* Loop through segments and write the segments themselves out. */
for (seg = 0; seg < NR_LOCAL_SEGS; seg++) { for (seg = 0; seg < NR_LOCAL_SEGS; seg++) {
len= segs[seg].mem_len << CLICK_SHIFT; len= segs[seg].mem_len << CLICK_SHIFT;
seg_off= segs[seg].mem_vir << CLICK_SHIFT; seg_off= segs[seg].mem_vir << CLICK_SHIFT;
r= write_seg(fd, off, proc_e, seg, seg_off, len); r= write_seg(fd, pid, seg, seg_off, len);
if (r != OK) if (r != 0)
{ goto error;
printf( "write failed\n");
return 1;
} }
off += len;
/* Give the core file its final name. */
if (rename(core_name, "core")) {
perror("rename");
goto error;
} }
close(fd); close(fd);
return 0; return 0;
error:
close(fd);
unlink(core_name);
return 1;
} }
main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
pid_t pid;
int r, status;
if(argc != 2) { if(argc != 2) {
printf("usage: %s <endpoint>\n", argv[0]); printf("usage: %s <pid>\n", argv[0]);
return 1;
}
dumpcore(atoi(argv[1]));
return 1; return 1;
} }
pid = atoi(argv[1]);
if (ptrace(T_ATTACH, pid, 0, 0) != 0) {
perror("ptrace(T_ATTACH)");
return 1;
}
if (waitpid(pid, &status, 0) != pid) {
perror("waitpid");
return 1;
}
while (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) {
/* whatever happens here is fine */
ptrace(T_RESUME, pid, 0, WSTOPSIG(status));
if (waitpid(pid, &status, 0) != pid) {
perror("waitpid");
return 1;
}
}
if (!WIFSTOPPED(status)) {
fprintf(stderr, "process died while attaching\n");
return 1;
}
r = dumpcore(pid);
if (ptrace(T_DETACH, pid, 0, 0)) {
fprintf(stderr, "warning, detaching failed (%s)\n",
strerror(errno));
}
return r;
}

View File

@ -142,19 +142,6 @@ service fxp
; ;
}; };
service dumpcore
{
system
TRACE # 5
VIRCOPY # 15
TIMES # 25
GETINFO # 26
SETGRANT # 34
PROFBUF # 38
SYSCTL # 44
;
};
service inet service inet
{ {
system system

View File

@ -2,7 +2,7 @@
.SH NAME .SH NAME
dumpcore \- generate core file of running process dumpcore \- generate core file of running process
.SH SYNOPSIS .SH SYNOPSIS
service run /usr/bin/dumpcore -args \fIendpoint\fR dumpcore \fIpid\fR
.br .br
.de FL .de FL
.TP .TP
@ -15,12 +15,15 @@ service run /usr/bin/dumpcore -args \fIendpoint\fR
# \\$2 # \\$2
.. ..
.SH DESCRIPTION .SH DESCRIPTION
\fIDumpcore\fR executes the same code that FS executes in order to The \fBdumpcore\fR utility generates a core file for a running process, based
generate a core dump of a currently running process, identified by on that process's process ID (\fIpid\fP). The resulting core file will be
endpoint number. Obtain an endpoint number by using the F1 debug stored under the name "core" in the current working directory. Any previous
dump, or ps with -E option. file with that name will be overwritten.
.PP
The resulting core file can be used with for example
.BR mdb (1).
.SH BUGS .SH BUGS
\fIDumpcore\fR has duplicate code with FS, and has to be started with The process of generating the core file is currently very rudimentary, and
the service command. the generated core file does not contain for example memory mapped areas.
.SH "SEE ALSO" .SH "SEE ALSO"
.BR ps (1). .BR mdb (1).