summaryrefslogtreecommitdiffstats
path: root/sys/kern/imgact_elf.c
diff options
context:
space:
mode:
authoravg <avg@FreeBSD.org>2017-01-26 09:46:34 +0000
committeravg <avg@FreeBSD.org>2017-01-26 09:46:34 +0000
commit946a6f1a286e12b9c3eb4be1b1aabd8890c38f06 (patch)
treeafd194ae31df196bb3cb30f7de475199d3eaf114 /sys/kern/imgact_elf.c
parentc810b9890f96e4c2e99985c18dfb3dd6a4301741 (diff)
downloadFreeBSD-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.c41
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);
}
/*
OpenPOWER on IntegriCloud