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 <[email protected]>
> ---
>  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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to