diff mbox

[v4] scripts: add leaking_addresses.pl

Message ID CA+55aFwiaQezaPi2nzmyuFZNgR3PFcsWjjBTHJYBesKrh-Q_pQ@mail.gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Linus Torvalds Nov. 8, 2017, 12:59 a.m. UTC
On Tue, Nov 7, 2017 at 3:36 PM, Laura Abbott <labbott@redhat.com> wrote:
>
> I'd probably put /proc/kallsyms and /proc/modules on the omit list
> since those are designed to leak addresses to userspace.

Well, they are indeed designed to leak addresses, but not a lot of
people should care.

So I think we could tighten them up.

For example, maybe /proc/kallsyms could just default to not showing
values to non-root users.

We *did* originally try to use "kptr_restrict" with a default value of
1, it's just that it was never fixable on a case-by-case basis as
people started saying "that breaks my flow, because xyz".

But if we do it for one file at a time, we probably *can* try to fix complaints.

Something like the attached TOTALLY UNTESTED patch. It's meant more as
an RFC, not for application, but it's also meant to show how we can
tailor the behavior for specific workflow issues.

So take that "kallsyms_for_perf()" thing as an example of how we can
say "hey, if you already have access to kernel profiling anyway,
there's no point in hiding kallsyms".

And there may be other similar things we can do.

The situation with /proc/modules should be similar. Using
kptr_restrict was a big hammer and might have broken something
unrelated, but did anybody actually care about the particular case of
/proc/modules not showing the module address to normal users? probably
not. "lsmod" certainly doesn't care, and that's what people really
want.

Both /proc/kallsyms and /proc/modules _used_ to be really important
for oops reporting, but that was long ago when the kernel didn't
report symbol information of its own. So we have historical reasons
for people to be able to read those files, but those are mainly things
that aren't relevant (or even possible) on modern kernels anyway.

So I don'r think we should omit /proc/kallsyms and /proc/modules - we
should just fix them.

The attached patch may not be good enough as is, but maybe something
_like_ it will work well enough that people won't care?

(And do note the "TOTALLY UNTESTED". It seems to compile. But maybe I
got some test exactly the wrong way around and it doesn't actually
_work_. Caveat testor).

                     Linus
kernel/kallsyms.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

Comments

Linus Torvalds Nov. 8, 2017, 8:39 p.m. UTC | #1
On Tue, Nov 7, 2017 at 4:59 PM, Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> For example, maybe /proc/kallsyms could just default to not showing
> values to non-root users.
>
> Something like the attached TOTALLY UNTESTED patch. It's meant more as
> an RFC, not for application, but it's also meant to show how we can
> tailor the behavior for specific workflow issues.

It seems to work, except I got the condition wrong for
sysctl_perf_event_paranoid.

It should if

        if (sysctl_perf_event_paranoid <= 1)
                return 1;

rather than "<= 0", because '1' still means "allow kernel profiling"
(and the default value is "2").

But I don't know if there is anything else than the profiling code
that _really_ wants access to /proc/kallsyms in user space as a
regular user.

That said, that patch also fixes the /proc/kallsyms root check, in
that now you can do:

    sudo head < /proc/kallsyms

and it still shows all zeroes - because the file was *opened* as a
normal user. That's how UNIX file access security works, and how it is
fundamentally supposed to work (ie passing a file descriptor to a sui
program doesn't magically make it gain privileges).

Anyway, I'm obviously not going to commit that patch now, but I'd be
happy to try it for the 4.15 merge window, to see if we can close
/proc/kallsyms without people screaming..

            Linus
Kaiwan N Billimoria Nov. 9, 2017, 4:43 a.m. UTC | #2
> But I don't know if there is anything else than the profiling code
> that _really_ wants access to /proc/kallsyms in user space as a
> regular user.

Am unsure about this, but kprobes? (/jprobes/kretprobes), and by
extension, wrappers over this infra (like SystemTap)?
I (hazily) recollect a script I once wrote (years back though) that
collects kernel virtual addresses off of kallsyms for the purpose of
passing them to a 'helper' kernel module that uses kprobes. I realize
that 'modern' kprobes exposes APIs that just require the symbolic name
& that they're anyway at kernel privilege... but the point is, a
usermode script was picking up and passing the kernel addresses.

Also, what about kernel addresses exposed via System.map?
Oh, just checked, it's root rw only.. pl ignore.

> That said, that patch also fixes the /proc/kallsyms root check, in
> that now you can do:
>
>     sudo head < /proc/kallsyms
>
> and it still shows all zeroes - because the file was *opened* as a
> normal user. That's how UNIX file access security works, and how it is
> fundamentally supposed to work (ie passing a file descriptor to a sui
> program doesn't magically make it gain privileges).

Indeed.
Kaiwan N Billimoria Nov. 9, 2017, 4:54 a.m. UTC | #3
On Thu, Nov 9, 2017 at 10:13 AM, Kaiwan N Billimoria
<kaiwan.billimoria@gmail.com> wrote:
>> But I don't know if there is anything else than the profiling code
>> that _really_ wants access to /proc/kallsyms in user space as a
>> regular user.
>

Front-ends to ftrace, like trace-cmd?
[from the trace-cmd git repo (Steve Rostedt, pl stand up, pl stand up :-)
Documentation/trace-cmd-restore.1.txt :
...
*-k* kallsyms::
    Used with *-c*, it overrides where to read the kallsyms file from.
    By default, /proc/kallsyms is used. *-k* will override the file to
    read the kallsyms from. This can be useful if the trace.dat file
    to create is from another machine. Just copy the /proc/kallsyms
    file locally, and use *-k* to point to that file.
...
]

> Am unsure about this, but kprobes? (/jprobes/kretprobes), and by
> extension, wrappers over this infra (like SystemTap)?
> I (hazily) recollect a script I once wrote (years back though) that
> collects kernel virtual addresses off of kallsyms for the purpose of
> passing them to a 'helper' kernel module that uses kprobes. I realize
> that 'modern' kprobes exposes APIs that just require the symbolic name
> & that they're anyway at kernel privilege... but the point is, a
> usermode script was picking up and passing the kernel addresses.
>
> Also, what about kernel addresses exposed via System.map?
> Oh, just checked, it's root rw only.. pl ignore.
Steven Rostedt Nov. 9, 2017, 6:11 p.m. UTC | #4
On Thu, 9 Nov 2017 10:24:32 +0530
Kaiwan N Billimoria <kaiwan.billimoria@gmail.com> wrote:

> On Thu, Nov 9, 2017 at 10:13 AM, Kaiwan N Billimoria
> <kaiwan.billimoria@gmail.com> wrote:
> >> But I don't know if there is anything else than the profiling code
> >> that _really_ wants access to /proc/kallsyms in user space as a
> >> regular user.  
> >  
> 
> Front-ends to ftrace, like trace-cmd?
> [from the trace-cmd git repo (Steve Rostedt, pl stand up, pl stand up :-)
> Documentation/trace-cmd-restore.1.txt :

Yes, profiling and tracing are similar. And you need to be root to run
the recording anyway. Thus, as long as root user can read kallsyms,
trace-cmd should be fine. As trace-cmd requires root access to do any
ftrace tracing.

-- Steve
Kaiwan N Billimoria Nov. 10, 2017, 3:03 a.m. UTC | #5
>
> Yes, profiling and tracing are similar. And you need to be root to run
> the recording anyway. Thus, as long as root user can read kallsyms,
> trace-cmd should be fine. As trace-cmd requires root access to do any
> ftrace tracing.
>
> -- Steve
Got it, thanks..
diff mbox

Patch

diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index 127e7cfafa55..5b1299c1e4b0 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -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 <= 0)
+		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;
 }