[RFC,10/11] module: Reorder functions
diff mbox series

Message ID 20200205223950.1212394-11-kristen@linux.intel.com
State New
Headers show
Series
  • Finer grained kernel address space randomization
Related show

Commit Message

Kristen Carlson Accardi Feb. 5, 2020, 10:39 p.m. UTC
If a module has functions split out into separate text sections
(i.e. compiled with the -ffunction-sections flag), reorder the
functions to provide some code diversification to modules.

Signed-off-by: Kristen Carlson Accardi <kristen@linux.intel.com>
---
 kernel/module.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

Comments

Kees Cook Feb. 6, 2020, 12:41 p.m. UTC | #1
On Wed, Feb 05, 2020 at 02:39:49PM -0800, Kristen Carlson Accardi wrote:
> If a module has functions split out into separate text sections
> (i.e. compiled with the -ffunction-sections flag), reorder the
> functions to provide some code diversification to modules.
> 
> Signed-off-by: Kristen Carlson Accardi <kristen@linux.intel.com>

Probably a good idea to add Jessica to CC in next version:
	Jessica Yu <jeyu@kernel.org>

Reviewed-by: Kees Cook <keescook@chromium.org>

-Kees

> ---
>  kernel/module.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 85 insertions(+)
> 
> diff --git a/kernel/module.c b/kernel/module.c
> index b56f3224b161..231563e95e61 100644
> --- a/kernel/module.c
> +++ b/kernel/module.c
> @@ -53,6 +53,8 @@
>  #include <linux/bsearch.h>
>  #include <linux/dynamic_debug.h>
>  #include <linux/audit.h>
> +#include <linux/random.h>
> +#include <asm/setup.h>
>  #include <uapi/linux/module.h>
>  #include "module-internal.h"
>  
> @@ -3245,6 +3247,87 @@ static int find_module_sections(struct module *mod, struct load_info *info)
>  	return 0;
>  }
>  
> +/*
> + * shuffle_text_list()
> + * Use a Fisher Yates algorithm to shuffle a list of text sections.
> + */
> +static void shuffle_text_list(Elf_Shdr **list, int size)
> +{
> +	int i;
> +	unsigned int j;
> +	Elf_Shdr *temp;
> +
> +	for (i = size - 1; i > 0; i--) {
> +		/*
> +		 * TBD - seed. We need to be able to use a known
> +		 * seed so that we can non-randomly randomize for
> +		 * debugging.
> +		 */
> +
> +		// pick a random index from 0 to i
> +		get_random_bytes(&j, sizeof(j));
> +		j = j % (i + 1);
> +
> +		temp = list[i];
> +		list[i] = list[j];
> +		list[j] = temp;
> +	}
> +}
> +
> +/*
> + * randomize_text()
> + * Look through the core section looking for executable code sections.
> + * Store sections in an array and then shuffle the sections
> + * to reorder the functions.
> + */
> +static void randomize_text(struct module *mod, struct load_info *info)
> +{
> +	int i;
> +	int num_text_sections = 0;
> +	Elf_Shdr **text_list;
> +	int size = 0;
> +	int max_sections = info->hdr->e_shnum;
> +	unsigned int sec = find_sec(info, ".text");
> +
> +	if (!IS_ENABLED(CONFIG_FG_KASLR) || !kaslr_enabled())
> +		return;
> +
> +	if (sec == 0)
> +		return;
> +
> +	text_list = kmalloc_array(max_sections, sizeof(*text_list), GFP_KERNEL);
> +	if (text_list == NULL)
> +		return;
> +
> +	for (i = 0; i < max_sections; i++) {
> +		Elf_Shdr *shdr = &info->sechdrs[i];
> +		const char *sname = info->secstrings + shdr->sh_name;
> +
> +		if (!(shdr->sh_flags & SHF_ALLOC) ||
> +		    !(shdr->sh_flags & SHF_EXECINSTR) ||
> +		    strstarts(sname, ".init"))
> +			continue;
> +
> +		text_list[num_text_sections] = shdr;
> +		num_text_sections++;
> +	}
> +
> +	shuffle_text_list(text_list, num_text_sections);
> +
> +	for (i = 0; i < num_text_sections; i++) {
> +		Elf_Shdr *shdr = text_list[i];
> +		unsigned int infosec;
> +		const char *sname;
> +
> +		sname = info->secstrings + shdr->sh_name;
> +		infosec	= shdr->sh_info;
> +
> +		shdr->sh_entsize = get_offset(mod, &size, shdr, infosec);
> +	}
> +
> +	kfree(text_list);
> +}
> +
>  static int move_module(struct module *mod, struct load_info *info)
>  {
>  	int i;
> @@ -3282,6 +3365,8 @@ static int move_module(struct module *mod, struct load_info *info)
>  	} else
>  		mod->init_layout.base = NULL;
>  
> +	randomize_text(mod, info);
> +
>  	/* Transfer each section which specifies SHF_ALLOC */
>  	pr_debug("final section addresses:\n");
>  	for (i = 0; i < info->hdr->e_shnum; i++) {
> -- 
> 2.24.1
>
Jessica Yu Feb. 11, 2020, 12:39 p.m. UTC | #2
+++ Kees Cook [06/02/20 04:41 -0800]:
>On Wed, Feb 05, 2020 at 02:39:49PM -0800, Kristen Carlson Accardi wrote:
>> If a module has functions split out into separate text sections
>> (i.e. compiled with the -ffunction-sections flag), reorder the
>> functions to provide some code diversification to modules.
>>
>> Signed-off-by: Kristen Carlson Accardi <kristen@linux.intel.com>
>
>Probably a good idea to add Jessica to CC in next version:
>	Jessica Yu <jeyu@kernel.org>

Thanks :)

>Reviewed-by: Kees Cook <keescook@chromium.org>
>
>-Kees
>
>> ---
>>  kernel/module.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 85 insertions(+)
>>
>> diff --git a/kernel/module.c b/kernel/module.c
>> index b56f3224b161..231563e95e61 100644
>> --- a/kernel/module.c
>> +++ b/kernel/module.c
>> @@ -53,6 +53,8 @@
>>  #include <linux/bsearch.h>
>>  #include <linux/dynamic_debug.h>
>>  #include <linux/audit.h>
>> +#include <linux/random.h>
>> +#include <asm/setup.h>
>>  #include <uapi/linux/module.h>
>>  #include "module-internal.h"
>>
>> @@ -3245,6 +3247,87 @@ static int find_module_sections(struct module *mod, struct load_info *info)
>>  	return 0;
>>  }
>>
>> +/*
>> + * shuffle_text_list()
>> + * Use a Fisher Yates algorithm to shuffle a list of text sections.
>> + */
>> +static void shuffle_text_list(Elf_Shdr **list, int size)
>> +{
>> +	int i;
>> +	unsigned int j;
>> +	Elf_Shdr *temp;
>> +
>> +	for (i = size - 1; i > 0; i--) {
>> +		/*
>> +		 * TBD - seed. We need to be able to use a known
>> +		 * seed so that we can non-randomly randomize for
>> +		 * debugging.
>> +		 */
>> +
>> +		// pick a random index from 0 to i
>> +		get_random_bytes(&j, sizeof(j));
>> +		j = j % (i + 1);
>> +
>> +		temp = list[i];
>> +		list[i] = list[j];
>> +		list[j] = temp;
>> +	}
>> +}
>> +
>> +/*
>> + * randomize_text()
>> + * Look through the core section looking for executable code sections.
>> + * Store sections in an array and then shuffle the sections
>> + * to reorder the functions.
>> + */
>> +static void randomize_text(struct module *mod, struct load_info *info)
>> +{
>> +	int i;
>> +	int num_text_sections = 0;
>> +	Elf_Shdr **text_list;
>> +	int size = 0;
>> +	int max_sections = info->hdr->e_shnum;
>> +	unsigned int sec = find_sec(info, ".text");
>> +
>> +	if (!IS_ENABLED(CONFIG_FG_KASLR) || !kaslr_enabled())
>> +		return;

Maybe put this conditional before the call to randomize_text() in
move_module()? Just for code readability reasons (i.e., we only call
randomize_text() when CONFIG_FG_KASLR || kaslr_enabled()). But this is
just a matter of preference.

>> +
>> +	if (sec == 0)
>> +		return;
>> +
>> +	text_list = kmalloc_array(max_sections, sizeof(*text_list), GFP_KERNEL);
>> +	if (text_list == NULL)
>> +		return;
>> +
>> +	for (i = 0; i < max_sections; i++) {
>> +		Elf_Shdr *shdr = &info->sechdrs[i];
>> +		const char *sname = info->secstrings + shdr->sh_name;
>> +
>> +		if (!(shdr->sh_flags & SHF_ALLOC) ||
>> +		    !(shdr->sh_flags & SHF_EXECINSTR) ||
>> +		    strstarts(sname, ".init"))
>> +			continue;
>> +
>> +		text_list[num_text_sections] = shdr;
>> +		num_text_sections++;
>> +	}
>> +
>> +	shuffle_text_list(text_list, num_text_sections);
>> +
>> +	for (i = 0; i < num_text_sections; i++) {
>> +		Elf_Shdr *shdr = text_list[i];
>> +		unsigned int infosec;
>> +		const char *sname;
>> +
>> +		sname = info->secstrings + shdr->sh_name;

sname doesn't appear to be used after this (?)

>> +		infosec	= shdr->sh_info;
>> +
>> +		shdr->sh_entsize = get_offset(mod, &size, shdr, infosec);

get_offset() expects a section index as the last argument, but sh_info
is 0 for text sections (SHT_PROGBITS). It's really only used in
arch_mod_section_prepend() though, which is only defined for parisc.
We could perhaps save the section index in sh_info in the previous for
loop (hacky, I have to double check if this is actually safe, but it
seems sh_info only has signficance for SHT_REL* and SHT_SYMTAB
sections).

>> +	}
>> +
>> +	kfree(text_list);
>> +}
>> +
>>  static int move_module(struct module *mod, struct load_info *info)
>>  {
>>  	int i;
>> @@ -3282,6 +3365,8 @@ static int move_module(struct module *mod, struct load_info *info)
>>  	} else
>>  		mod->init_layout.base = NULL;
>>
>> +	randomize_text(mod, info);

Hm, I wonder if we should not incorporate the randomize_text() logic into
layout_sections() instead of move_module(), since logically speaking
layout_sections() determines all the section offsets and stores them
in sh_entsize, whereas move_module() does all the allocations and
copying to final destination. Just a thought.


Thanks!

Jessica

Patch
diff mbox series

diff --git a/kernel/module.c b/kernel/module.c
index b56f3224b161..231563e95e61 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -53,6 +53,8 @@ 
 #include <linux/bsearch.h>
 #include <linux/dynamic_debug.h>
 #include <linux/audit.h>
+#include <linux/random.h>
+#include <asm/setup.h>
 #include <uapi/linux/module.h>
 #include "module-internal.h"
 
@@ -3245,6 +3247,87 @@  static int find_module_sections(struct module *mod, struct load_info *info)
 	return 0;
 }
 
+/*
+ * shuffle_text_list()
+ * Use a Fisher Yates algorithm to shuffle a list of text sections.
+ */
+static void shuffle_text_list(Elf_Shdr **list, int size)
+{
+	int i;
+	unsigned int j;
+	Elf_Shdr *temp;
+
+	for (i = size - 1; i > 0; i--) {
+		/*
+		 * TBD - seed. We need to be able to use a known
+		 * seed so that we can non-randomly randomize for
+		 * debugging.
+		 */
+
+		// pick a random index from 0 to i
+		get_random_bytes(&j, sizeof(j));
+		j = j % (i + 1);
+
+		temp = list[i];
+		list[i] = list[j];
+		list[j] = temp;
+	}
+}
+
+/*
+ * randomize_text()
+ * Look through the core section looking for executable code sections.
+ * Store sections in an array and then shuffle the sections
+ * to reorder the functions.
+ */
+static void randomize_text(struct module *mod, struct load_info *info)
+{
+	int i;
+	int num_text_sections = 0;
+	Elf_Shdr **text_list;
+	int size = 0;
+	int max_sections = info->hdr->e_shnum;
+	unsigned int sec = find_sec(info, ".text");
+
+	if (!IS_ENABLED(CONFIG_FG_KASLR) || !kaslr_enabled())
+		return;
+
+	if (sec == 0)
+		return;
+
+	text_list = kmalloc_array(max_sections, sizeof(*text_list), GFP_KERNEL);
+	if (text_list == NULL)
+		return;
+
+	for (i = 0; i < max_sections; i++) {
+		Elf_Shdr *shdr = &info->sechdrs[i];
+		const char *sname = info->secstrings + shdr->sh_name;
+
+		if (!(shdr->sh_flags & SHF_ALLOC) ||
+		    !(shdr->sh_flags & SHF_EXECINSTR) ||
+		    strstarts(sname, ".init"))
+			continue;
+
+		text_list[num_text_sections] = shdr;
+		num_text_sections++;
+	}
+
+	shuffle_text_list(text_list, num_text_sections);
+
+	for (i = 0; i < num_text_sections; i++) {
+		Elf_Shdr *shdr = text_list[i];
+		unsigned int infosec;
+		const char *sname;
+
+		sname = info->secstrings + shdr->sh_name;
+		infosec	= shdr->sh_info;
+
+		shdr->sh_entsize = get_offset(mod, &size, shdr, infosec);
+	}
+
+	kfree(text_list);
+}
+
 static int move_module(struct module *mod, struct load_info *info)
 {
 	int i;
@@ -3282,6 +3365,8 @@  static int move_module(struct module *mod, struct load_info *info)
 	} else
 		mod->init_layout.base = NULL;
 
+	randomize_text(mod, info);
+
 	/* Transfer each section which specifies SHF_ALLOC */
 	pr_debug("final section addresses:\n");
 	for (i = 0; i < info->hdr->e_shnum; i++) {