diff options
-rw-r--r-- | scripts/mod/modpost.c | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index bf0cf8173beb..e95aa28ce0f7 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -875,6 +875,8 @@ static void check_section(const char *modname, struct elf_info *elf, #define DATA_SECTIONS ".data", ".data.rel" #define TEXT_SECTIONS ".text", ".text.unlikely", ".sched.text", \ ".kprobes.text" +#define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \ + ".fixup", ".entry.text" #define INIT_SECTIONS ".init.*" #define MEM_INIT_SECTIONS ".meminit.*" @@ -882,6 +884,9 @@ static void check_section(const char *modname, struct elf_info *elf, #define EXIT_SECTIONS ".exit.*" #define MEM_EXIT_SECTIONS ".memexit.*" +#define ALL_TEXT_SECTIONS ALL_INIT_TEXT_SECTIONS, ALL_EXIT_TEXT_SECTIONS, \ + TEXT_SECTIONS, OTHER_TEXT_SECTIONS + /* init data sections */ static const char *const init_data_sections[] = { ALL_INIT_DATA_SECTIONS, NULL }; @@ -922,6 +927,7 @@ enum mismatch { ANY_INIT_TO_ANY_EXIT, ANY_EXIT_TO_ANY_INIT, EXPORT_TO_INIT_EXIT, + EXTABLE_TO_NON_TEXT, }; struct sectioncheck { @@ -936,6 +942,11 @@ struct sectioncheck { }; +static void extable_mismatch_handler(const char *modname, struct elf_info *elf, + const struct sectioncheck* const mismatch, + Elf_Rela *r, Elf_Sym *sym, + const char *fromsec); + static const struct sectioncheck sectioncheck[] = { /* Do not reference init/exit code/data from * normal code and data @@ -1013,6 +1024,16 @@ static const struct sectioncheck sectioncheck[] = { .bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL }, .mismatch = EXPORT_TO_INIT_EXIT, .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, +}, +{ + .fromsec = { "__ex_table", NULL }, + /* If you're adding any new black-listed sections in here, consider + * adding a special 'printer' for them in scripts/check_extable. + */ + .bad_tosec = { ".altinstr_replacement", NULL }, + .good_tosec = {ALL_TEXT_SECTIONS , NULL}, + .mismatch = EXTABLE_TO_NON_TEXT, + .handler = extable_mismatch_handler, } }; @@ -1418,6 +1439,10 @@ static void report_sec_mismatch(const char *modname, tosym, prl_to, prl_to, tosym); free(prl_to); break; + case EXTABLE_TO_NON_TEXT: + fatal("There's a special handler for this mismatch type, " + "we should never get here."); + break; } fprintf(stderr, "\n"); } @@ -1453,6 +1478,120 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf, } } +static int is_executable_section(struct elf_info* elf, unsigned int section_index) +{ + if (section_index > elf->num_sections) + fatal("section_index is outside elf->num_sections!\n"); + + return ((elf->sechdrs[section_index].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR); +} + +/* + * We rely on a gross hack in section_rel[a]() calling find_extable_entry_size() + * to know the sizeof(struct exception_table_entry) for the target architecture. + */ +static unsigned int extable_entry_size = 0; +static void find_extable_entry_size(const char* const sec, const Elf_Rela* r, + const void* start, const void* cur) +{ + /* + * If we're currently checking the second relocation within __ex_table, + * that relocation offset tells us the offsetof(struct + * exception_table_entry, fixup) which is equal to sizeof(struct + * exception_table_entry) divided by two. We use that to our advantage + * since there's no portable way to get that size as every architecture + * seems to go with different sized types. Not pretty but better than + * hard-coding the size for every architecture.. + */ + if (!extable_entry_size && cur == start + 1 && + strcmp("__ex_table", sec) == 0) + extable_entry_size = r->r_offset * 2; +} +static inline bool is_extable_fault_address(Elf_Rela *r) +{ + if (!extable_entry_size == 0) + fatal("extable_entry size hasn't been discovered!\n"); + + return ((r->r_offset == 0) || + (r->r_offset % extable_entry_size == 0)); +} + +static void report_extable_warnings(const char* modname, struct elf_info* elf, + const struct sectioncheck* const mismatch, + Elf_Rela* r, Elf_Sym* sym, + const char* fromsec, const char* tosec) +{ + Elf_Sym* fromsym = find_elf_symbol2(elf, r->r_offset, fromsec); + const char* fromsym_name = sym_name(elf, fromsym); + Elf_Sym* tosym = find_elf_symbol(elf, r->r_addend, sym); + const char* tosym_name = sym_name(elf, tosym); + const char* from_pretty_name; + const char* from_pretty_name_p; + const char* to_pretty_name; + const char* to_pretty_name_p; + + get_pretty_name(is_function(fromsym), + &from_pretty_name, &from_pretty_name_p); + get_pretty_name(is_function(tosym), + &to_pretty_name, &to_pretty_name_p); + + warn("%s(%s+0x%lx): Section mismatch in reference" + " from the %s %s%s to the %s %s:%s%s\n", + modname, fromsec, (long)r->r_offset, from_pretty_name, + fromsym_name, from_pretty_name_p, + to_pretty_name, tosec, tosym_name, to_pretty_name_p); + + if (!match(tosec, mismatch->bad_tosec) && + is_executable_section(elf, get_secindex(elf, sym))) + fprintf(stderr, + "The relocation at %s+0x%lx references\n" + "section \"%s\" which is not in the list of\n" + "authorized sections. If you're adding a new section\n" + "and/or if this reference is valid, add \"%s\" to the\n" + "list of authorized sections to jump to on fault.\n" + "This can be achieved by adding \"%s\" to \n" + "OTHER_TEXT_SECTIONS in scripts/mod/modpost.c.\n", + fromsec, (long)r->r_offset, tosec, tosec, tosec); +} + +static void extable_mismatch_handler(const char* modname, struct elf_info *elf, + const struct sectioncheck* const mismatch, + Elf_Rela* r, Elf_Sym* sym, + const char *fromsec) +{ + const char* tosec = sec_name(elf, get_secindex(elf, sym)); + + sec_mismatch_count++; + + if (sec_mismatch_verbose) + report_extable_warnings(modname, elf, mismatch, r, sym, + fromsec, tosec); + + if (match(tosec, mismatch->bad_tosec)) + fatal("The relocation at %s+0x%lx references\n" + "section \"%s\" which is black-listed.\n" + "Something is seriously wrong and should be fixed.\n" + "You might get more information about where this is\n" + "coming from by using scripts/check_extable.sh %s\n", + fromsec, (long)r->r_offset, tosec, modname); + else if (!is_executable_section(elf, get_secindex(elf, sym))) { + if (is_extable_fault_address(r)) + fatal("The relocation at %s+0x%lx references\n" + "section \"%s\" which is not executable, IOW\n" + "it is not possible for the kernel to fault\n" + "at that address. Something is seriously wrong\n" + "and should be fixed.\n", + fromsec, (long)r->r_offset, tosec); + else + fatal("The relocation at %s+0x%lx references\n" + "section \"%s\" which is not executable, IOW\n" + "the kernel will fault if it ever tries to\n" + "jump to it. Something is seriously wrong\n" + "and should be fixed.\n", + fromsec, (long)r->r_offset, tosec); + } +} + static void check_section_mismatch(const char *modname, struct elf_info *elf, Elf_Rela *r, Elf_Sym *sym, const char *fromsec) { @@ -1605,6 +1744,7 @@ static void section_rela(const char *modname, struct elf_info *elf, /* Skip special sections */ if (is_shndx_special(sym->st_shndx)) continue; + find_extable_entry_size(fromsec, &r, start, rela); check_section_mismatch(modname, elf, &r, sym, fromsec); } } @@ -1663,6 +1803,7 @@ static void section_rel(const char *modname, struct elf_info *elf, /* Skip special sections */ if (is_shndx_special(sym->st_shndx)) continue; + find_extable_entry_size(fromsec, &r, start, rel); check_section_mismatch(modname, elf, &r, sym, fromsec); } } |