stop using '%pK' for /proc/kallsyms pointer values
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Nov 2017 20:51:04 +0000 (12:51 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Nov 2017 20:51:04 +0000 (12:51 -0800)
Not only is it annoying to have one single flag for all pointers, as if
that was a global choice and all kernel pointers are the same, but %pK
can't get the 'access' vs 'open' time check right anyway.

So make the /proc/kallsyms pointer value code use logic specific to that
particular file.  We do continue to honor kptr_restrict, but the default
(which is unrestricted) is changed to instead take expected users into
account, and restrict access by default.

Right now the only actual expected user is kernel profiling, which has a
separate sysctl flag for kernel profile access.  There may be others.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
kernel/kallsyms.c

index 127e7cfafa55207a3fb45e5dc158c7885b949b49..51b49ed452e4e5dbccf6f30ece23068a315f969c 100644 (file)
@@ -480,6 +480,7 @@ struct kallsym_iter {
        char name[KSYM_NAME_LEN];
        char module_name[MODULE_NAME_LEN];
        int exported;
+       int show_value;
 };
 
 static int get_ksymbol_mod(struct kallsym_iter *iter)
@@ -580,14 +581,23 @@ static void s_stop(struct seq_file *m, void *p)
 {
 }
 
+#ifndef CONFIG_64BIT
+# define KALLSYM_FMT "%08lx"
+#else
+# define KALLSYM_FMT "%016lx"
+#endif
+
 static int s_show(struct seq_file *m, void *p)
 {
+       unsigned long value;
        struct kallsym_iter *iter = m->private;
 
        /* Some debugging symbols have no name.  Ignore them. */
        if (!iter->name[0])
                return 0;
 
+       value = iter->show_value ? iter->value : 0;
+
        if (iter->module_name[0]) {
                char type;
 
@@ -597,10 +607,10 @@ static int s_show(struct seq_file *m, void *p)
                 */
                type = iter->exported ? toupper(iter->type) :
                                        tolower(iter->type);
-               seq_printf(m, "%pK %c %s\t[%s]\n", (void *)iter->value,
+               seq_printf(m, KALLSYM_FMT " %c %s\t[%s]\n", value,
                           type, iter->name, iter->module_name);
        } else
-               seq_printf(m, "%pK %c %s\n", (void *)iter->value,
+               seq_printf(m, KALLSYM_FMT " %c %s\n", value,
                           iter->type, iter->name);
        return 0;
 }
@@ -612,6 +622,40 @@ static const struct seq_operations kallsyms_op = {
        .show = s_show
 };
 
+static inline int kallsyms_for_perf(void)
+{
+#ifdef CONFIG_PERF_EVENTS
+       extern int sysctl_perf_event_paranoid;
+       if (sysctl_perf_event_paranoid <= 1)
+               return 1;
+#endif
+       return 0;
+}
+
+/*
+ * We show kallsyms information even to normal users if we've enabled
+ * kernel profiling and are explicitly not paranoid (so kptr_restrict
+ * is clear, and sysctl_perf_event_paranoid isn't set).
+ *
+ * Otherwise, require CAP_SYSLOG (assuming kptr_restrict isn't set to
+ * block even that).
+ */
+static int kallsyms_show_value(void)
+{
+       switch (kptr_restrict) {
+       case 0:
+               if (kallsyms_for_perf())
+                       return 1;
+       /* fallthrough */
+       case 1:
+               if (has_capability_noaudit(current, CAP_SYSLOG))
+                       return 1;
+       /* fallthrough */
+       default:
+               return 0;
+       }
+}
+
 static int kallsyms_open(struct inode *inode, struct file *file)
 {
        /*
@@ -625,6 +669,7 @@ static int kallsyms_open(struct inode *inode, struct file *file)
                return -ENOMEM;
        reset_iter(iter, 0);
 
+       iter->show_value = kallsyms_show_value();
        return 0;
 }