git: 7893a7edb43e - main - coredump: Don't assume that the number of ELF segments is consistent
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 29 Jul 2026 17:48:00 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=7893a7edb43e7c7328f8f46e3929e2412b45d048
commit 7893a7edb43e7c7328f8f46e3929e2412b45d048
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-07-27 15:42:49 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-07-29 17:45:02 +0000
coredump: Don't assume that the number of ELF segments is consistent
In an ELF coredump, each dumped vm_map_entry is represented by a
segment. __elfN(coredump) first computes the number of segments by
looping over the vm_map entries (in each_dumpable_segment()), then
allocates a buffer to hold the ELF header and program headers, then
loops over the entries again to populate the program headers.
each_dumpable_segment() holds the vm_map read lock, but that lock is
dropped between the two calls. If the map is shared with another
process, via rfork(), then the map can change. cb_put_phdr() did not
account for this, and so could write out of bounds.
Add a check to prevent this; simply do not write out excess segments.
Approved by: so
Security: FreeBSD-SA-26:55.elf
Security: CVE-2026-58088
Reported by: Maik Muench of Secfault Security
Reviewed by: kib, emaste
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D58416
---
sys/kern/imgact_elf.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index 7c78d66b3648..08e9718c4d24 100644
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -1585,6 +1585,8 @@ typedef void (*segment_callback)(vm_map_entry_t, void *);
struct phdr_closure {
Elf_Phdr *phdr; /* Program header to fill in */
Elf_Off offset; /* Offset of segment in core file */
+ int numsegs; /* Maximum number of segments */
+ int nextseg; /* Next segment to fill in */
};
struct note_info {
@@ -1701,10 +1703,15 @@ __elfN(coredump)(struct thread *td, struct coredump_writer *cdw, off_t limit, in
}
/*
- * Allocate memory for building the header, fill it up,
- * and write it out following the notes.
+ * Allocate memory for building the header, fill it up, and write it out
+ * following the notes.
+ *
+ * Note that a process sharing our vmspace might be concurrently
+ * mutating the map, in which case we could populate fewer than
+ * seginfo.count headers. Zero the buffer to ensure that unpopulated
+ * headers are still initialized.
*/
- hdr = malloc(hdrsize, M_TEMP, M_WAITOK);
+ hdr = malloc(hdrsize, M_TEMP, M_WAITOK | M_ZERO);
error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst,
notesz, flags);
@@ -1757,6 +1764,11 @@ cb_put_phdr(vm_map_entry_t entry, void *closure)
struct phdr_closure *phc = (struct phdr_closure *)closure;
Elf_Phdr *phdr = phc->phdr;
+ if (phc->nextseg >= phc->numsegs) {
+ /* Only write as many headers as we have space for. */
+ return;
+ }
+
phc->offset = round_page(phc->offset);
phdr->p_type = PT_LOAD;
@@ -1769,6 +1781,8 @@ cb_put_phdr(vm_map_entry_t entry, void *closure)
phc->offset += phdr->p_filesz;
phc->phdr++;
+
+ phc->nextseg++;
}
/*
@@ -2031,6 +2045,8 @@ __elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs,
/* All the writable segments from the program. */
phc.phdr = phdr;
phc.offset = round_page(hdrsize + notesz);
+ phc.numsegs = numsegs;
+ phc.nextseg = 0;
each_dumpable_segment(td, cb_put_phdr, &phc, flags);
}