Re: [PATCH v4 09/10] kallsyms: Hide layout

From: Kees Cook
Date: Sun Jul 19 2020 - 21:25:35 EST


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@xxxxxxxxxxxxxxx>
> Reviewed-by: Tony Luck <tony.luck@xxxxxxxxx>
> Tested-by: Tony Luck <tony.luck@xxxxxxxxx>
> ---
> 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...)


--
Kees Cook