From e7a40e26f00ddfe6d952478f6c86a040ecf230b2 Mon Sep 17 00:00:00 2001 From: kurt Date: Wed, 17 Jan 2024 22:22:25 +0000 Subject: [PATCH] Fix core file writing when a file map into memory has later been truncated to be smaller than the mapping. Record which memory segments are backed by vnodes while walking the uvm map and later suppress EFAULT errors caused by the underlying file being truncated. okay miod@ --- sys/kern/exec_elf.c | 31 +++++++++++++++++-------------- sys/kern/kern_sig.c | 7 ++++--- sys/sys/core.h | 4 ++-- sys/sys/exec_elf.h | 6 +++++- sys/uvm/uvm_extern.h | 6 +++--- sys/uvm/uvm_unix.c | 14 ++++++++------ 6 files changed, 39 insertions(+), 29 deletions(-) diff --git a/sys/kern/exec_elf.c b/sys/kern/exec_elf.c index 1ad6e33a76c..33e5cadb76a 100644 --- a/sys/kern/exec_elf.c +++ b/sys/kern/exec_elf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_elf.c,v 1.184 2024/01/16 19:05:01 deraadt Exp $ */ +/* $OpenBSD: exec_elf.c,v 1.185 2024/01/17 22:22:25 kurt Exp $ */ /* * Copyright (c) 1996 Per Fogelstrom @@ -1154,7 +1154,7 @@ coredump_elf(struct proc *p, void *cookie) goto out; error = coredump_write(cookie, UIO_SYSSPACE, ws.psections, - ws.psectionslen); + ws.psectionslen, 0); if (error) goto out; @@ -1193,10 +1193,11 @@ coredump_elf(struct proc *p, void *cookie) if (pent->p_vaddr == p->p_p->ps_sigcode && pent->p_filesz == sigcode_sz) { error = coredump_write(cookie, UIO_SYSSPACE, - (void *)sigcode_va, sigcode_sz); + (void *)sigcode_va, sigcode_sz, 0); } else { error = coredump_write(cookie, UIO_USERSPACE, - (void *)(vaddr_t)pent->p_vaddr, pent->p_filesz); + (void *)(vaddr_t)pent->p_vaddr, pent->p_filesz, + (pent->p_flags & PF_ISVNODE)); } if (error) goto out; @@ -1298,7 +1299,7 @@ coredump_setup_elf(int segment_count, void *cookie) } /* Write out the ELF header. */ - error = coredump_write(ws->iocookie, UIO_SYSSPACE, &ehdr, sizeof(ehdr)); + error = coredump_write(ws->iocookie, UIO_SYSSPACE, &ehdr, sizeof(ehdr), 0); if (error) return error; @@ -1309,11 +1310,11 @@ coredump_setup_elf(int segment_count, void *cookie) if (ehdr.e_shnum != 0) { Elf_Shdr shdr = { .sh_info = ws->npsections }; error = coredump_write(ws->iocookie, UIO_SYSSPACE, &shdr, - sizeof shdr); + sizeof shdr, 0); if (error) return error; error = coredump_write(ws->iocookie, UIO_SYSSPACE, &shstrtab, - sizeof(shstrtab.shdr) + sizeof(shstrtab.shstrtab)); + sizeof(shstrtab.shdr) + sizeof(shstrtab.shstrtab), 0); if (error) return error; } @@ -1348,7 +1349,7 @@ coredump_setup_elf(int segment_count, void *cookie) int coredump_walk_elf(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot, - int nsegment, void *cookie) + int isvnode, int nsegment, void *cookie) { struct writesegs_state *ws = cookie; Elf_Phdr phdr; @@ -1370,6 +1371,8 @@ coredump_walk_elf(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot, phdr.p_flags |= PF_W; if (prot & PROT_EXEC) phdr.p_flags |= PF_X; + if (isvnode) + phdr.p_flags |= PF_ISVNODE; phdr.p_align = PAGE_SIZE; ws->secoff += phdr.p_filesz; @@ -1446,17 +1449,17 @@ coredump_notes_elf(struct proc *p, void *iocookie, size_t *sizep) nhdr.type = NT_OPENBSD_AUXV; error = coredump_write(iocookie, UIO_SYSSPACE, - &nhdr, sizeof(nhdr)); + &nhdr, sizeof(nhdr), 0); if (error) return (error); error = coredump_write(iocookie, UIO_SYSSPACE, - "OpenBSD", elfround(nhdr.namesz)); + "OpenBSD", elfround(nhdr.namesz), 0); if (error) return (error); error = coredump_write(iocookie, UIO_USERSPACE, - (caddr_t)pr->ps_auxinfo, nhdr.descsz); + (caddr_t)pr->ps_auxinfo, nhdr.descsz, 0); if (error) return (error); } @@ -1596,15 +1599,15 @@ coredump_writenote_elf(struct proc *p, void *cookie, Elf_Note *nhdr, { int error; - error = coredump_write(cookie, UIO_SYSSPACE, nhdr, sizeof(*nhdr)); + error = coredump_write(cookie, UIO_SYSSPACE, nhdr, sizeof(*nhdr), 0); if (error) return error; error = coredump_write(cookie, UIO_SYSSPACE, name, - elfround(nhdr->namesz)); + elfround(nhdr->namesz), 0); if (error) return error; - return coredump_write(cookie, UIO_SYSSPACE, data, nhdr->descsz); + return coredump_write(cookie, UIO_SYSSPACE, data, nhdr->descsz, 0); } #endif /* !SMALL_KERNEL */ diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 4714c829403..f20b516bab0 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sig.c,v 1.320 2023/10/06 08:58:13 claudio Exp $ */ +/* $OpenBSD: kern_sig.c,v 1.321 2024/01/17 22:22:25 kurt Exp $ */ /* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */ /* @@ -1745,7 +1745,8 @@ out: #ifndef SMALL_KERNEL int -coredump_write(void *cookie, enum uio_seg segflg, const void *data, size_t len) +coredump_write(void *cookie, enum uio_seg segflg, const void *data, size_t len, + int isvnode) { struct coredump_iostate *io = cookie; off_t coffset = 0; @@ -1766,7 +1767,7 @@ coredump_write(void *cookie, enum uio_seg segflg, const void *data, size_t len) (caddr_t)data + coffset, chunk, io->io_offset + coffset, segflg, IO_UNIT, io->io_cred, NULL, io->io_proc); - if (error) { + if (error && (error != EFAULT || !isvnode)) { struct process *pr = io->io_proc->p_p; if (error == ENOSPC) diff --git a/sys/sys/core.h b/sys/sys/core.h index 284ef50329f..e5260c20e72 100644 --- a/sys/sys/core.h +++ b/sys/sys/core.h @@ -1,4 +1,4 @@ -/* $OpenBSD: core.h,v 1.8 2022/02/22 17:14:13 deraadt Exp $ */ +/* $OpenBSD: core.h,v 1.9 2024/01/17 22:22:25 kurt Exp $ */ /* $NetBSD: core.h,v 1.4 1994/10/29 08:20:14 cgd Exp $ */ /* @@ -94,6 +94,6 @@ struct coreseg { }; #else -int coredump_write(void *, enum uio_seg, const void *, size_t); +int coredump_write(void *, enum uio_seg, const void *, size_t, int); void coredump_unmap(void *, vaddr_t, vaddr_t); #endif diff --git a/sys/sys/exec_elf.h b/sys/sys/exec_elf.h index a64587050b3..e207b721d17 100644 --- a/sys/sys/exec_elf.h +++ b/sys/sys/exec_elf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_elf.h,v 1.102 2023/12/08 13:58:41 deraadt Exp $ */ +/* $OpenBSD: exec_elf.h,v 1.103 2024/01/17 22:22:25 kurt Exp $ */ /* * Copyright (c) 1995, 1996 Erik Theisen. All rights reserved. * @@ -496,6 +496,10 @@ typedef struct { #define PF_OPENBSD_MUTABLE 0x08000000 /* Mutable */ +#ifdef _KERNEL +#define PF_ISVNODE 0x00100000 /* For coredump segments */ +#endif + /* Dynamic structure */ typedef struct { Elf32_Sword d_tag; /* controls meaning of d_val */ diff --git a/sys/uvm/uvm_extern.h b/sys/uvm/uvm_extern.h index 53461acb3d4..e7701532448 100644 --- a/sys/uvm/uvm_extern.h +++ b/sys/uvm/uvm_extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_extern.h,v 1.171 2023/08/18 09:18:52 claudio Exp $ */ +/* $OpenBSD: uvm_extern.h,v 1.172 2024/01/17 22:22:25 kurt Exp $ */ /* $NetBSD: uvm_extern.h,v 1.57 2001/03/09 01:02:12 chs Exp $ */ /* @@ -441,8 +441,8 @@ void uvm_pmr_use_inc(paddr_t, paddr_t); void uvm_swap_init(void); typedef int uvm_coredump_setup_cb(int _nsegment, void *_cookie); typedef int uvm_coredump_walk_cb(vaddr_t _start, vaddr_t _realend, - vaddr_t _end, vm_prot_t _prot, int _nsegment, - void *_cookie); + vaddr_t _end, vm_prot_t _prot, int _isvnode, + int _nsegment, void *_cookie); int uvm_coredump_walkmap(struct proc *_p, uvm_coredump_setup_cb *_setup, uvm_coredump_walk_cb *_walk, void *_cookie); diff --git a/sys/uvm/uvm_unix.c b/sys/uvm/uvm_unix.c index 3ec0b00a0fb..56ee0370317 100644 --- a/sys/uvm/uvm_unix.c +++ b/sys/uvm/uvm_unix.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_unix.c,v 1.72 2023/01/13 23:02:44 kettenis Exp $ */ +/* $OpenBSD: uvm_unix.c,v 1.73 2024/01/17 22:22:25 kurt Exp $ */ /* $NetBSD: uvm_unix.c,v 1.18 2000/09/13 15:00:25 thorpej Exp $ */ /* @@ -198,7 +198,7 @@ uvm_coredump_walk_amap(struct vm_map_entry *entry, int *nsegmentp, end = pos + (i << PAGE_SHIFT); if (start != end) { error = (*walk)(start, realend, end, prot, - nsegment, cookie); + 0, nsegment, cookie); if (error) return error; nsegment++; @@ -210,7 +210,7 @@ uvm_coredump_walk_amap(struct vm_map_entry *entry, int *nsegmentp, if (!absent) realend = entry_end; - error = (*walk)(start, realend, entry_end, prot, nsegment, cookie); + error = (*walk)(start, realend, entry_end, prot, 0, nsegment, cookie); *nsegmentp = nsegment + 1; return error; } @@ -257,7 +257,7 @@ uvm_should_coredump(struct proc *p, struct vm_map_entry *entry) /* do nothing callback for uvm_coredump_walk_amap() */ static int noop(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot, - int nsegment, void *cookie) + int isvnode, int nsegment, void *cookie) { return 0; } @@ -280,7 +280,7 @@ uvm_coredump_walkmap(struct proc *p, uvm_coredump_setup_cb *setup, struct vm_map_entry *entry; vaddr_t end; int refed_amaps = 0; - int nsegment, error; + int nsegment, error, isvnode; /* * Walk the map once to count the segments. If an amap is @@ -350,8 +350,10 @@ uvm_coredump_walkmap(struct proc *p, uvm_coredump_setup_cb *setup, if (end > VM_MAXUSER_ADDRESS) end = VM_MAXUSER_ADDRESS; + isvnode = (entry->object.uvm_obj != NULL && + UVM_OBJ_IS_VNODE(entry->object.uvm_obj)); error = (*walk)(entry->start, end, end, entry->protection, - nsegment, cookie); + isvnode, nsegment, cookie); if (error) break; nsegment++; -- 2.20.1