Message ID | 20200205223950.1212394-11-kristen@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Finer grained kernel address space randomization | expand |
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 >
+++ 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
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++) {
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(+)