From d773820bd9bc49b2ee3303c0ce19218fa6d35635 Mon Sep 17 00:00:00 2001 From: dsl Date: Fri, 3 Jan 2014 20:52:47 +0000 Subject: [PATCH] Instead of generating all the 'note' sections twice (and hoping that the 'fast path' size on the first path matches the actual size on the second) save all the notes (mostly the cpu registers for all the LWPs) in malloced memory on the first pass. Sanity check that the number of memory segments matches written matches the count obtained earlier. If gcore() is used they could differ. (Not sure that returning ENOMEM is ideal, but it is better than a crash.) --- sys/kern/core_elf32.c | 340 ++++++++++++++++++++++-------------------- sys/kern/kern_core.c | 13 +- sys/sys/exec.h | 3 +- sys/sys/exec_elf.h | 11 +- 4 files changed, 196 insertions(+), 171 deletions(-) diff --git a/sys/kern/core_elf32.c b/sys/kern/core_elf32.c index a5e992f0f6e3..44194db93095 100644 --- a/sys/kern/core_elf32.c +++ b/sys/kern/core_elf32.c @@ -1,4 +1,4 @@ -/* $NetBSD: core_elf32.c,v 1.38 2014/01/03 15:15:02 dsl Exp $ */ +/* $NetBSD: core_elf32.c,v 1.39 2014/01/03 20:52:47 dsl Exp $ */ /* * Copyright (c) 2001 Wasabi Systems, Inc. @@ -40,7 +40,7 @@ */ #include -__KERNEL_RCSID(1, "$NetBSD: core_elf32.c,v 1.38 2014/01/03 15:15:02 dsl Exp $"); +__KERNEL_RCSID(1, "$NetBSD: core_elf32.c,v 1.39 2014/01/03 20:52:47 dsl Exp $"); #ifdef _KERNEL_OPT #include "opt_coredump.h" @@ -69,18 +69,33 @@ __KERNEL_RCSID(1, "$NetBSD: core_elf32.c,v 1.38 2014/01/03 15:15:02 dsl Exp $"); struct writesegs_state { Elf_Phdr *psections; off_t secoff; + size_t npsections; +}; + +/* + * We need to know how big the 'notes' are before we write the main header. + * To avoid problems with double-processing we save the data. + */ +struct note_buf { + struct note_buf *nb_next; + unsigned char nb_data[4096 - sizeof (void *)]; +}; + +struct note_state { + struct note_buf *ns_first; + struct note_buf *ns_last; + unsigned int ns_count; /* Of full buffers */ + unsigned int ns_offset; /* Write point in last buffer */ }; static int ELFNAMEEND(coredump_getseghdrs)(struct proc *, struct uvm_coredump_state *); -static int ELFNAMEEND(coredump_notes)(struct proc *, struct lwp *, - struct coredump_iostate *, size_t *); -static int ELFNAMEEND(coredump_note)(struct proc *, struct lwp *, - struct coredump_iostate *, size_t *); +static int ELFNAMEEND(coredump_notes)(struct lwp *, struct note_state *); +static int ELFNAMEEND(coredump_note)(struct lwp *, struct note_state *); +/* The 'note' section names and data are always 4-byte aligned. */ #define ELFROUNDSIZE 4 /* XXX Should it be sizeof(Elf_Word)? */ -#define elfround(x) roundup((x), ELFROUNDSIZE) #define elf_process_read_regs CONCAT(process_read_regs, ELFSIZE) #ifdef __HAVE_PROCESS_XFPREGS @@ -94,18 +109,31 @@ static int ELFNAMEEND(coredump_note)(struct proc *, struct lwp *, int ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie) { - struct proc *p; Elf_Ehdr ehdr; - Elf_Phdr phdr, *psections; + Elf_Phdr *psections; size_t psectionssize; int npsections; struct writesegs_state ws; - off_t notestart, secstart, offset; + off_t notestart; size_t notesize; int error, i; + struct note_state ns; + struct note_buf *nb; + psections = NULL; - p = l->l_proc; + + /* Get all of the notes (mostly all the registers). */ + ns.ns_first = kmem_alloc(sizeof *ns.ns_first, KM_SLEEP); + ns.ns_last = ns.ns_first; + ns.ns_count = 0; + ns.ns_offset = 0; + error = ELFNAMEEND(coredump_notes)(l, &ns); + ns.ns_last->nb_next = NULL; + if (error) + goto out; + notesize = ns.ns_count * sizeof nb->nb_data + ns.ns_offset; + /* * We have to make a total of 3 passes across the map: * @@ -118,15 +146,11 @@ ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie) */ /* Pass 1: count the entries. */ - npsections = uvm_coredump_count_segs(p); - /* Count the PT_NOTE section. */ + npsections = uvm_coredump_count_segs(l->l_proc); + /* Allow for the PT_NOTE section. */ npsections++; - /* Get the size of the notes (mostly all the registers). */ - error = ELFNAMEEND(coredump_notes)(p, l, NULL, ¬esize); - if (error) - goto out; - + /* Build the main elf header */ memset(&ehdr.e_ident[EI_PAD], 0, sizeof(ehdr.e_ident) - EI_PAD); memcpy(ehdr.e_ident, ELFMAG, SELFMAG); #if ELFSIZE == 32 @@ -164,20 +188,24 @@ ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie) if (error) goto out; - offset = sizeof(ehdr); + psectionssize = npsections * sizeof(*psections); + notestart = sizeof(ehdr) + psectionssize; - notestart = offset + sizeof(phdr) * npsections; - secstart = notestart + notesize; - - psectionssize = npsections * sizeof(Elf_Phdr); psections = kmem_zalloc(psectionssize, KM_SLEEP); /* Pass 2: now find the P-section headers. */ - ws.secoff = secstart; + ws.secoff = notestart + notesize; ws.psections = psections; - error = uvm_coredump_walkmap(p, ELFNAMEEND(coredump_getseghdrs), &ws); + ws.npsections = npsections - 1; + error = uvm_coredump_walkmap(l->l_proc, ELFNAMEEND(coredump_getseghdrs), + &ws); if (error) goto out; + if (ws.npsections != 0) { + /* A section went away */ + error = ENOMEM; + goto out; + } /* Add the PT_NOTE header after the P-section headers. */ ws.psections->p_type = PT_NOTE; @@ -189,40 +217,35 @@ ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie) ws.psections->p_flags = PF_R; ws.psections->p_align = ELFROUNDSIZE; - /* Write the P-section headers followed by the PT_NOTR header */ - error = coredump_write(cookie, UIO_SYSSPACE, psections, - npsections * sizeof(Elf_Phdr)); + /* Write the P-section headers followed by the PT_NOTE header */ + error = coredump_write(cookie, UIO_SYSSPACE, psections, psectionssize); if (error) goto out; #ifdef DIAGNOSTIC - offset += npsections * sizeof(Elf_Phdr); - if (offset != notestart) + if (coredump_offset(cookie) != notestart) panic("coredump: offset %lld != notestart %lld", - (long long) offset, (long long) notestart); + (long long) coredump_offset(cookie), + (long long) notestart); #endif /* Write out the notes. */ - error = ELFNAMEEND(coredump_notes)(p, l, cookie, ¬esize); - if (error) - goto out; + for (nb = ns.ns_first; nb != NULL; nb = nb->nb_next) { + error = coredump_write(cookie, UIO_SYSSPACE, nb->nb_data, + nb->nb_next == NULL ? ns.ns_offset : sizeof nb->nb_data); + if (error) + goto out; + } -#ifdef DIAGNOSTIC - offset += notesize; - if (offset != secstart) - panic("coredump: offset %lld != secstart %lld", - (long long) offset, (long long) secstart); -#endif - - /* Pass 3: finally, write the sections themselves. */ + /* Finally, write the sections themselves. */ for (i = 0; i < npsections - 1; i++) { if (psections[i].p_filesz == 0) continue; #ifdef DIAGNOSTIC - if (offset != psections[i].p_offset) + if (coredump_offset(cookie) != psections[i].p_offset) panic("coredump: offset %lld != p_offset[%d] %lld", - (long long) offset, i, + (long long) coredump_offset(cookie), i, (long long) psections[i].p_filesz); #endif @@ -231,15 +254,13 @@ ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie) psections[i].p_filesz); if (error) goto out; - -#ifdef DIAGNOSTIC - offset += psections[i].p_filesz; -#endif } out: if (psections) kmem_free(psections, psectionssize); + for (; (nb = ns.ns_first) != NULL; ns.ns_first = nb->nb_next) + kmem_free(nb, sizeof *nb); return (error); } @@ -252,6 +273,11 @@ ELFNAMEEND(coredump_getseghdrs)(struct proc *p, struct uvm_coredump_state *us) vaddr_t end; int error; + /* Don't overrun if there are more sections */ + if (ws->npsections == 0) + return ENOMEM; + ws->npsections--; + size = us->end - us->start; realsize = us->realend - us->start; end = us->realend; @@ -299,74 +325,65 @@ ELFNAMEEND(coredump_getseghdrs)(struct proc *p, struct uvm_coredump_state *us) } static int -ELFNAMEEND(coredump_notes)(struct proc *p, struct lwp *l, - struct coredump_iostate *iocookie, size_t *sizep) +ELFNAMEEND(coredump_notes)(struct lwp *l, struct note_state *ns) { + struct proc *p; struct netbsd_elfcore_procinfo cpi; Elf_Nhdr nhdr; - size_t size, notesize; int error; struct lwp *l0; sigset_t ss1, ss2; - size = 0; + p = l->l_proc; /* First, write an elfcore_procinfo. */ - notesize = sizeof(nhdr) + elfround(sizeof(ELF_NOTE_NETBSD_CORE_NAME)) + - elfround(sizeof(cpi)); - if (iocookie) { - cpi.cpi_version = NETBSD_ELFCORE_PROCINFO_VERSION; - cpi.cpi_cpisize = sizeof(cpi); - cpi.cpi_signo = p->p_sigctx.ps_signo; - cpi.cpi_sigcode = p->p_sigctx.ps_code; - cpi.cpi_siglwp = p->p_sigctx.ps_lwp; + cpi.cpi_version = NETBSD_ELFCORE_PROCINFO_VERSION; + cpi.cpi_cpisize = sizeof(cpi); + cpi.cpi_signo = p->p_sigctx.ps_signo; + cpi.cpi_sigcode = p->p_sigctx.ps_code; + cpi.cpi_siglwp = p->p_sigctx.ps_lwp; - /* - * XXX This should be per-LWP. - */ - ss1 = p->p_sigpend.sp_set; - sigemptyset(&ss2); - LIST_FOREACH(l0, &p->p_lwps, l_sibling) { - sigplusset(&l0->l_sigpend.sp_set, &ss1); - sigplusset(&l0->l_sigmask, &ss2); - } - memcpy(&cpi.cpi_sigpend, &ss1, sizeof(cpi.cpi_sigpend)); - memcpy(&cpi.cpi_sigmask, &ss2, sizeof(cpi.cpi_sigmask)); - memcpy(&cpi.cpi_sigignore, &p->p_sigctx.ps_sigignore, - sizeof(cpi.cpi_sigignore)); - memcpy(&cpi.cpi_sigcatch, &p->p_sigctx.ps_sigcatch, - sizeof(cpi.cpi_sigcatch)); - - cpi.cpi_pid = p->p_pid; - mutex_enter(proc_lock); - cpi.cpi_ppid = p->p_pptr->p_pid; - cpi.cpi_pgrp = p->p_pgid; - cpi.cpi_sid = p->p_session->s_sid; - mutex_exit(proc_lock); - - cpi.cpi_ruid = kauth_cred_getuid(l->l_cred); - cpi.cpi_euid = kauth_cred_geteuid(l->l_cred); - cpi.cpi_svuid = kauth_cred_getsvuid(l->l_cred); - - cpi.cpi_rgid = kauth_cred_getgid(l->l_cred); - cpi.cpi_egid = kauth_cred_getegid(l->l_cred); - cpi.cpi_svgid = kauth_cred_getsvgid(l->l_cred); - - cpi.cpi_nlwps = p->p_nlwps; - (void)strncpy(cpi.cpi_name, p->p_comm, sizeof(cpi.cpi_name)); - cpi.cpi_name[sizeof(cpi.cpi_name) - 1] = '\0'; - - nhdr.n_namesz = sizeof(ELF_NOTE_NETBSD_CORE_NAME); - nhdr.n_descsz = sizeof(cpi); - nhdr.n_type = ELF_NOTE_NETBSD_CORE_PROCINFO; - - error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr, - ELF_NOTE_NETBSD_CORE_NAME "\0\0\0", &cpi); - if (error) - return (error); + /* + * XXX This should be per-LWP. + */ + ss1 = p->p_sigpend.sp_set; + sigemptyset(&ss2); + LIST_FOREACH(l0, &p->p_lwps, l_sibling) { + sigplusset(&l0->l_sigpend.sp_set, &ss1); + sigplusset(&l0->l_sigmask, &ss2); } + memcpy(&cpi.cpi_sigpend, &ss1, sizeof(cpi.cpi_sigpend)); + memcpy(&cpi.cpi_sigmask, &ss2, sizeof(cpi.cpi_sigmask)); + memcpy(&cpi.cpi_sigignore, &p->p_sigctx.ps_sigignore, + sizeof(cpi.cpi_sigignore)); + memcpy(&cpi.cpi_sigcatch, &p->p_sigctx.ps_sigcatch, + sizeof(cpi.cpi_sigcatch)); - size += notesize; + cpi.cpi_pid = p->p_pid; + mutex_enter(proc_lock); + cpi.cpi_ppid = p->p_pptr->p_pid; + cpi.cpi_pgrp = p->p_pgid; + cpi.cpi_sid = p->p_session->s_sid; + mutex_exit(proc_lock); + + cpi.cpi_ruid = kauth_cred_getuid(l->l_cred); + cpi.cpi_euid = kauth_cred_geteuid(l->l_cred); + cpi.cpi_svuid = kauth_cred_getsvuid(l->l_cred); + + cpi.cpi_rgid = kauth_cred_getgid(l->l_cred); + cpi.cpi_egid = kauth_cred_getegid(l->l_cred); + cpi.cpi_svgid = kauth_cred_getsvgid(l->l_cred); + + cpi.cpi_nlwps = p->p_nlwps; + (void)strncpy(cpi.cpi_name, p->p_comm, sizeof(cpi.cpi_name)); + cpi.cpi_name[sizeof(cpi.cpi_name) - 1] = '\0'; + + nhdr.n_namesz = sizeof(ELF_NOTE_NETBSD_CORE_NAME); + nhdr.n_descsz = sizeof(cpi); + nhdr.n_type = ELF_NOTE_NETBSD_CORE_PROCINFO; + + ELFNAMEEND(coredump_savenote)(ns, &nhdr, ELF_NOTE_NETBSD_CORE_NAME, + &cpi); /* XXX Add hook for machdep per-proc notes. */ @@ -374,10 +391,9 @@ ELFNAMEEND(coredump_notes)(struct proc *p, struct lwp *l, * Now write the register info for the thread that caused the * coredump. */ - error = ELFNAMEEND(coredump_note)(p, l, iocookie, ¬esize); + error = ELFNAMEEND(coredump_note)(l, ns); if (error) return (error); - size += notesize; /* * Now, for each LWP, write the register info and any other @@ -387,98 +403,100 @@ ELFNAMEEND(coredump_notes)(struct proc *p, struct lwp *l, LIST_FOREACH(l0, &p->p_lwps, l_sibling) { if (l0 == l) /* we've taken care of this thread */ continue; - error = ELFNAMEEND(coredump_note)(p, l0, iocookie, ¬esize); + error = ELFNAMEEND(coredump_note)(l0, ns); if (error) return (error); - size += notesize; } - *sizep = size; return (0); } static int -ELFNAMEEND(coredump_note)(struct proc *p, struct lwp *l, - struct coredump_iostate *iocookie, size_t *sizep) +ELFNAMEEND(coredump_note)(struct lwp *l, struct note_state *ns) { Elf_Nhdr nhdr; - int size, notesize, error; + int error; int namesize; - char name[64+ELFROUNDSIZE]; + char name[64]; elf_reg intreg; #ifdef PT_GETFPREGS elf_fpreg freg; + size_t freglen; #endif - size = 0; - - snprintf(name, sizeof(name)-ELFROUNDSIZE, "%s@%d", + snprintf(name, sizeof(name), "%s@%d", ELF_NOTE_NETBSD_CORE_NAME, l->l_lid); namesize = strlen(name) + 1; - memset(name + namesize, 0, elfround(namesize) - namesize); - notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(intreg)); - if (iocookie) { - error = elf_process_read_regs(l, &intreg); - if (error) - return (error); + error = elf_process_read_regs(l, &intreg); + if (error) + return (error); - nhdr.n_namesz = namesize; - nhdr.n_descsz = sizeof(intreg); - nhdr.n_type = PT_GETREGS; + nhdr.n_namesz = namesize; + nhdr.n_descsz = sizeof(intreg); + nhdr.n_type = PT_GETREGS; - error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr, - name, &intreg); - if (error) - return (error); - - } - size += notesize; + ELFNAMEEND(coredump_savenote)(ns, &nhdr, name, &intreg); #ifdef PT_GETFPREGS - notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(freg)); - if (iocookie) { - size_t freglen = sizeof(freg); + freglen = sizeof(freg); #ifdef __HAVE_PROCESS_XFPREGS - error = elf_process_read_xfpregs(l, &freg, &freglen); + error = elf_process_read_xfpregs(l, &freg, &freglen); #else - error = elf_process_read_fpregs(l, &freg); + error = elf_process_read_fpregs(l, &freg); #endif - if (error) - return (error); + if (error) + return (error); - nhdr.n_namesz = namesize; - nhdr.n_descsz = freglen; - nhdr.n_type = PT_GETFPREGS; + nhdr.n_namesz = namesize; + nhdr.n_descsz = freglen; + nhdr.n_type = PT_GETFPREGS; - error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr, - name, &freg); - if (error) - return (error); - } - size += notesize; + ELFNAMEEND(coredump_savenote)(ns, &nhdr, name, &freg); #endif - *sizep = size; /* XXX Add hook for machdep per-LWP notes. */ return (0); } -int -ELFNAMEEND(coredump_writenote)(struct proc *p, struct coredump_iostate *cookie, - Elf_Nhdr *nhdr, const char *name, void *data) +static void +save_note_bytes(struct note_state *ns, const void *data, size_t len) { - int error; + struct note_buf *nb = ns->ns_last; + size_t copylen; + unsigned char *wp; - error = coredump_write(cookie, UIO_SYSSPACE, nhdr, sizeof(*nhdr)); - if (error) - return error; + /* + * Just copy the data into a buffer list. + * All but the last buffer is full. + */ + for (;;) { + copylen = min(len, sizeof nb->nb_data - ns->ns_offset); + wp = nb->nb_data + ns->ns_offset; + memcpy(wp, data, copylen); + if (copylen == len) + break; + nb->nb_next = kmem_alloc(sizeof *nb->nb_next, KM_SLEEP); + nb = nb->nb_next; + ns->ns_last = nb; + ns->ns_count++; + ns->ns_offset = 0; + len -= copylen; + data = (const unsigned char *)data + copylen; + } - error = coredump_write(cookie, UIO_SYSSPACE, name, - elfround(nhdr->n_namesz)); - if (error) - return error; + while (copylen & (ELFROUNDSIZE - 1)) + wp[copylen++] = 0; - return coredump_write(cookie, UIO_SYSSPACE, data, nhdr->n_descsz); + ns->ns_offset += copylen; +} + +void +ELFNAMEEND(coredump_savenote)(struct note_state *ns, Elf_Nhdr *nhdr, + const char *name, void *data) +{ + save_note_bytes(ns, nhdr, sizeof (*nhdr)); + save_note_bytes(ns, name, nhdr->n_namesz); + save_note_bytes(ns, data, nhdr->n_descsz); } #else /* COREDUMP */ diff --git a/sys/kern/kern_core.c b/sys/kern/kern_core.c index f3ac9413b924..dc701c9f48fd 100644 --- a/sys/kern/kern_core.c +++ b/sys/kern/kern_core.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_core.c,v 1.21 2014/01/01 18:57:16 dsl Exp $ */ +/* $NetBSD: kern_core.c,v 1.22 2014/01/03 20:52:47 dsl Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 @@ -37,7 +37,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: kern_core.c,v 1.21 2014/01/01 18:57:16 dsl Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_core.c,v 1.22 2014/01/03 20:52:47 dsl Exp $"); #include #include @@ -303,10 +303,9 @@ coredump_buildname(struct proc *p, char *dst, const char *src, size_t len) } int -coredump_write(struct coredump_iostate *cookie, enum uio_seg segflg, +coredump_write(struct coredump_iostate *io, enum uio_seg segflg, const void *data, size_t len) { - struct coredump_iostate *io = cookie; int error; error = vn_rdwr(UIO_WRITE, io->io_vp, __UNCONST(data), len, @@ -324,3 +323,9 @@ coredump_write(struct coredump_iostate *cookie, enum uio_seg segflg, io->io_offset += len; return (0); } + +off_t +coredump_offset(struct coredump_iostate *io) +{ + return io->io_offset; +} diff --git a/sys/sys/exec.h b/sys/sys/exec.h index 923d933b740b..f945a62d1d6f 100644 --- a/sys/sys/exec.h +++ b/sys/sys/exec.h @@ -1,4 +1,4 @@ -/* $NetBSD: exec.h,v 1.143 2014/01/01 18:57:16 dsl Exp $ */ +/* $NetBSD: exec.h,v 1.144 2014/01/03 20:52:47 dsl Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -275,6 +275,7 @@ int exec_setup_stack (struct lwp *, struct exec_package *); int coredump_write (struct coredump_iostate *, enum uio_seg, const void *, size_t); +off_t coredump_offset (struct coredump_iostate *); void exec_free_emul_arg (struct exec_package *); diff --git a/sys/sys/exec_elf.h b/sys/sys/exec_elf.h index 502abf1c1a7b..16fdee348e1b 100644 --- a/sys/sys/exec_elf.h +++ b/sys/sys/exec_elf.h @@ -1,4 +1,4 @@ -/* $NetBSD: exec_elf.h,v 1.135 2014/01/03 16:54:48 dsl Exp $ */ +/* $NetBSD: exec_elf.h,v 1.136 2014/01/03 20:52:47 dsl Exp $ */ /*- * Copyright (c) 1994 The NetBSD Foundation, Inc. @@ -1250,6 +1250,7 @@ struct elf_args { struct ps_strings; struct coredump_iostate; +struct note_state; #ifdef EXEC_ELF32 int exec_elf32_makecmds(struct lwp *, struct exec_package *); @@ -1257,8 +1258,8 @@ int elf32_copyargs(struct lwp *, struct exec_package *, struct ps_strings *, char **, void *); int coredump_elf32(struct lwp *, struct coredump_iostate *); -int coredump_writenote_elf32(struct proc *, struct coredump_iostate *, - Elf32_Nhdr *, const char *, void *); +void coredump_savenote_elf32(struct note_state *, Elf32_Nhdr *, + const char *, void *); int elf32_check_header(Elf32_Ehdr *, int); #endif @@ -1269,8 +1270,8 @@ int elf64_copyargs(struct lwp *, struct exec_package *, struct ps_strings *, char **, void *); int coredump_elf64(struct lwp *, struct coredump_iostate *); -int coredump_writenote_elf64(struct proc *, struct coredump_iostate *, - Elf64_Nhdr *, const char *, void *); +void coredump_savenote_elf64(struct note_state *, Elf64_Nhdr *, + const char *, void *); int elf64_check_header(Elf64_Ehdr *, int); #endif