Re: [PATCH 8/9] kallsyms: hide layout
From: Ard Biesheuvel
Date: Mon Apr 20 2020 - 07:58:24 EST
On Wed, 15 Apr 2020 at 23:06, Kristen Carlson Accardi
<kristen@xxxxxxxxxxxxxxx> wrote:
>
> To support finer grained kaslr (fgkaslr), we need to hide our sorted
> list of symbols, since this will give away our new layout.
> This patch makes /proc/kallsyms only visible to priviledged users.
>
Does it?
> Signed-off-by: Kristen Carlson Accardi <kristen@xxxxxxxxxxxxxxx>
> ---
> kernel/kallsyms.c | 138 +++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 137 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
> index 16c8c605f4b0..861972b6a879 100644
> --- a/kernel/kallsyms.c
> +++ b/kernel/kallsyms.c
> @@ -25,6 +25,7 @@
> #include <linux/filter.h>
> #include <linux/ftrace.h>
> #include <linux/compiler.h>
> +#include <linux/list_sort.h>
>
> /*
> * These will be re-linked against their real values
> @@ -446,6 +447,11 @@ struct kallsym_iter {
> int show_value;
> };
>
> +struct kallsyms_iter_list {
> + struct kallsym_iter iter;
> + struct list_head next;
> +};
> +
> int __weak arch_get_kallsym(unsigned int symnum, unsigned long *value,
> char *type, char *name)
> {
> @@ -660,6 +666,121 @@ int kallsyms_show_value(void)
> }
> }
>
> +static int sorted_show(struct seq_file *m, void *p)
> +{
> + struct list_head *list = m->private;
> + struct kallsyms_iter_list *iter;
> + int rc;
> +
> + if (list_empty(list))
> + return 0;
> +
> + iter = list_first_entry(list, struct kallsyms_iter_list, next);
> +
> + m->private = iter;
> + rc = s_show(m, p);
> + m->private = list;
> +
> + list_del(&iter->next);
> + kfree(iter);
> +
> + return rc;
> +}
> +
> +static void *sorted_start(struct seq_file *m, loff_t *pos)
> +{
> + return m->private;
> +}
> +
> +static void *sorted_next(struct seq_file *m, void *p, loff_t *pos)
> +{
> + struct list_head *list = m->private;
> +
> + (*pos)++;
> +
> + if (list_empty(list))
> + return NULL;
> +
> + return p;
> +}
> +
> +static const struct seq_operations kallsyms_sorted_op = {
> + .start = sorted_start,
> + .next = sorted_next,
> + .stop = s_stop,
> + .show = sorted_show
> +};
> +
> +static int kallsyms_list_cmp(void *priv, struct list_head *a,
> + struct list_head *b)
> +{
> + struct kallsyms_iter_list *iter_a, *iter_b;
> +
> + iter_a = list_entry(a, struct kallsyms_iter_list, next);
> + iter_b = list_entry(b, struct kallsyms_iter_list, next);
> +
> + return strcmp(iter_a->iter.name, iter_b->iter.name);
> +}
> +
> +int get_all_symbol_name(void *data, const char *name, struct module *mod,
> + unsigned long addr)
> +{
> + unsigned long sym_pos;
> + struct kallsyms_iter_list *node, *last;
> + struct list_head *head = (struct list_head *)data;
> +
> + node = kmalloc(sizeof(*node), GFP_KERNEL);
> + if (!node)
> + return -ENOMEM;
> +
> + if (list_empty(head)) {
> + sym_pos = 0;
> + memset(node, 0, sizeof(*node));
> + reset_iter(&node->iter, 0);
> + node->iter.show_value = kallsyms_show_value();
> + } else {
> + last = list_first_entry(head, struct kallsyms_iter_list, next);
> + memcpy(node, last, sizeof(*node));
> + sym_pos = last->iter.pos;
> + }
> +
> + INIT_LIST_HEAD(&node->next);
> + list_add(&node->next, head);
> +
> + /*
> + * update_iter returns false when at end of file
> + * which in this case we don't care about and can
> + * safely ignore. update_iter() will increment
> + * the value of iter->pos, for ksymbol_core.
> + */
> + if (sym_pos >= kallsyms_num_syms)
> + sym_pos++;
> +
> + (void) update_iter(&node->iter, sym_pos);
> +
> + return 0;
> +}
> +
> +static int kallsyms_sorted_open(struct inode *inode, struct file *file)
> +{
> + int ret;
> + struct list_head *list;
> +
> + list = __seq_open_private(file, &kallsyms_sorted_op, sizeof(*list));
> + if (!list)
> + return -ENOMEM;
> +
> + INIT_LIST_HEAD(list);
> +
> + ret = kallsyms_on_each_symbol(get_all_symbol_name, list);
> + if (ret != 0)
> + return ret;
> +
> + list_sort(NULL, list, kallsyms_list_cmp);
> +
Could we do the sort at init time rather than open time?
> + return 0;
> +}
> +
> static int kallsyms_open(struct inode *inode, struct file *file)
> {
> /*
> @@ -704,9 +825,24 @@ static const struct proc_ops kallsyms_proc_ops = {
> .proc_release = seq_release_private,
> };
>
> +static const struct proc_ops kallsyms_sorted_proc_ops = {
> + .proc_open = kallsyms_sorted_open,
> + .proc_read = seq_read,
> + .proc_lseek = seq_lseek,
> + .proc_release = seq_release_private,
> +};
> +
> static int __init kallsyms_init(void)
> {
> - proc_create("kallsyms", 0444, NULL, &kallsyms_proc_ops);
> + /*
> + * When fine grained kaslr is enabled, we need to
> + * print out the symbols sorted by name rather than by
> + * by address, because this reveals the randomization order.
> + */
> + if (!IS_ENABLED(CONFIG_FG_KASLR))
> + proc_create("kallsyms", 0444, NULL, &kallsyms_proc_ops);
> + else
> + proc_create("kallsyms", 0444, NULL, &kallsyms_sorted_proc_ops);
Can we just switch to the sorted version unconditionally instead? Or
is the output order of /proc/kallsyms considered kernel ABI?
> return 0;
> }
> device_initcall(kallsyms_init);
> --
> 2.20.1
>