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
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
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