Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3834,6 +3834,7 @@ kern/kern_ubsan.c optional kubsan kern/kern_umtx.c standard kern/kern_uuid.c standard +kern/kern_vnodedumper.c standard kern/kern_xxx.c standard kern/link_elf.c standard kern/linker_if.m standard Index: sys/kern/kern_shutdown.c =================================================================== --- sys/kern/kern_shutdown.c +++ sys/kern/kern_shutdown.c @@ -383,6 +383,17 @@ printf("%lds\n", (long)ts.tv_sec); } +/* + * Set up a context that can be extracted from the dump. + */ +void +dump_savectx(void) +{ + + savectx(&dumppcb); + dumptid = curthread->td_tid; +} + int doadump(boolean_t textdump) { @@ -395,8 +406,7 @@ if (TAILQ_EMPTY(&dumper_configs)) return (ENXIO); - savectx(&dumppcb); - dumptid = curthread->td_tid; + dump_savectx(); dumping++; coredump = TRUE; Index: sys/kern/kern_vnodedumper.c =================================================================== --- /dev/null +++ sys/kern/kern_vnodedumper.c @@ -0,0 +1,225 @@ +/*- + * Copyright (c) 2021 Juniper Networks + * + * This software was developed by Mitchell Horne + * under sponsorship from Juniper Networks and Klara Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static dumper_t vnode_dump; +static dumper_start_t vnode_dumper_start; +static dumper_hdr_t vnode_write_headers; + +static int livedump_running; + +/* + * Invoke a live minidump on the system. + */ +static int +sysctl_live_dump(SYSCTL_HANDLER_ARGS) +{ +#if MINIDUMP_PAGE_TRACKING == 1 + struct dumperinfo di; + char *pathbuf; + int error; + + if (req->newptr == NULL) + return (EINVAL); + if (req->newlen == 0) + return (EINVAL); + if (req->newlen >= PATH_MAX) + return (ENAMETOOLONG); + + pathbuf = malloc(PATH_MAX, M_TEMP, M_WAITOK); + error = SYSCTL_IN(req, pathbuf, req->newlen); + if (error != 0) + return (error); + pathbuf[req->newlen] = '\0'; + + /* Set up dumper */ + bzero(&di, sizeof(di)); + di.dumper_start = vnode_dumper_start; + di.dumper = vnode_dump; + di.dumper_hdr = vnode_write_headers; + di.blocksize = PAGE_SIZE; /* Arbitrary. */ + di.maxiosize = MAXDUMPPGS * PAGE_SIZE; + di.mediasize = 0; + di.mediaoffset = 0; + di.priv = pathbuf; /* pass path via priv */ + + /* Only allow one livedump to proceed at a time. */ + while (!atomic_cmpset_int(&livedump_running, 0, 1)) + pause("livedump", hz / 2); + + dump_savectx(); + error = minidumpsys(&di, true); + livedump_running = 0; + + return (error); +#else + return (EOPNOTSUPP); +#endif /* MINIDUMP_PAGE_TRACKING == 1 */ +} + +SYSCTL_PROC(_kern, OID_AUTO, livedump, + CTLTYPE_STRING | CTLFLAG_WR | CTLFLAG_MPSAFE | CTLFLAG_CAPWR, + NULL, 0, sysctl_live_dump, "A", + "Start live dump of system"); + +/* + * Perform any initialization needed prior to transmitting the kernel core. + * + * Namely, this routine opens/creates the vnode for the file path provided in + * di->priv. If successful di->priv is reused to hold the vnode pointer. + */ +int +vnode_dumper_start(struct dumperinfo *di, void *key, uint32_t keysize) +{ + struct nameidata nd; + struct vnode *vp; + struct vattr vattr; + struct ucred *cred; + char *path; + int flags; + int error; + + MPASS(di->priv != NULL); + path = di->priv; + + if (keysize > 0) { + /* TODO ? */ + printf("%s: keysize > 0 unhandled\n", __func__); + return (EINVAL); + } + + /* Instantiate a vnode for provided path. */ + flags = FWRITE | O_NOFOLLOW | O_CREAT; + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_SYSSPACE, path); + error = vn_open_cred(&nd, &flags, S_IRUSR | S_IWUSR, + VN_OPEN_NOAUDIT | VN_OPEN_NAMECACHE, curthread->td_ucred, NULL); + if (error != 0) + return (error); + + /* Replace priv with the vnode pointer, now that we've obtained it. */ + vp = nd.ni_vp; + di->priv = vp; + NDFREE(&nd, NDF_ONLY_PNBUF); + + /* + * Don't dump to non-regular files or files with links. + * Do not dump into system files. Effective user must own the corefile. + */ + cred = curthread->td_ucred; + if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred) != 0 || + vattr.va_nlink != 1 || (vp->v_vflag & VV_SYSTEM) != 0 || + vattr.va_uid != cred->cr_uid) { + VOP_UNLOCK(vp); + vn_close(vp, FWRITE, cred, curthread); + return (EFAULT); + } + + return (0); +} + +/* + * Callback from dumpsys() to dump a chunk of memory. + * + * Parameters: + * arg Opaque private pointer to vnode + * virtual Virtual address (where to read the data from) + * physical Physical memory address (unused) + * offset Offset from start of core file + * length Data length + * + * Return value: + * 0 on success + * errno on error + */ +int +vnode_dump(void *arg, void *virtual, vm_offset_t physical __unused, + off_t offset, size_t length) +{ + struct vnode *vp; + int error = 0; + + vp = arg; + MPASS(vp != NULL); + ASSERT_VOP_LOCKED(vp, __func__); + + /* Done? */ + if (virtual == NULL) + return (0); + + error = vn_rdwr(UIO_WRITE, vp, virtual, length, offset, UIO_SYSSPACE, + IO_NODELOCKED, curthread->td_ucred, NOCRED, NULL, curthread); + if (error != 0) { + printf("%s: error writing to vnode %p: %d\n", __func__, vp, + error); + VOP_UNLOCK(vp); + vn_close(vp, FWRITE, curthread->td_ucred, curthread); + } + + return (error); +} + +/* + * Callback from dumpsys() to write out the dump header, placed at the end. + */ +int +vnode_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh) +{ + struct vnode *vp; + int error; + + vp = di->priv; + MPASS(vp != NULL); + ASSERT_VOP_LOCKED(vp, __func__); + + /* Write the kernel dump header to the end of the file. */ + error = vn_rdwr(UIO_WRITE, vp, kdh, sizeof(*kdh), di->dumpoff, + UIO_SYSSPACE, IO_NODELOCKED, curthread->td_ucred, NOCRED, NULL, + curthread); + if (error != 0) + printf("%s: error writing to vnode %p: %d\n", __func__, vp, + error); + + /* Unconditionally close the vnode; we are finished with it. */ + VOP_UNLOCK(vp); + vn_close(vp, FWRITE, curthread->td_ucred, curthread); + return (error); +} Index: sys/sys/conf.h =================================================================== --- sys/sys/conf.h +++ sys/sys/conf.h @@ -362,6 +362,7 @@ extern int dumping; /* system is dumping */ +void dump_savectx(void); int doadump(boolean_t); struct diocskerneldump_arg; int dumper_insert(const struct dumperinfo *di_template, const char *devname,