git: 804daf166406 - main - imgact_elf: read program headers if not contained in the first page
Date: Thu, 28 May 2026 20:59:51 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=804daf166406a1b0e22f93b9417b90ed7d3aa1ae
commit 804daf166406a1b0e22f93b9417b90ed7d3aa1ae
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-05-28 09:06:33 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
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 <iwtcex@gmail.com>
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;