|
@@ -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);
|
|
|
}
|
|
|
}
|