diff options
author | avg <avg@FreeBSD.org> | 2017-01-26 09:46:34 +0000 |
---|---|---|
committer | avg <avg@FreeBSD.org> | 2017-01-26 09:46:34 +0000 |
commit | 946a6f1a286e12b9c3eb4be1b1aabd8890c38f06 (patch) | |
tree | afd194ae31df196bb3cb30f7de475199d3eaf114 /sys/kern/imgact_elf.c | |
parent | c810b9890f96e4c2e99985c18dfb3dd6a4301741 (diff) | |
download | FreeBSD-src-946a6f1a286e12b9c3eb4be1b1aabd8890c38f06.zip FreeBSD-src-946a6f1a286e12b9c3eb4be1b1aabd8890c38f06.tar.gz |
MFC r312532: don't abort writing of a core dump after EFAULT
Diffstat (limited to 'sys/kern/imgact_elf.c')
-rw-r--r-- | sys/kern/imgact_elf.c | 41 |
1 files changed, 34 insertions, 7 deletions
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index b1d6c32..a1a24d1 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1160,7 +1160,7 @@ struct coredump_params { static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); -static int core_write(struct coredump_params *, void *, size_t, off_t, +static int core_write(struct coredump_params *, const void *, size_t, off_t, enum uio_seg); static void each_writable_segment(struct thread *, segment_callback, void *); static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t, @@ -1202,7 +1202,14 @@ compress_chunk(struct coredump_params *p, char *base, char *buf, u_int len) while (len > 0) { chunk_len = MIN(len, CORE_BUF_SIZE); - copyin(base, buf, chunk_len); + + /* + * We can get EFAULT error here. + * In that case zero out the current chunk of the segment. + */ + error = copyin(base, buf, chunk_len); + if (error != 0) + bzero(buf, chunk_len); error = gzio_write(p->gzs, buf, chunk_len); if (error != 0) break; @@ -1222,12 +1229,12 @@ core_gz_write(void *base, size_t len, off_t offset, void *arg) #endif /* GZIO */ static int -core_write(struct coredump_params *p, void *base, size_t len, off_t offset, - enum uio_seg seg) +core_write(struct coredump_params *p, const void *base, size_t len, + off_t offset, enum uio_seg seg) { - return (vn_rdwr_inchunks(UIO_WRITE, p->vp, base, len, offset, - seg, IO_UNIT | IO_DIRECT | IO_RANGELOCKED, + return (vn_rdwr_inchunks(UIO_WRITE, p->vp, __DECONST(void *, base), + len, offset, seg, IO_UNIT | IO_DIRECT | IO_RANGELOCKED, p->active_cred, p->file_cred, NULL, p->td)); } @@ -1235,12 +1242,32 @@ static int core_output(void *base, size_t len, off_t offset, struct coredump_params *p, void *tmpbuf) { + int error; #ifdef GZIO if (p->gzs != NULL) return (compress_chunk(p, base, tmpbuf, len)); #endif - return (core_write(p, base, len, offset, UIO_USERSPACE)); + /* + * EFAULT is a non-fatal error that we can get, for example, + * if the segment is backed by a file but extends beyond its + * end. + */ + error = core_write(p, base, len, offset, UIO_USERSPACE); + if (error == EFAULT) { + log(LOG_WARNING, "Failed to fully fault in a core file segment " + "at VA %p with size 0x%zx to be written at offset 0x%jx " + "for process %s\n", base, len, offset, curproc->p_comm); + + /* + * Write a "real" zero byte at the end of the target region + * in the case this is the last segment. + * The intermediate space will be implicitly zero-filled. + */ + error = core_write(p, zero_region, 1, offset + len - 1, + UIO_SYSSPACE); + } + return (error); } /* |