Message ID | 20200717170008.5949-10-kristen@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Function Granular KASLR | expand |
On Fri, Jul 17, 2020 at 10:00:06AM -0700, Kristen Carlson Accardi wrote: > This patch makes /proc/kallsyms display in a random order, rather > than sorted by address in order to hide the newly randomized address > layout. Ah! Much nicer. Is there any reason not to just do this unconditionally, regardless of FGKASLR? It's a smallish dynamic allocation, and displaying kallsyms is hardly fast-path... > > Signed-off-by: Kristen Carlson Accardi <kristen@linux.intel.com> > Reviewed-by: Tony Luck <tony.luck@intel.com> > Tested-by: Tony Luck <tony.luck@intel.com> > --- > kernel/kallsyms.c | 163 +++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 162 insertions(+), 1 deletion(-) > > diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c > index bb14e64f62a4..45d147f7f10e 100644 > --- a/kernel/kallsyms.c > +++ b/kernel/kallsyms.c > @@ -446,6 +446,12 @@ struct kallsym_iter { > int show_value; > }; > > +struct kallsyms_shuffled_iter { > + struct kallsym_iter iter; > + loff_t total_syms; > + loff_t shuffled_index[]; > +}; > + > int __weak arch_get_kallsym(unsigned int symnum, unsigned long *value, > char *type, char *name) > { > @@ -661,7 +667,7 @@ bool kallsyms_show_value(const struct cred *cred) > } > } > > -static int kallsyms_open(struct inode *inode, struct file *file) > +static int __kallsyms_open(struct inode *inode, struct file *file) > { > /* > * We keep iterator in m->private, since normal case is to > @@ -682,6 +688,161 @@ static int kallsyms_open(struct inode *inode, struct file *file) > return 0; > } > > +/* > + * When function granular kaslr is enabled, we need to print out the symbols > + * at random so we don't reveal the new layout. > + */ > +#if defined(CONFIG_FG_KASLR) > +static int update_random_pos(struct kallsyms_shuffled_iter *s_iter, > + loff_t pos, loff_t *new_pos) > +{ > + loff_t new; > + > + if (pos >= s_iter->total_syms) > + return 0; > + > + new = s_iter->shuffled_index[pos]; > + > + /* > + * normally this would be done as part of update_iter, however, > + * we want to avoid triggering this in the event that new is > + * zero since we don't want to blow away our pos end indicators. > + */ > + if (new == 0) { > + s_iter->iter.name[0] = '\0'; > + s_iter->iter.nameoff = get_symbol_offset(new); > + s_iter->iter.pos = new; > + } > + > + *new_pos = new; > + return 1; > +} > + > +static void *shuffled_start(struct seq_file *m, loff_t *pos) > +{ > + struct kallsyms_shuffled_iter *s_iter = m->private; > + loff_t new_pos; > + > + if (!update_random_pos(s_iter, *pos, &new_pos)) > + return NULL; > + > + return s_start(m, &new_pos); > +} > + > +static void *shuffled_next(struct seq_file *m, void *p, loff_t *pos) > +{ > + struct kallsyms_shuffled_iter *s_iter = m->private; > + loff_t new_pos; > + > + (*pos)++; > + > + if (!update_random_pos(s_iter, *pos, &new_pos)) > + return NULL; > + > + if (!update_iter(m->private, new_pos)) > + return NULL; > + > + return p; > +} > + > +/* > + * shuffle_index_list() > + * Use a Fisher Yates algorithm to shuffle a list of text sections. > + */ > +static void shuffle_index_list(loff_t *indexes, loff_t size) > +{ > + int i; > + unsigned int j; > + loff_t temp; > + > + for (i = size - 1; i > 0; i--) { > + /* pick a random index from 0 to i */ > + get_random_bytes(&j, sizeof(j)); > + j = j % (i + 1); > + > + temp = indexes[i]; > + indexes[i] = indexes[j]; > + indexes[j] = temp; > + } > +} > + > +static const struct seq_operations kallsyms_shuffled_op = { > + .start = shuffled_start, > + .next = shuffled_next, > + .stop = s_stop, > + .show = s_show > +}; > + > +static int kallsyms_random_open(struct inode *inode, struct file *file) > +{ > + loff_t pos; > + struct kallsyms_shuffled_iter *shuffled_iter; > + struct kallsym_iter iter; > + bool show_value; > + > + /* > + * If privileged, go ahead and use the normal algorithm for > + * displaying symbols > + */ > + show_value = kallsyms_show_value(file->f_cred); > + if (show_value) > + return __kallsyms_open(inode, file); > + > + /* > + * we need to figure out how many extra symbols there are > + * to print out past kallsyms_num_syms > + */ > + pos = kallsyms_num_syms; > + reset_iter(&iter, 0); > + do { > + if (!update_iter(&iter, pos)) > + break; > + pos++; > + } while (1); Can this be tracked separately instead of needing to search for it every time? (Looks like it's modules and ftrace? Could they each have a *_num_sysms?) (I need to go read how kallsyms doesn't miscount in general when the symbol table changes out from under it...)
On Sun, 2020-07-19 at 18:25 -0700, Kees Cook wrote: > On Fri, Jul 17, 2020 at 10:00:06AM -0700, Kristen Carlson Accardi > wrote: > > This patch makes /proc/kallsyms display in a random order, rather > > than sorted by address in order to hide the newly randomized > > address > > layout. > > Ah! Much nicer. Is there any reason not to just do this > unconditionally, > regardless of FGKASLR? It's a smallish dynamic allocation, and > displaying kallsyms is hardly fast-path... My only concern would be whether or not there are scripts out there which assume the list would be ordered. If someone chooses to enable CONFIG_FG_KASLR, I think that it is reasonable to break those scripts. On the flip side, I don't know why it needs to come out of /proc/kallsyms in order, you can always just sort it after the fact if you need it sorted. It would make it more maintainable to not special case this. Hopefully a maintainer will comment on their preference. Another thing I do in this patch is continue to use the existing sorted by address functions if you are root. I didn't know if this was neccessary - it'd be nice if we could just do it the same way all the time. But I need some guidance here. > > > Signed-off-by: Kristen Carlson Accardi <kristen@linux.intel.com> > > Reviewed-by: Tony Luck <tony.luck@intel.com> > > Tested-by: Tony Luck <tony.luck@intel.com> > > --- > > kernel/kallsyms.c | 163 > > +++++++++++++++++++++++++++++++++++++++++++++- > > 1 file changed, 162 insertions(+), 1 deletion(-) > > > > diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c > > index bb14e64f62a4..45d147f7f10e 100644 > > --- a/kernel/kallsyms.c > > +++ b/kernel/kallsyms.c > > @@ -446,6 +446,12 @@ struct kallsym_iter { > > int show_value; > > }; > > > > +struct kallsyms_shuffled_iter { > > + struct kallsym_iter iter; > > + loff_t total_syms; > > > > > > (I need to go read how kallsyms doesn't miscount in general when > > the > > > symbol table changes out from under it...) > > > > > > > > + loff_t shuffled_index[]; > > +}; > > + > > int __weak arch_get_kallsym(unsigned int symnum, unsigned long > > *value, > > char *type, char *name) > > { > > @@ -661,7 +667,7 @@ bool kallsyms_show_value(const struct cred > > *cred) > > } > > } > > > > -static int kallsyms_open(struct inode *inode, struct file *file) > > +static int __kallsyms_open(struct inode *inode, struct file *file) > > { > > /* > > * We keep iterator in m->private, since normal case is to > > @@ -682,6 +688,161 @@ static int kallsyms_open(struct inode *inode, > > struct file *file) > > return 0; > > } > > > > +/* > > + * When function granular kaslr is enabled, we need to print out > > the symbols > > + * at random so we don't reveal the new layout. > > + */ > > +#if defined(CONFIG_FG_KASLR) > > +static int update_random_pos(struct kallsyms_shuffled_iter > > *s_iter, > > + loff_t pos, loff_t *new_pos) > > +{ > > + loff_t new; > > + > > + if (pos >= s_iter->total_syms) > > + return 0; > > + > > + new = s_iter->shuffled_index[pos]; > > + > > + /* > > + * normally this would be done as part of update_iter, however, > > + * we want to avoid triggering this in the event that new is > > + * zero since we don't want to blow away our pos end > > indicators. > > + */ > > + if (new == 0) { > > + s_iter->iter.name[0] = '\0'; > > + s_iter->iter.nameoff = get_symbol_offset(new); > > + s_iter->iter.pos = new; > > + } > > + > > + *new_pos = new; > > + return 1; > > +} > > + > > +static void *shuffled_start(struct seq_file *m, loff_t *pos) > > +{ > > + struct kallsyms_shuffled_iter *s_iter = m->private; > > + loff_t new_pos; > > + > > + if (!update_random_pos(s_iter, *pos, &new_pos)) > > + return NULL; > > + > > + return s_start(m, &new_pos); > > +} > > + > > +static void *shuffled_next(struct seq_file *m, void *p, loff_t > > *pos) > > +{ > > + struct kallsyms_shuffled_iter *s_iter = m->private; > > + loff_t new_pos; > > + > > + (*pos)++; > > + > > + if (!update_random_pos(s_iter, *pos, &new_pos)) > > + return NULL; > > + > > + if (!update_iter(m->private, new_pos)) > > + return NULL; > > + > > + return p; > > +} > > + > > +/* > > + * shuffle_index_list() > > + * Use a Fisher Yates algorithm to shuffle a list of text > > sections. > > + */ > > +static void shuffle_index_list(loff_t *indexes, loff_t size) > > +{ > > + int i; > > + unsigned int j; > > + loff_t temp; > > + > > + for (i = size - 1; i > 0; i--) { > > + /* pick a random index from 0 to i */ > > + get_random_bytes(&j, sizeof(j)); > > + j = j % (i + 1); > > + > > + temp = indexes[i]; > > + indexes[i] = indexes[j]; > > + indexes[j] = temp; > > + } > > +} > > + > > +static const struct seq_operations kallsyms_shuffled_op = { > > + .start = shuffled_start, > > + .next = shuffled_next, > > + .stop = s_stop, > > + .show = s_show > > +}; > > + > > +static int kallsyms_random_open(struct inode *inode, struct file > > *file) > > +{ > > + loff_t pos; > > + struct kallsyms_shuffled_iter *shuffled_iter; > > + struct kallsym_iter iter; > > + bool show_value; > > + > > + /* > > + * If privileged, go ahead and use the normal algorithm for > > + * displaying symbols > > + */ > > + show_value = kallsyms_show_value(file->f_cred); > > + if (show_value) > > + return __kallsyms_open(inode, file); > > + > > + /* > > + * we need to figure out how many extra symbols there are > > + * to print out past kallsyms_num_syms > > + */ > > + pos = kallsyms_num_syms; > > + reset_iter(&iter, 0); > > + do { > > + if (!update_iter(&iter, pos)) > > + break; > > + pos++; > > + } while (1); > > Can this be tracked separately instead of needing to search for it > every > time? (Looks like it's modules and ftrace? Could they each have a > *_num_sysms?) It could, but I'd probably have to make more changes to those subsystems to keep this information there than I would to just do this search here. Because we start search at the end of the kernel core symbols, I don't think this search takes too many cycles to complete. On my system running a standard distro config the number of module and bpf symbols is not all that many, especially compared to the kernel core symbols.
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index bb14e64f62a4..45d147f7f10e 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -446,6 +446,12 @@ struct kallsym_iter { int show_value; }; +struct kallsyms_shuffled_iter { + struct kallsym_iter iter; + loff_t total_syms; + loff_t shuffled_index[]; +}; + int __weak arch_get_kallsym(unsigned int symnum, unsigned long *value, char *type, char *name) { @@ -661,7 +667,7 @@ bool kallsyms_show_value(const struct cred *cred) } } -static int kallsyms_open(struct inode *inode, struct file *file) +static int __kallsyms_open(struct inode *inode, struct file *file) { /* * We keep iterator in m->private, since normal case is to @@ -682,6 +688,161 @@ static int kallsyms_open(struct inode *inode, struct file *file) return 0; } +/* + * When function granular kaslr is enabled, we need to print out the symbols + * at random so we don't reveal the new layout. + */ +#if defined(CONFIG_FG_KASLR) +static int update_random_pos(struct kallsyms_shuffled_iter *s_iter, + loff_t pos, loff_t *new_pos) +{ + loff_t new; + + if (pos >= s_iter->total_syms) + return 0; + + new = s_iter->shuffled_index[pos]; + + /* + * normally this would be done as part of update_iter, however, + * we want to avoid triggering this in the event that new is + * zero since we don't want to blow away our pos end indicators. + */ + if (new == 0) { + s_iter->iter.name[0] = '\0'; + s_iter->iter.nameoff = get_symbol_offset(new); + s_iter->iter.pos = new; + } + + *new_pos = new; + return 1; +} + +static void *shuffled_start(struct seq_file *m, loff_t *pos) +{ + struct kallsyms_shuffled_iter *s_iter = m->private; + loff_t new_pos; + + if (!update_random_pos(s_iter, *pos, &new_pos)) + return NULL; + + return s_start(m, &new_pos); +} + +static void *shuffled_next(struct seq_file *m, void *p, loff_t *pos) +{ + struct kallsyms_shuffled_iter *s_iter = m->private; + loff_t new_pos; + + (*pos)++; + + if (!update_random_pos(s_iter, *pos, &new_pos)) + return NULL; + + if (!update_iter(m->private, new_pos)) + return NULL; + + return p; +} + +/* + * shuffle_index_list() + * Use a Fisher Yates algorithm to shuffle a list of text sections. + */ +static void shuffle_index_list(loff_t *indexes, loff_t size) +{ + int i; + unsigned int j; + loff_t temp; + + for (i = size - 1; i > 0; i--) { + /* pick a random index from 0 to i */ + get_random_bytes(&j, sizeof(j)); + j = j % (i + 1); + + temp = indexes[i]; + indexes[i] = indexes[j]; + indexes[j] = temp; + } +} + +static const struct seq_operations kallsyms_shuffled_op = { + .start = shuffled_start, + .next = shuffled_next, + .stop = s_stop, + .show = s_show +}; + +static int kallsyms_random_open(struct inode *inode, struct file *file) +{ + loff_t pos; + struct kallsyms_shuffled_iter *shuffled_iter; + struct kallsym_iter iter; + bool show_value; + + /* + * If privileged, go ahead and use the normal algorithm for + * displaying symbols + */ + show_value = kallsyms_show_value(file->f_cred); + if (show_value) + return __kallsyms_open(inode, file); + + /* + * we need to figure out how many extra symbols there are + * to print out past kallsyms_num_syms + */ + pos = kallsyms_num_syms; + reset_iter(&iter, 0); + do { + if (!update_iter(&iter, pos)) + break; + pos++; + } while (1); + + /* + * add storage space for an array of loff_t equal to the size + * of the total number of symbols we need to print + */ + shuffled_iter = __seq_open_private(file, &kallsyms_shuffled_op, + sizeof(*shuffled_iter) + + (sizeof(pos) * pos)); + if (!shuffled_iter) + return -ENOMEM; + + reset_iter(&shuffled_iter->iter, 0); + shuffled_iter->iter.show_value = show_value; + shuffled_iter->total_syms = pos; + + /* + * the existing update_iter algorithm requires that we + * are either moving along increasing pos sequentially, + * or that these values are correct. Since these values + * were discovered above, initialize our new iter so we + * can use update_iter non-sequentially. + */ + shuffled_iter->iter.pos_arch_end = iter.pos_arch_end; + shuffled_iter->iter.pos_mod_end = iter.pos_mod_end; + shuffled_iter->iter.pos_ftrace_mod_end = iter.pos_ftrace_mod_end; + + /* + * initialize the array with all possible pos values, then + * shuffle the array so that the values will display in a random + * order. + */ + for (pos = 0; pos < shuffled_iter->total_syms; pos++) + shuffled_iter->shuffled_index[pos] = pos; + + shuffle_index_list(shuffled_iter->shuffled_index, shuffled_iter->total_syms); + + return 0; +} + +#define kallsyms_open kallsyms_random_open +#else +#define kallsyms_open __kallsyms_open +#endif /* CONFIG_FG_KASLR */ + #ifdef CONFIG_KGDB_KDB const char *kdb_walk_kallsyms(loff_t *pos) {