[PATCH 1/2] Fix perf LBR filtering

From: Andi Kleen
Date: Wed Apr 24 2013 - 19:05:04 EST


From: Andi Kleen <ak@xxxxxxxxxxxxxxx>

The perf LBR code has special code to filter specific
instructions in software.

The LBR logs any instruction address, even if IP just faulted.
This means user space can control any address by just branching
to a bad address.

On a modern Intel system the only software filtering needed
is to include SYSCALL/RETs in PERF_SAMPLE_BRANCH_ANY_CALL/RETURN.
The hardware call filter only handles short calls, but syscall
is a far call. So it enables far call logging too, but removes
any other far calls (like interrupts) by looking at the instruction.
On older systems some additional software filtering is done too,
to work a problem that CALLs can be only logged together with
indirect jumps.

It currently assumes that any address that looks like a kernel
address can be safely referenced.

But that is dangerous if can be controlled by the user:
- It can be used to crash the kernel
- It allows to probe any physical address for a small set of values
(valid call op codes) which is an information leak.
- It may point to a side effect on read MMIO region

So we cannot reference kernel addresses safely.

Possible options:

I) Disable FAR calls for ANY_CALL/RETURNS.
This just means syscalls are not logged
as calls. This also lowers the overhead of call logging.
This changes semantics slightly.
This is reasonable on Sandy Bridge and later, but would
cause additional problems on Nehalem and Westmere with
their additional filters.

II) Simple disable any filtering for kernel space.
This means interrupts in kernel space are reported as calls
and on Nehalem/Westmere some indirect jumps are reported
as calls too

III) Enumerate all the kernel entry points and check.
Any bad call must have a kernel entry point as to.
This seemed to fragile to maintain.

IV) Enumerate all kernel code and check for these ranges.
Quite complicated, especially with the new kernel code JITs.
Would also allow to probe for kernel code (defeating randomized kernel)

This patch implements II: Simply disable software filtering for
any kernel address, which seemed the best.
(I) would be also an option and was earlier implemented in
https://patchwork.kernel.org/patch/2468351/
(however this patch still leaves Nehalem/Westmere/Atom open to the problem)
(III) and (IV) appear too complicated and risky.

Should be applied to applicable stable branches too. The problem
goes back a long time.

Signed-off-by: Andi Kleen <ak@xxxxxxxxxxxxxxx>
---
arch/x86/kernel/cpu/perf_event_intel_lbr.c | 18 +++++++++++++++---
1 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
index da02e9c..ae8c76f 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
@@ -442,15 +442,27 @@ static int branch_type(unsigned long from, unsigned long to)
return X86_BR_NONE;

addr = buf;
- } else
- addr = (void *)from;
+ } else {
+ /*
+ * The LBR logs any address in IP, even if IP just faulted.
+ * This means user space can control any address. Since
+ * it's dangerous to reference a user controlled kernel
+ * address we don't do any software filtering for addresses that
+ * look like kernel.
+ *
+ * On modern Intel systems (Sandy Bridge+) this implies that
+ * exceptions and interrupts in kernel space may be reported like
+ * calls.
+ */
+ return X86_BR_NONE;
+ }

/*
* decoder needs to know the ABI especially
* on 64-bit systems running 32-bit apps
*/
#ifdef CONFIG_X86_64
- is64 = kernel_ip((unsigned long)addr) || !test_thread_flag(TIF_IA32);
+ is64 = !test_thread_flag(TIF_IA32);
#endif
insn_init(&insn, addr, is64);
insn_get_opcode(&insn);
--
1.7.7.6

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/