Page MenuHomeFreeBSD

D59710.id188859.diff
No OneTemporary

D59710.id188859.diff

diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -1110,17 +1110,17 @@
error = ENOEXEC;
goto out;
}
-
if (phdr->p_memsz < phdr->p_filesz) {
- link_elf_error(filename,
- "Invalid program header");
+ link_elf_error(filename, "Invalid segment size");
+ error = ENOEXEC;
+ goto out;
+ }
+ if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr) {
+ link_elf_error(filename, "Segment address overflow");
error = ENOEXEC;
goto out;
}
- /*
- * XXX: We just trust they come in right order ??
- */
segs[nsegs] = phdr;
++nsegs;
break;
@@ -1147,6 +1147,17 @@
goto out;
}
+ /*
+ * Segment entries must appear in the proper order.
+ */
+ for (i = 0; i < nsegs - 1; i++) {
+ if (segs[i]->p_vaddr + segs[i]->p_memsz > segs[i+1]->p_vaddr) {
+ link_elf_error(filename, "Segments are unsorted or overlapping");
+ error = ENOEXEC;
+ goto out;
+ }
+ }
+
/*
* Allocate the entire address space of the object, to stake
* out our contiguous region, and to establish the base
@@ -1228,6 +1239,14 @@
segs[i]->p_memsz - segs[i]->p_filesz);
}
+ if ((phdyn->p_vaddr < base_vaddr) ||
+ (phdyn->p_vaddr >= base_vaddr + mapsize) ||
+ (phdyn->p_memsz > mapsize - (phdyn->p_vaddr - base_vaddr))) {
+ link_elf_error(filename, "Dynamic segment out of bounds");
+ error = ENOEXEC;
+ goto out;
+ }
+
ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr);
lf->address = ef->address;
diff --git a/tests/sys/Makefile b/tests/sys/Makefile
--- a/tests/sys/Makefile
+++ b/tests/sys/Makefile
@@ -18,6 +18,7 @@
TESTS_SUBDIRS+= fs
TESTS_SUBDIRS+= geom
TESTS_SUBDIRS+= kern
+TESTS_SUBDIRS+= kld
TESTS_SUBDIRS+= kqueue
TESTS_SUBDIRS+= mac
TESTS_SUBDIRS+= mqueue
diff --git a/tests/sys/kld/Makefile b/tests/sys/kld/Makefile
new file mode 100644
--- /dev/null
+++ b/tests/sys/kld/Makefile
@@ -0,0 +1,9 @@
+.include <src.opts.mk>
+
+TESTSDIR= ${TESTSBASE}/sys/kld
+
+ATF_TESTS_C= link-elf
+
+SRCS.link-elf= link_elf.c
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kld/link_elf.c b/tests/sys/kld/link_elf.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kld/link_elf.c
@@ -0,0 +1,245 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2026 Federico Angelilli
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/param.h>
+#include <sys/elf.h>
+#include <sys/linker.h>
+#include <machine/elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <atf-c.h>
+
+#define EMPTY_FILE 0
+#define INVALID_EHDR 1
+#define PARTIAL_EHDR 2
+#define WRONG_ARCH 3
+#define WRONG_CLASS 4
+#define UNREADABLE_PHDR 5
+#define PARTIAL_PHDR 6
+#define INVALID_PHDR 7
+#define OVERLAPPING_SEGMENTS 8
+#define UNSORTED_SEGMENTS 9
+#define MISSING_DYNAMIC 10
+#define MEMSZ_LESS_FILESZ 11
+#define HUGE_PHNUM 12
+#define VADDR_OVERFLOW 13
+#define OOB_DYNAMIC 14
+#define ZERO_MEMSZ_LOAD 15
+
+#define ELF_TC(name, type) \
+ ATF_TC(name); \
+ ATF_TC_HEAD(name, tc) { \
+ atf_tc_set_md_var(tc, "require.user", "root"); \
+ } \
+ ATF_TC_BODY(name, tc) { \
+ const char *mod = "./" #name ".ko"; \
+ make_elf_file(mod, type); \
+ int res = kldload(mod); \
+ ATF_REQUIRE_EQ_MSG(-1, res, "kldload succeeded unexpectedly"); \
+ ATF_REQUIRE_EQ_MSG(ENOEXEC, errno, "kldload failed with %d instead of ENOEXEC", errno); \
+ }
+
+static void
+make_elf_file(const char *filename, int type)
+{
+ Elf_Ehdr ehdr;
+ Elf_Phdr phdr[3];
+ ssize_t ehdrsz, phdrsz;
+ int fd;
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE_MSG(fd >= 0, "Failed to create %s", filename);
+
+ /*
+ * Create standard ELF header
+ */
+ ehdrsz = sizeof(ehdr);
+ memset(&ehdr, 0, ehdrsz);
+
+ ehdr.e_ident[EI_MAG0] = ELFMAG0;
+ ehdr.e_ident[EI_MAG1] = ELFMAG1;
+ ehdr.e_ident[EI_MAG2] = ELFMAG2;
+ ehdr.e_ident[EI_MAG3] = ELFMAG3;
+ ehdr.e_ident[EI_CLASS] = ELF_TARG_CLASS;
+ ehdr.e_ident[EI_DATA] = ELF_TARG_DATA;
+ ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
+ ehdr.e_ident[EI_ABIVERSION] = 0;
+ ehdr.e_type = ET_DYN;
+ ehdr.e_machine = ELF_TARG_MACH;
+ ehdr.e_version = EV_CURRENT;
+ ehdr.e_ehsize = sizeof(Elf_Ehdr);
+ ehdr.e_phoff = sizeof(Elf_Ehdr);
+ ehdr.e_phnum = sizeof(phdr) / sizeof(Elf_Phdr);
+ ehdr.e_phentsize = sizeof(Elf_Phdr);
+
+ /*
+ * Create program headers
+ */
+ phdrsz = sizeof(phdr);
+ memset(phdr, 0, phdrsz);
+
+ phdr[0].p_type = PT_LOAD;
+ phdr[0].p_vaddr = 0x1000;
+ phdr[0].p_memsz = 0x1000;
+ phdr[0].p_filesz = 0x1000;
+
+ phdr[1].p_type = PT_LOAD;
+ phdr[1].p_vaddr = 0x2000;
+ phdr[1].p_memsz = 0x1000;
+ phdr[1].p_filesz = 0x1000;
+
+ phdr[2].p_type = PT_DYNAMIC;
+ phdr[2].p_vaddr = 0x2000;
+ phdr[2].p_memsz = 0x1000;
+ phdr[2].p_filesz = 0x1000;
+
+ /*
+ * Inject malformations
+ */
+ switch (type) {
+ case EMPTY_FILE:
+ close(fd);
+ return;
+
+ case INVALID_EHDR:
+ ehdr.e_ident[EI_MAG0] = 0x42;
+ break;
+
+ case PARTIAL_EHDR:
+ ehdrsz /= 2;
+ break;
+
+ case WRONG_ARCH:
+ ehdr.e_machine = (ELF_TARG_MACH == EM_X86_64) ? EM_ARM : EM_X86_64;
+ break;
+
+ case WRONG_CLASS:
+ ehdr.e_ident[EI_CLASS] = (ELF_TARG_CLASS == ELFCLASS64) ? ELFCLASS32 : ELFCLASS64;
+ break;
+
+ case UNREADABLE_PHDR:
+ ehdr.e_phoff = PAGE_SIZE * 2;
+ break;
+
+ case PARTIAL_PHDR:
+ phdrsz -= sizeof(Elf_Phdr) / 2;
+ break;
+
+ case INVALID_PHDR:
+ ehdr.e_phentsize = sizeof(Elf_Phdr) - 1;
+ break;
+
+ case OVERLAPPING_SEGMENTS:
+ phdr[0].p_memsz = 0x2000;
+ break;
+
+ case UNSORTED_SEGMENTS:
+ phdr[0].p_vaddr = 0x3000;
+ break;
+
+ case MISSING_DYNAMIC:
+ phdr[2].p_type = PT_NULL;
+ break;
+
+ case MEMSZ_LESS_FILESZ:
+ phdr[0].p_memsz = phdr[0].p_filesz / 2;
+ break;
+
+ case HUGE_PHNUM:
+ ehdr.e_phnum = 0xffff;
+ break;
+
+ case VADDR_OVERFLOW:
+ phdr[0].p_vaddr = ~(Elf_Addr)0 - 0x1000;
+ phdr[0].p_memsz = 0x2000;
+ break;
+
+ case OOB_DYNAMIC:
+ phdr[2].p_vaddr = 0x9000;
+ break;
+
+ case ZERO_MEMSZ_LOAD:
+ phdr[0].p_memsz = 0;
+ break;
+ }
+
+ ATF_REQUIRE(write(fd, &ehdr, ehdrsz) == ehdrsz);
+
+ if (type != PARTIAL_EHDR) {
+ ATF_REQUIRE(write(fd, phdr, phdrsz) == phdrsz);
+ }
+
+ close(fd);
+}
+
+ELF_TC(elf_empty_file, EMPTY_FILE)
+ELF_TC(elf_invalid_ehdr, INVALID_EHDR)
+ELF_TC(elf_partial_ehdr, PARTIAL_EHDR)
+ELF_TC(elf_wrong_arch, WRONG_ARCH)
+ELF_TC(elf_wrong_class, WRONG_CLASS)
+ELF_TC(elf_unreadable_phdr, UNREADABLE_PHDR)
+ELF_TC(elf_partial_phdr, PARTIAL_PHDR)
+ELF_TC(elf_invalid_phdr, INVALID_PHDR)
+ELF_TC(elf_overlapping_segments, OVERLAPPING_SEGMENTS)
+ELF_TC(elf_unsorted_segments, UNSORTED_SEGMENTS)
+ELF_TC(elf_missing_dynamic, MISSING_DYNAMIC)
+ELF_TC(elf_memsz_less_filesz, MEMSZ_LESS_FILESZ)
+ELF_TC(elf_huge_phnum, HUGE_PHNUM)
+ELF_TC(elf_vaddr_overflow, VADDR_OVERFLOW)
+ELF_TC(elf_oob_dynamic, OOB_DYNAMIC)
+ELF_TC(elf_zero_memsz_load, ZERO_MEMSZ_LOAD)
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, elf_empty_file);
+ ATF_TP_ADD_TC(tp, elf_invalid_ehdr);
+ ATF_TP_ADD_TC(tp, elf_partial_ehdr);
+ ATF_TP_ADD_TC(tp, elf_wrong_arch);
+ ATF_TP_ADD_TC(tp, elf_wrong_class);
+ ATF_TP_ADD_TC(tp, elf_unreadable_phdr);
+ ATF_TP_ADD_TC(tp, elf_partial_phdr);
+ ATF_TP_ADD_TC(tp, elf_invalid_phdr);
+ ATF_TP_ADD_TC(tp, elf_overlapping_segments);
+ ATF_TP_ADD_TC(tp, elf_unsorted_segments);
+ ATF_TP_ADD_TC(tp, elf_missing_dynamic);
+ ATF_TP_ADD_TC(tp, elf_memsz_less_filesz);
+ ATF_TP_ADD_TC(tp, elf_huge_phnum);
+ ATF_TP_ADD_TC(tp, elf_vaddr_overflow);
+ ATF_TP_ADD_TC(tp, elf_oob_dynamic);
+ ATF_TP_ADD_TC(tp, elf_zero_memsz_load);
+
+ return (atf_no_error());
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Oct 10, 1:17 AM (7 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40516351
Default Alt Text
D59710.id188859.diff (8 KB)

Event Timeline