The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=7893a7edb43e7c7328f8f46e3929e2412b45d048

commit 7893a7edb43e7c7328f8f46e3929e2412b45d048
Author:     Mark Johnston <[email protected]>
AuthorDate: 2026-07-27 15:42:49 +0000
Commit:     Mark Johnston <[email protected]>
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)(&params, seginfo.count, hdr, hdrsize, &notelst,
            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);
 }
 

Reply via email to