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
This commit is contained in:
Konstantin Belousov
2026-05-28 12:06:33 +03:00
parent 9b729333bb
commit 804daf1664
+62 -29
View File
@@ -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 @@ __elfN(load_file)(struct thread *td, const char *file, u_long *addr,
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 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
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;