diff options
author | Robert Mustacchi <rm@fingolfin.org> | 2021-07-21 07:15:49 -0700 |
---|---|---|
committer | Robert Mustacchi <rm@fingolfin.org> | 2021-08-03 07:36:50 -0700 |
commit | 5d228828cbfb65f9632a1eedca4291380fca8303 (patch) | |
tree | a39f017c1ef850ec19fe84c1aebac845a68c15ad /usr/src | |
parent | f198607dfbf80950ec14f7c1fecf634f2da7ebb0 (diff) | |
download | illumos-gate-5d228828cbfb65f9632a1eedca4291380fca8303.tar.gz |
13926 core files can fail to dump leading large sections
13927 core dump of PROT_NONE segment leads to confusing behavior
Reviewed by: Jason King <jason.brian.king@gmail.com>
Reviewed by: Patrick Mooney <pmooney@pfmooney.com>
Reviewed by: Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/lib/libproc/common/Pgcore.c | 16 | ||||
-rw-r--r-- | usr/src/uts/common/exec/elf/elf.c | 61 |
2 files changed, 65 insertions, 12 deletions
diff --git a/usr/src/lib/libproc/common/Pgcore.c b/usr/src/lib/libproc/common/Pgcore.c index e4d4437baa..8890d85efc 100644 --- a/usr/src/lib/libproc/common/Pgcore.c +++ b/usr/src/lib/libproc/common/Pgcore.c @@ -28,6 +28,7 @@ * Copyright 2018 Joyent, Inc. * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. + * Copyright 2021 Oxide Computer Company */ #define _STRUCTURED_PROC 1 @@ -920,6 +921,18 @@ dump_map(void *data, const prmap_t *pmp, const char *name) n = 0; while (n < pmp->pr_size) { size_t csz = MIN(pmp->pr_size - n, pgc->pgc_chunksz); + ssize_t ret; + + /* + * If we happen to have a PROT_NONE mapping, don't try to read + * from the address space. + */ + if ((pmp->pr_mflags & (MA_READ | MA_WRITE | MA_EXEC)) == 0) { + bzero(pgc->pgc_chunk, csz); + ret = csz; + } else { + ret = Pread(P, pgc->pgc_chunk, csz, pmp->pr_vaddr + n); + } /* * If we can't read out part of the victim's address @@ -929,8 +942,7 @@ dump_map(void *data, const prmap_t *pmp, const char *name) * PF_SUNW_FAILURE flag and store the errno where the * mapping would have been. */ - if (Pread(P, pgc->pgc_chunk, csz, pmp->pr_vaddr + n) != csz || - gc_pwrite64(pgc->pgc_fd, pgc->pgc_chunk, csz, + if (ret != csz || gc_pwrite64(pgc->pgc_fd, pgc->pgc_chunk, csz, *pgc->pgc_doff + n) != 0) { int err = errno; (void) gc_pwrite64(pgc->pgc_fd, &err, sizeof (err), diff --git a/usr/src/uts/common/exec/elf/elf.c b/usr/src/uts/common/exec/elf/elf.c index 9e6b6bf69e..771a4f8761 100644 --- a/usr/src/uts/common/exec/elf/elf.c +++ b/usr/src/uts/common/exec/elf/elf.c @@ -27,6 +27,7 @@ /* All Rights Reserved */ /* * Copyright (c) 2019, Joyent, Inc. + * Copyright 2021 Oxide Computer Company */ #include <sys/types.h> @@ -1620,8 +1621,10 @@ copy_scn(Shdr *src, vnode_t *src_vp, Shdr *dst, vnode_t *dst_vp, Off *doffset, #ifdef _ELF32_COMPAT extern size_t elf_datasz_max; +extern size_t elf_zeropg_sz; #else size_t elf_datasz_max = 1 * 1024 * 1024; +size_t elf_zeropg_sz = 4 * 1024; #endif /* @@ -1705,8 +1708,10 @@ process_scns(core_content_t content, proc_t *p, cred_t *credp, vnode_t *vp, off = ehdr.e_shentsize; for (j = 1; j < nshdrs; j++, off += ehdr.e_shentsize) { Shdr *symtab = NULL, *strtab; + size_t allocsz; shdr = (Shdr *)(shbase + off); + allocsz = MIN(shdr->sh_size, elf_datasz_max); if (shdr->sh_name >= shstrsize) continue; @@ -1725,12 +1730,11 @@ process_scns(core_content_t content, proc_t *p, cred_t *credp, vnode_t *vp, } if (v != NULL && i < nv - 1) { - if (shdr->sh_size > datasz && - shdr->sh_size <= elf_datasz_max) { + if (allocsz > datasz) { if (data != NULL) kmem_free(data, datasz); - datasz = shdr->sh_size; + datasz = allocsz; data = kmem_alloc(datasz, KM_SLEEP); } @@ -1795,12 +1799,12 @@ process_scns(core_content_t content, proc_t *p, cred_t *credp, vnode_t *vp, if (v != NULL && i < nv - 2) { sz = MAX(symtab->sh_size, strtab->sh_size); - if (sz > datasz && - sz <= elf_datasz_max) { + allocsz = MIN(sz, elf_datasz_max); + if (allocsz > datasz) { if (data != NULL) kmem_free(data, datasz); - datasz = sz; + datasz = allocsz; data = kmem_alloc(datasz, KM_SLEEP); } @@ -1934,6 +1938,7 @@ elfcore(vnode_t *vp, proc_t *p, cred_t *credp, rlim64_t rlimit, int sig, size_t phdrsz, shdrsz; Ehdr *ehdr; Phdr *v; + void *zeropg = NULL; caddr_t brkbase; size_t brksize; caddr_t stkbase; @@ -2215,6 +2220,13 @@ exclude: continue; /* + * If we hit a region that was mapped PROT_NONE then we cannot + * continue dumping this normally as the kernel would be unable + * to read from the page and that would result in us failing to + * dump the page. As such, any region mapped PROT_NONE, we dump + * as a zero-filled page such that this is still represented in + * the map. + * * If dumping out this segment fails, rather than failing * the core dump entirely, we reset the size of the mapping * to zero to indicate that the data is absent from the core @@ -2222,10 +2234,36 @@ exclude: * this from mappings that were excluded due to the core file * content settings. */ - if ((error = core_seg(p, vp, v[i].p_offset, - (caddr_t)(uintptr_t)v[i].p_vaddr, v[i].p_filesz, - rlimit, credp)) == 0) { - continue; + if ((v[i].p_flags & (PF_R | PF_W | PF_X)) == 0) { + size_t towrite = v[i].p_filesz; + size_t curoff = 0; + + if (zeropg == NULL) { + zeropg = kmem_zalloc(elf_zeropg_sz, KM_SLEEP); + } + + error = 0; + while (towrite != 0) { + size_t len = MIN(towrite, elf_zeropg_sz); + + error = core_write(vp, UIO_SYSSPACE, + v[i].p_offset + curoff, zeropg, len, rlimit, + credp); + if (error != 0) + break; + + towrite -= len; + curoff += len; + } + + if (error == 0) + continue; + } else { + error = core_seg(p, vp, v[i].p_offset, + (caddr_t)(uintptr_t)v[i].p_vaddr, v[i].p_filesz, + rlimit, credp); + if (error == 0) + continue; } if ((sig = lwp->lwp_cursig) == 0) { @@ -2345,6 +2383,9 @@ exclude: } done: + if (zeropg != NULL) { + kmem_free(zeropg, elf_zeropg_sz); + } kmem_free(bigwad, bigsize); return (error); } |