lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Wed, 18 Mar 2015 10:09:48 +0100 From: Quentin Casasnovas <quentin.casasnovas@...cle.com> To: Quentin Casasnovas <quentin.casasnovas@...cle.com>, Rusty Russell <rusty@...tcorp.com.au>, Michal Marek <mmarek@...e.cz> Cc: lkml <linux-kernel@...r.kernel.org>, Oleg Nesterov <oleg@...hat.com>, Borislav Petkov <bp@...en8.de>, Linus Torvalds <torvalds@...ux-foundation.org> Subject: Re: [PATCH 7/7] modpost: handle relocations mismatch in __ex_table. Adding Rusty and Michal to CC. On Tue, Mar 17, 2015 at 01:40:02PM +0100, Quentin Casasnovas wrote: > __ex_table is a simple table section where each entry is a pair of > addresses - the first address is an address which can fault in kernel > space, and the second address points to where the kernel should jump to > when handling that fault. This is how copy_from_user() does not crash the > kernel if userspace gives a borked pointer for example. > > If one of these addresses point to a non-executable section, something is > seriously wrong since it either means the kernel will never fault from > there or it will not be able to jump to there. As both cases are serious > enough, we simply error out in these cases so the build fails and the > developper has to fix the issue. > > In case the section is executable, but it isn't referenced in our list of > authorized sections to point to from __ex_table, we just dump a warning > giving more information about it. We do this in case the new section is > executable but isn't supposed to be executed by the kernel. This happened > with .altinstr_replacement, which is executable but is only used to copy > instructions from - we should never have our instruction pointer pointing > in .altinstr_replacement. Admitedly, a proper fix in that case would be to > just set .altinstr_replacement NX, but we need to warn about future cases > like this. > > Signed-off-by: Quentin Casasnovas <quentin.casasnovas@...cle.com> > --- > scripts/mod/modpost.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 141 insertions(+) > > diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c > index bf0cf81..dfe9c3c 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, 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, 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, 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, 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, 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); > } > } > -- > 2.0.5 > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists