#define VAS_EBOOT_MODULE_VERIFIERXX #if !defined(MODULEVERIFIER_ELF32) && !defined(MODULEVERIFIER_ELF64) #if __SIZEOF_POINTER__ == 8 #include "VasEBoot-module-verifier64.c" #else #include "VasEBoot-module-verifier32.c" #endif #endif #include #include #include #include #if defined(MODULEVERIFIER_ELF32) # define SUFFIX(x) x ## 32 # define ELFCLASSXX ELFCLASS32 # define Elf_Ehdr Elf32_Ehdr # define Elf_Phdr Elf32_Phdr # define Elf_Nhdr Elf32_Nhdr # define Elf_Addr Elf32_Addr # define Elf_Sym Elf32_Sym # define Elf_Off Elf32_Off # define Elf_Shdr Elf32_Shdr # define Elf_Rela Elf32_Rela # define Elf_Rel Elf32_Rel # define Elf_Word Elf32_Word # define Elf_Half Elf32_Half # define Elf_Shnum Elf32_Shnum # define Elf_Section Elf32_Section # define ELF_R_SYM(val) ELF32_R_SYM(val) # define ELF_R_TYPE(val) ELF32_R_TYPE(val) # define ELF_ST_TYPE(val) ELF32_ST_TYPE(val) #elif defined(MODULEVERIFIER_ELF64) # define SUFFIX(x) x ## 64 # define ELFCLASSXX ELFCLASS64 # define Elf_Ehdr Elf64_Ehdr # define Elf_Phdr Elf64_Phdr # define Elf_Nhdr Elf64_Nhdr # define Elf_Addr Elf64_Addr # define Elf_Sym Elf64_Sym # define Elf_Off Elf64_Off # define Elf_Shdr Elf64_Shdr # define Elf_Rela Elf64_Rela # define Elf_Rel Elf64_Rel # define Elf_Word Elf64_Word # define Elf_Half Elf64_Half # define Elf_Shnum Elf64_Shnum # define Elf_Section Elf64_Section # define ELF_R_SYM(val) ELF64_R_SYM(val) # define ELF_R_TYPE(val) ELF64_R_TYPE(val) # define ELF_ST_TYPE(val) ELF64_ST_TYPE(val) #else #error "I'm confused" #endif #define VasEBoot_target_to_host32(x) (VasEBoot_target_to_host32_real (arch, (x))) #define VasEBoot_host_to_target32(x) (VasEBoot_host_to_target32_real (arch, (x))) #define VasEBoot_target_to_host64(x) (VasEBoot_target_to_host64_real (arch, (x))) #define VasEBoot_host_to_target64(x) (VasEBoot_host_to_target64_real (arch, (x))) #define VasEBoot_host_to_target_addr(x) (VasEBoot_host_to_target_addr_real (arch, (x))) #define VasEBoot_target_to_host16(x) (VasEBoot_target_to_host16_real (arch, (x))) #define VasEBoot_host_to_target16(x) (VasEBoot_host_to_target16_real (arch, (x))) #define VasEBoot_target_to_host(val) VasEBoot_target_to_host_real(arch, (val)) static inline VasEBoot_uint32_t VasEBoot_target_to_host32_real (const struct VasEBoot_module_verifier_arch *arch, VasEBoot_uint32_t in) { if (arch->bigendian) return VasEBoot_be_to_cpu32 (in); else return VasEBoot_le_to_cpu32 (in); } static inline VasEBoot_uint64_t VasEBoot_target_to_host64_real (const struct VasEBoot_module_verifier_arch *arch, VasEBoot_uint64_t in) { if (arch->bigendian) return VasEBoot_be_to_cpu64 (in); else return VasEBoot_le_to_cpu64 (in); } static inline VasEBoot_uint64_t VasEBoot_host_to_target64_real (const struct VasEBoot_module_verifier_arch *arch, VasEBoot_uint64_t in) { if (arch->bigendian) return VasEBoot_cpu_to_be64 (in); else return VasEBoot_cpu_to_le64 (in); } static inline VasEBoot_uint32_t VasEBoot_host_to_target32_real (const struct VasEBoot_module_verifier_arch *arch, VasEBoot_uint32_t in) { if (arch->bigendian) return VasEBoot_cpu_to_be32 (in); else return VasEBoot_cpu_to_le32 (in); } static inline VasEBoot_uint16_t VasEBoot_target_to_host16_real (const struct VasEBoot_module_verifier_arch *arch, VasEBoot_uint16_t in) { if (arch->bigendian) return VasEBoot_be_to_cpu16 (in); else return VasEBoot_le_to_cpu16 (in); } static inline VasEBoot_uint16_t VasEBoot_host_to_target16_real (const struct VasEBoot_module_verifier_arch *arch, VasEBoot_uint16_t in) { if (arch->bigendian) return VasEBoot_cpu_to_be16 (in); else return VasEBoot_cpu_to_le16 (in); } static inline VasEBoot_uint64_t VasEBoot_host_to_target_addr_real (const struct VasEBoot_module_verifier_arch *arch, VasEBoot_uint64_t in) { if (arch->voidp_sizeof == 8) return VasEBoot_host_to_target64_real (arch, in); else return VasEBoot_host_to_target32_real (arch, in); } static inline VasEBoot_uint64_t VasEBoot_target_to_host_real (const struct VasEBoot_module_verifier_arch *arch, VasEBoot_uint64_t in) { if (arch->voidp_sizeof == 8) return VasEBoot_target_to_host64_real (arch, in); else return VasEBoot_target_to_host32_real (arch, in); } static Elf_Shdr * get_shdr (const struct VasEBoot_module_verifier_arch *arch, Elf_Ehdr *e, Elf_Word index, size_t module_size) { Elf_Shdr *s; if (VasEBoot_target_to_host (e->e_shoff) == 0) VasEBoot_util_error ("Invalid section header offset"); s = (Elf_Shdr *) ((char *) e + VasEBoot_target_to_host (e->e_shoff) + index * VasEBoot_target_to_host16 (e->e_shentsize)); /* Check that the header being accessed isn't outside the module size. */ if (VasEBoot_target_to_host32 (s->sh_type) != SHT_NULL && VasEBoot_target_to_host32 (s->sh_type) != SHT_NOBITS) { if (VasEBoot_target_to_host (s->sh_offset) > module_size) VasEBoot_util_error ("Section %d starts after the end of the module", index); if (VasEBoot_target_to_host (s->sh_offset) + VasEBoot_target_to_host (s->sh_size) > module_size) VasEBoot_util_error ("Section %d ends after the end of the module", index); } return s; } static Elf_Shnum get_shnum (const struct VasEBoot_module_verifier_arch *arch, Elf_Ehdr *e) { Elf_Shdr *s; Elf_Shnum shnum; shnum = VasEBoot_target_to_host16 (e->e_shnum); if (shnum == SHN_UNDEF) { s = get_shdr (arch, e, 0, 0); shnum = VasEBoot_target_to_host (s->sh_size); if (shnum < SHN_LORESERVE) VasEBoot_util_error ("Invalid number of section header table entries in sh_size: %" PRIuVAS_EBOOT_UINT64_T, (VasEBoot_uint64_t) shnum); } else { if (shnum >= SHN_LORESERVE) VasEBoot_util_error ("Invalid number of section header table entries in e_shnum: %" PRIuVAS_EBOOT_UINT64_T, (VasEBoot_uint64_t) shnum); } return shnum; } static Elf_Word get_shstrndx (const struct VasEBoot_module_verifier_arch *arch, Elf_Ehdr *e, size_t module_size) { Elf_Shdr *s; Elf_Word shstrndx; shstrndx = VasEBoot_target_to_host16 (e->e_shstrndx); if (shstrndx == SHN_XINDEX) { s = get_shdr (arch, e, 0, 0); shstrndx = VasEBoot_target_to_host (s->sh_link); if (shstrndx < SHN_LORESERVE) VasEBoot_util_error ("Invalid section header table index in sh_link: %d", shstrndx); } else { if (shstrndx >= SHN_LORESERVE) VasEBoot_util_error ("Invalid section header table index in e_shstrndx: %d", shstrndx); } return shstrndx; } static Elf_Shdr * find_section (const struct VasEBoot_module_verifier_arch *arch, Elf_Ehdr *e, const char *name, size_t module_size) { Elf_Shdr *s; const char *str; unsigned i; s = get_shdr (arch, e, get_shstrndx (arch, e, module_size), module_size); str = (char *) e + VasEBoot_target_to_host (s->sh_offset); for (i = 0; i < get_shnum (arch, e); i++) { s = get_shdr (arch, e, i, module_size); if (strcmp (str + VasEBoot_target_to_host32 (s->sh_name), name) == 0) return s; } return NULL; } static void check_license (const char * const filename, const struct VasEBoot_module_verifier_arch *arch, Elf_Ehdr *e, size_t module_size) { Elf_Shdr *s = find_section (arch, e, ".module_license", module_size); if (s && (strcmp ((char *) e + VasEBoot_target_to_host(s->sh_offset), "LICENSE=GPLv3") == 0 || strcmp ((char *) e + VasEBoot_target_to_host(s->sh_offset), "LICENSE=GPLv3+") == 0 || strcmp ((char *) e + VasEBoot_target_to_host(s->sh_offset), "LICENSE=GPLv2+") == 0)) return; VasEBoot_util_error ("%s: incompatible license", filename); } static Elf_Sym * get_symtab (const struct VasEBoot_module_verifier_arch *arch, Elf_Ehdr *e, Elf_Word *size, Elf_Word *entsize, size_t module_size) { unsigned i; Elf_Shdr *s; Elf_Sym *sym; for (i = 0; i < get_shnum (arch, e); i++) { s = get_shdr (arch, e, i, module_size); if (VasEBoot_target_to_host32 (s->sh_type) == SHT_SYMTAB) break; } if (i == get_shnum (arch, e)) return NULL; sym = (Elf_Sym *) ((char *) e + VasEBoot_target_to_host (s->sh_offset)); *size = VasEBoot_target_to_host (s->sh_size); *entsize = VasEBoot_target_to_host (s->sh_entsize); return sym; } static int is_whitelisted (const char *modname, const char **whitelist) { const char **ptr; if (!whitelist) return 0; if (!modname) return 0; for (ptr = whitelist; *ptr; ptr++) if (strcmp (modname, *ptr) == 0) return 1; return 0; } static void check_symbols (const struct VasEBoot_module_verifier_arch *arch, Elf_Ehdr *e, const char *modname, const char **whitelist_empty, size_t module_size) { Elf_Sym *sym; Elf_Word size, entsize; unsigned i; /* Module without symbol table and without .moddeps section is useless at boot time, so catch it early to prevent build errors */ sym = get_symtab (arch, e, &size, &entsize, module_size); if (!sym) { Elf_Shdr *s; /* However some modules are dependencies-only, e.g. insmod all_video pulls in all video drivers. Some platforms e.g. xen have no video drivers, so the module does nothing. */ if (is_whitelisted (modname, whitelist_empty)) return; s = find_section (arch, e, ".moddeps", module_size); if (!s) VasEBoot_util_error ("%s: no symbol table and no .moddeps section", modname); if (!s->sh_size) VasEBoot_util_error ("%s: no symbol table and empty .moddeps section", modname); return; } for (i = 0; i < size / entsize; i++, sym = (Elf_Sym *) ((char *) sym + entsize)) { unsigned char type = ELF_ST_TYPE (sym->st_info); switch (type) { case STT_NOTYPE: case STT_OBJECT: case STT_FUNC: case STT_SECTION: case STT_FILE: break; default: return VasEBoot_util_error ("%s: unknown symbol type `%d'", modname, (int) type); } } } static int is_symbol_local(Elf_Sym *sym) { switch (ELF_ST_TYPE (sym->st_info)) { case STT_NOTYPE: case STT_OBJECT: if (sym->st_name != 0 && sym->st_shndx == 0) return 0; return 1; case STT_FUNC: case STT_SECTION: return 1; default: return 0; } } static void section_check_relocations (const char * const modname, const struct VasEBoot_module_verifier_arch *arch, void *ehdr, Elf_Shdr *s, size_t target_seg_size, size_t module_size) { Elf_Rel *rel, *max; Elf_Sym *symtab; Elf_Word symtabsize, symtabentsize; symtab = get_symtab (arch, ehdr, &symtabsize, &symtabentsize, module_size); if (!symtab) VasEBoot_util_error ("%s: relocation without symbol table", modname); for (rel = (Elf_Rel *) ((char *) ehdr + VasEBoot_target_to_host (s->sh_offset)), max = (Elf_Rel *) ((char *) rel + VasEBoot_target_to_host (s->sh_size)); rel < max; rel = (Elf_Rel *) ((char *) rel + VasEBoot_target_to_host (s->sh_entsize))) { Elf_Sym *sym; unsigned i; if (target_seg_size < VasEBoot_target_to_host (rel->r_offset)) VasEBoot_util_error ("%s: reloc offset is out of the segment", modname); VasEBoot_uint32_t type = ELF_R_TYPE (VasEBoot_target_to_host (rel->r_info)); if (arch->machine == EM_SPARCV9) type &= 0xff; for (i = 0; arch->supported_relocations[i] != -1; i++) if (type == arch->supported_relocations[i]) break; if (arch->supported_relocations[i] != -1) continue; if (!arch->short_relocations) VasEBoot_util_error ("%s: unsupported relocation 0x%x", modname, type); for (i = 0; arch->short_relocations[i] != -1; i++) if (type == arch->short_relocations[i]) break; if (arch->short_relocations[i] == -1) VasEBoot_util_error ("%s: unsupported relocation 0x%x", modname, type); sym = (Elf_Sym *) ((char *) symtab + symtabentsize * ELF_R_SYM (VasEBoot_target_to_host (rel->r_info))); if (is_symbol_local (sym)) continue; VasEBoot_util_error ("%s: relocation 0x%x is not module-local", modname, type); } #if defined(MODULEVERIFIER_ELF64) if (arch->machine == EM_AARCH64) { unsigned unmatched_adr_got_page = 0; Elf_Rela *rel2; for (rel = (Elf_Rel *) ((char *) ehdr + VasEBoot_target_to_host (s->sh_offset)), max = (Elf_Rel *) ((char *) rel + VasEBoot_target_to_host (s->sh_size)); rel < max; rel = (Elf_Rel *) ((char *) rel + VasEBoot_target_to_host (s->sh_entsize))) { switch (ELF_R_TYPE (VasEBoot_target_to_host (rel->r_info))) { case R_AARCH64_ADR_GOT_PAGE: unmatched_adr_got_page++; for (rel2 = (Elf_Rela *) ((char *) rel + VasEBoot_target_to_host (s->sh_entsize)); rel2 < (Elf_Rela *) max; rel2 = (Elf_Rela *) ((char *) rel2 + VasEBoot_target_to_host (s->sh_entsize))) if (ELF_R_SYM (rel2->r_info) == ELF_R_SYM (rel->r_info) && ((Elf_Rela *) rel)->r_addend == rel2->r_addend && ELF_R_TYPE (rel2->r_info) == R_AARCH64_LD64_GOT_LO12_NC) break; if (rel2 >= (Elf_Rela *) max) VasEBoot_util_error ("%s: ADR_GOT_PAGE without matching LD64_GOT_LO12_NC", modname); break; case R_AARCH64_LD64_GOT_LO12_NC: if (unmatched_adr_got_page == 0) VasEBoot_util_error ("%s: LD64_GOT_LO12_NC without matching ADR_GOT_PAGE", modname); unmatched_adr_got_page--; break; } } } #endif } static void check_relocations (const char * const modname, const struct VasEBoot_module_verifier_arch *arch, Elf_Ehdr *e, size_t module_size) { Elf_Shdr *s; unsigned i; for (i = 0; i < get_shnum (arch, e); i++) { s = get_shdr (arch, e, i, module_size); if (VasEBoot_target_to_host32 (s->sh_type) == SHT_REL || VasEBoot_target_to_host32 (s->sh_type) == SHT_RELA) { Elf_Shdr *ts; if (VasEBoot_target_to_host32 (s->sh_type) == SHT_REL && !(arch->flags & VAS_EBOOT_MODULE_VERIFY_SUPPORTS_REL)) VasEBoot_util_error ("%s: unsupported SHT_REL", modname); if (VasEBoot_target_to_host32 (s->sh_type) == SHT_RELA && !(arch->flags & VAS_EBOOT_MODULE_VERIFY_SUPPORTS_RELA)) VasEBoot_util_error ("%s: unsupported SHT_RELA", modname); /* Find the target segment. */ if (VasEBoot_target_to_host32 (s->sh_info) >= get_shnum (arch, e)) VasEBoot_util_error ("%s: orphaned reloc section", modname); ts = get_shdr (arch, e, VasEBoot_target_to_host32 (s->sh_info), module_size); section_check_relocations (modname, arch, e, s, VasEBoot_target_to_host (ts->sh_size), module_size); } } } void SUFFIX(VasEBoot_module_verify) (const char * const filename, void *module_img, size_t size, const struct VasEBoot_module_verifier_arch *arch, const char **whitelist_empty) { Elf_Ehdr *e = module_img; /* Check the header size. */ if (size < sizeof (Elf_Ehdr)) VasEBoot_util_error ("%s: ELF header smaller than expected", filename); /* Check the magic numbers. */ if (e->e_ident[EI_MAG0] != ELFMAG0 || e->e_ident[EI_MAG1] != ELFMAG1 || e->e_ident[EI_MAG2] != ELFMAG2 || e->e_ident[EI_MAG3] != ELFMAG3 || e->e_ident[EI_VERSION] != EV_CURRENT || VasEBoot_target_to_host32 (e->e_version) != EV_CURRENT) VasEBoot_util_error ("%s: invalid arch-independent ELF magic", filename); if (e->e_ident[EI_CLASS] != ELFCLASSXX || e->e_ident[EI_DATA] != (arch->bigendian ? ELFDATA2MSB : ELFDATA2LSB) || VasEBoot_target_to_host16 (e->e_machine) != arch->machine) VasEBoot_util_error ("%s: invalid arch-dependent ELF magic", filename); if (VasEBoot_target_to_host16 (e->e_type) != ET_REL) { VasEBoot_util_error ("%s: this ELF file is not of the right type", filename); } /* Make sure that every section is within the core. */ if (size < VasEBoot_target_to_host (e->e_shoff) + (VasEBoot_uint32_t) VasEBoot_target_to_host16 (e->e_shentsize) * get_shnum (arch, e)) { VasEBoot_util_error ("%s: ELF sections outside core", filename); } check_license (filename, arch, e, size); Elf_Shdr *s; const char *modname; s = find_section (arch, e, ".modname", size); if (!s) VasEBoot_util_error ("%s: no module name found", filename); modname = (const char *) e + VasEBoot_target_to_host (s->sh_offset); check_symbols(arch, e, modname, whitelist_empty, size); check_relocations(modname, arch, e, size); }