| From 160251842cd35a75edfb0a1d76afa3eb674ff40a Mon Sep 17 00:00:00 2001 |
| From: Kees Cook <keescook@chromium.org> |
| Date: Thu, 2 Jul 2020 11:49:23 -0700 |
| Subject: kallsyms: Refactor kallsyms_show_value() to take cred |
| |
| From: Kees Cook <keescook@chromium.org> |
| |
| commit 160251842cd35a75edfb0a1d76afa3eb674ff40a upstream. |
| |
| In order to perform future tests against the cred saved during open(), |
| switch kallsyms_show_value() to operate on a cred, and have all current |
| callers pass current_cred(). This makes it very obvious where callers |
| are checking the wrong credential in their "read" contexts. These will |
| be fixed in the coming patches. |
| |
| Additionally switch return value to bool, since it is always used as a |
| direct permission check, not a 0-on-success, negative-on-error style |
| function return. |
| |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Kees Cook <keescook@chromium.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| include/linux/filter.h | 2 +- |
| include/linux/kallsyms.h | 5 +++-- |
| kernel/kallsyms.c | 17 +++++++++++------ |
| kernel/kprobes.c | 4 ++-- |
| kernel/module.c | 2 +- |
| 5 files changed, 18 insertions(+), 12 deletions(-) |
| |
| --- a/include/linux/filter.h |
| +++ b/include/linux/filter.h |
| @@ -858,7 +858,7 @@ static inline bool bpf_dump_raw_ok(void) |
| /* Reconstruction of call-sites is dependent on kallsyms, |
| * thus make dump the same restriction. |
| */ |
| - return kallsyms_show_value() == 1; |
| + return kallsyms_show_value(current_cred()); |
| } |
| |
| struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, |
| --- a/include/linux/kallsyms.h |
| +++ b/include/linux/kallsyms.h |
| @@ -18,6 +18,7 @@ |
| #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s]") + (KSYM_NAME_LEN - 1) + \ |
| 2*(BITS_PER_LONG*3/10) + (MODULE_NAME_LEN - 1) + 1) |
| |
| +struct cred; |
| struct module; |
| |
| static inline int is_kernel_inittext(unsigned long addr) |
| @@ -98,7 +99,7 @@ int lookup_symbol_name(unsigned long add |
| int lookup_symbol_attrs(unsigned long addr, unsigned long *size, unsigned long *offset, char *modname, char *name); |
| |
| /* How and when do we show kallsyms values? */ |
| -extern int kallsyms_show_value(void); |
| +extern bool kallsyms_show_value(const struct cred *cred); |
| |
| #else /* !CONFIG_KALLSYMS */ |
| |
| @@ -158,7 +159,7 @@ static inline int lookup_symbol_attrs(un |
| return -ERANGE; |
| } |
| |
| -static inline int kallsyms_show_value(void) |
| +static inline bool kallsyms_show_value(const struct cred *cred) |
| { |
| return false; |
| } |
| --- a/kernel/kallsyms.c |
| +++ b/kernel/kallsyms.c |
| @@ -645,19 +645,20 @@ static inline int kallsyms_for_perf(void |
| * Otherwise, require CAP_SYSLOG (assuming kptr_restrict isn't set to |
| * block even that). |
| */ |
| -int kallsyms_show_value(void) |
| +bool kallsyms_show_value(const struct cred *cred) |
| { |
| switch (kptr_restrict) { |
| case 0: |
| if (kallsyms_for_perf()) |
| - return 1; |
| + return true; |
| /* fallthrough */ |
| case 1: |
| - if (has_capability_noaudit(current, CAP_SYSLOG)) |
| - return 1; |
| + if (security_capable(cred, &init_user_ns, CAP_SYSLOG, |
| + CAP_OPT_NOAUDIT) == 0) |
| + return true; |
| /* fallthrough */ |
| default: |
| - return 0; |
| + return false; |
| } |
| } |
| |
| @@ -674,7 +675,11 @@ static int kallsyms_open(struct inode *i |
| return -ENOMEM; |
| reset_iter(iter, 0); |
| |
| - iter->show_value = kallsyms_show_value(); |
| + /* |
| + * Instead of checking this on every s_show() call, cache |
| + * the result here at open time. |
| + */ |
| + iter->show_value = kallsyms_show_value(file->f_cred); |
| return 0; |
| } |
| |
| --- a/kernel/kprobes.c |
| +++ b/kernel/kprobes.c |
| @@ -2362,7 +2362,7 @@ static void report_probe(struct seq_file |
| else |
| kprobe_type = "k"; |
| |
| - if (!kallsyms_show_value()) |
| + if (!kallsyms_show_value(current_cred())) |
| addr = NULL; |
| |
| if (sym) |
| @@ -2463,7 +2463,7 @@ static int kprobe_blacklist_seq_show(str |
| * If /proc/kallsyms is not showing kernel address, we won't |
| * show them here either. |
| */ |
| - if (!kallsyms_show_value()) |
| + if (!kallsyms_show_value(current_cred())) |
| seq_printf(m, "0x%px-0x%px\t%ps\n", NULL, NULL, |
| (void *)ent->start_addr); |
| else |
| --- a/kernel/module.c |
| +++ b/kernel/module.c |
| @@ -4391,7 +4391,7 @@ static int modules_open(struct inode *in |
| |
| if (!err) { |
| struct seq_file *m = file->private_data; |
| - m->private = kallsyms_show_value() ? NULL : (void *)8ul; |
| + m->private = kallsyms_show_value(current_cred()) ? NULL : (void *)8ul; |
| } |
| |
| return err; |