The branch main has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=804daf166406a1b0e22f93b9417b90ed7d3aa1ae

commit 804daf166406a1b0e22f93b9417b90ed7d3aa1ae
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2026-05-28 09:06:33 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2026-05-28 20:59:26 +0000

    imgact_elf: read program headers if not contained in the first page
    
    PR:     295629
    Reviewed by:    markj
    Tested by:      Alex S <[email protected]>
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D57294
---
 sys/kern/imgact_elf.c | 91 +++++++++++++++++++++++++++++++++++----------------
 1 file changed, 62 insertions(+), 29 deletions(-)

diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index 42cdfa08f19b..e3969223c170 100644
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -84,6 +84,8 @@
 #define ELF_NOTE_ROUNDSIZE     4
 #define OLD_EI_BRAND   8
 
+#define        ELF_OFFPAGE_PHNUM       128
+
 /*
  * ELF_ABI_NAME is a string name of the ELF ABI.  ELF_ABI_ID is used
  * to build variable names.
@@ -93,7 +95,7 @@
 
 static int __elfN(check_header)(const Elf_Ehdr *hdr);
 static const Elf_Brandinfo *__elfN(get_brandinfo)(struct image_params *imgp,
-    const char *interp, int32_t *osrel, uint32_t *fctl0);
+    const Elf_Phdr *phdr, const char *interp, int32_t *osrel, uint32_t *fctl0);
 static int __elfN(load_file)(struct thread *td, const char *file, u_long *addr,
     u_long *entry);
 static int __elfN(load_section)(const struct image_params *imgp,
@@ -103,7 +105,7 @@ static int __CONCAT(exec_, __elfN(imgact))(struct 
image_params *imgp);
 static bool __elfN(freebsd_trans_osrel)(const Elf_Note *note,
     int32_t *osrel);
 static bool kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel);
-static bool __elfN(check_note)(struct image_params *imgp,
+static bool __elfN(check_note)(struct image_params *imgp, const Elf_Phdr *phdr,
     const Elf_Brandnote *checknote, int32_t *osrel, bool *has_fctl0,
     uint32_t *fctl0);
 static vm_prot_t __elfN(trans_prot)(Elf_Word);
@@ -339,8 +341,8 @@ __elfN(brand_inuse)(const Elf_Brandinfo *entry)
 }
 
 static const Elf_Brandinfo *
-__elfN(get_brandinfo)(struct image_params *imgp, const char *interp,
-    int32_t *osrel, uint32_t *fctl0)
+__elfN(get_brandinfo)(struct image_params *imgp, const Elf_Phdr *phdr,
+    const char *interp, int32_t *osrel, uint32_t *fctl0)
 {
        const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header;
        const Elf_Brandinfo *bi, *bi_m;
@@ -369,8 +371,8 @@ __elfN(get_brandinfo)(struct image_params *imgp, const char 
*interp,
                        has_fctl0 = false;
                        *fctl0 = 0;
                        *osrel = 0;
-                       ret = __elfN(check_note)(imgp, bi->brand_note, osrel,
-                           &has_fctl0, fctl0);
+                       ret = __elfN(check_note)(imgp, phdr, bi->brand_note,
+                           osrel, &has_fctl0, fctl0);
                        /* Give brand a chance to veto check_note's guess */
                        if (ret && bi->header_supported) {
                                ret = bi->header_supported(imgp, osrel,
@@ -787,12 +789,13 @@ __elfN(load_file)(struct thread *td, const char *file, 
u_long *addr,
                struct nameidata nd;
                struct vattr attr;
                struct image_params image_params;
-       } *tempdata;
+       } *tempdata = NULL;
        const Elf_Ehdr *hdr = NULL;
        const Elf_Phdr *phdr = NULL;
        struct nameidata *nd;
        struct vattr *attr;
        struct image_params *imgp;
+       void *m_phdrs = NULL;
        u_long rbase;
        u_long base_addr = 0;
        int error;
@@ -852,16 +855,27 @@ __elfN(load_file)(struct thread *td, const char *file, 
u_long *addr,
                goto fail;
        }
 
-       /* Only support headers that fit within first page for now      */
-       if (!__elfN(phdr_in_zero_page)(hdr)) {
+       if (!aligned(imgp->image_header + hdr->e_phoff, Elf_Addr)) {
                error = ENOEXEC;
                goto fail;
        }
-
-       phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff);
-       if (!aligned(phdr, Elf_Addr)) {
-               error = ENOEXEC;
-               goto fail;
+       if (__elfN(phdr_in_zero_page)(hdr)) {
+               phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff);
+       } else {
+               if (hdr->e_phnum > ELF_OFFPAGE_PHNUM) {
+                       error = ENOEXEC;
+                       goto fail;
+               }
+               VOP_UNLOCK(imgp->vp);
+               phdr = m_phdrs = malloc(hdr->e_phnum * sizeof(Elf_Phdr),
+                   M_TEMP, M_WAITOK | M_ZERO);
+               vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
+               error = vn_rdwr(UIO_READ, imgp->vp, m_phdrs,
+                   hdr->e_phnum * sizeof(Elf_Phdr), hdr->e_phoff,
+                   UIO_SYSSPACE, IO_NODELOCKED, imgp->td->td_ucred,
+                   NOCRED, NULL, imgp->td);
+               if (error != 0)
+                       goto fail;
        }
 
        error = __elfN(load_sections)(imgp, hdr, phdr, rbase, &base_addr);
@@ -883,6 +897,7 @@ fail:
                        VOP_UNSET_TEXT_CHECKED(nd->ni_vp);
                vput(nd->ni_vp);
        }
+       free(m_phdrs, M_TEMP);
        free(tempdata, M_TEMP);
 
        return (error);
@@ -1108,6 +1123,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
        char *interp;
        const Elf_Brandinfo *brand_info;
        struct sysentvec *sv;
+       void *m_phdrs;
        u_long addr, baddr, entry, proghdr;
        u_long maxalign, maxsalign, mapsz, maxv, maxv1, anon_loc;
        uint32_t fctl0;
@@ -1132,16 +1148,6 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params 
*imgp)
         * detected an ELF file.
         */
 
-       if (!__elfN(phdr_in_zero_page)(hdr)) {
-               uprintf("Program headers not in the first page\n");
-               return (ENOEXEC);
-       }
-       phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); 
-       if (!aligned(phdr, Elf_Addr)) {
-               uprintf("Unaligned program headers\n");
-               return (ENOEXEC);
-       }
-
        n = error = 0;
        baddr = 0;
        osrel = 0;
@@ -1149,6 +1155,33 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params 
*imgp)
        entry = proghdr = 0;
        interp = NULL;
        free_interp = false;
+       m_phdrs = NULL;
+
+       if (!aligned(imgp->image_header + hdr->e_phoff, Elf_Addr)) {
+               uprintf("Unaligned program headers\n");
+               return (ENOEXEC);
+       }
+       if (hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize < hdr->e_phoff) {
+               uprintf("PHDRS wrap\n");
+               return (ENOEXEC);
+       }
+       if (__elfN(phdr_in_zero_page)(hdr)) {
+               phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff);
+       } else if (hdr->e_phnum > ELF_OFFPAGE_PHNUM) {
+               uprintf("Too many program headers\n");
+               return (ENOEXEC);
+       } else {
+               VOP_UNLOCK(imgp->vp);
+               phdr = m_phdrs = malloc(hdr->e_phnum * sizeof(Elf_Phdr),
+                   M_TEMP, M_WAITOK | M_ZERO);
+               vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
+               error = vn_rdwr(UIO_READ, imgp->vp, m_phdrs,
+                   hdr->e_phnum * sizeof(Elf_Phdr), hdr->e_phoff,
+                   UIO_SYSSPACE, IO_NODELOCKED, imgp->td->td_ucred,
+                   NOCRED, NULL, imgp->td);
+               if (error != 0)
+                       goto ret;
+       }
 
        /*
         * Somewhat arbitrary, limit accepted max alignment for the
@@ -1230,7 +1263,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
                }
        }
 
-       brand_info = __elfN(get_brandinfo)(imgp, interp, &osrel, &fctl0);
+       brand_info = __elfN(get_brandinfo)(imgp, phdr, interp, &osrel, &fctl0);
        if (brand_info == NULL) {
                uprintf("ELF binary type \"%u\" not known.\n",
                    hdr->e_ident[EI_OSABI]);
@@ -1434,6 +1467,7 @@ ret:
        ASSERT_VOP_LOCKED(imgp->vp, "skipped relock");
        if (free_interp)
                free(interp, M_TEMP);
+       free(m_phdrs, M_TEMP);
        return (error);
 }
 
@@ -2914,17 +2948,16 @@ note_fctl_cb(const Elf_Note *note, void *arg0, bool 
*res)
  * as for headers.
  */
 static bool
-__elfN(check_note)(struct image_params *imgp, const Elf_Brandnote *brandnote,
-    int32_t *osrel, bool *has_fctl0, uint32_t *fctl0)
+__elfN(check_note)(struct image_params *imgp, const Elf_Phdr *phdr,
+    const Elf_Brandnote *brandnote, int32_t *osrel, bool *has_fctl0,
+    uint32_t *fctl0)
 {
-       const Elf_Phdr *phdr;
        const Elf_Ehdr *hdr;
        struct brandnote_cb_arg b_arg;
        struct fctl_cb_arg f_arg;
        int i, j;
 
        hdr = (const Elf_Ehdr *)imgp->image_header;
-       phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff);
        b_arg.brandnote = brandnote;
        b_arg.osrel = osrel;
        f_arg.has_fctl0 = has_fctl0;

Reply via email to