perf core: Use KSTK_ESP() instead of pt_regs->sp while output user regs
Subsystems (5)
PATCH 1/1 perf core: Use KSTK_ESP() instead of pt_regs->sp while output user regs
From: Chenggang Qin <[email protected]>
For x86_64, the exact value of user stack's esp should be got by KSTK_ESP(current). current->thread.usersp is copied from PDA while enter ring0.
Now, we output the value of sp from pt_regs. But pt_regs->sp has changed before it was pushed into kernel stack.
So, we cannot get the correct callchain while unwind some user stacks. For example, if the stack contains __lll_unlock_wake()/__lll_lock_wait(), the callchain will break some times with the latest version of libunwind. The root cause is the sp that is used by libunwind may be wrong.
If we use KSTK_ESP(current), the correct callchain can be got everytime. Other architectures also have KSTK_ESP() macro.
--- arch/x86/kernel/perf_regs.c | 3 +++ 1 file changed, 3 insertions(+)
arch/x86/kernel/perf_regs.c +3 -0
@@ -60,6 +60,9 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)if (WARN_ON_ONCE (idx >=ARRAY_SIZE (pt_regs_offset)))return 0 ; +if (idx ==PERF_REG_X86_SP ) +return KSTK_ESP (current); +return regs_get_register (regs, pt_regs_offset[idx]); }
Signature · 1 line
1.9.1
Quoted 36 lines · root
From: Chenggang Qin <[email protected]>
For
x86_64, the exact value of user stack's esp should be got byKSTK_ESP(current).current->thread.userspis copied from PDA while enter ring0.
Now, we output the value of sp frompt_regs. Butpt_regs->sphas changed before it was pushed into kernel stack.So, we cannot get the correct callchain while unwind some user stacks. For example, if the stack contains __lll_unlock_wake()/__lll_lock_wait(), the callchain will break some times with the latest version of libunwind. The root cause is the sp that is used by libunwind may be wrong.
If we use
KSTK_ESP(current), the correct callchain can be got everytime. Other architectures also haveKSTK_ESP()macro.Signed-off-by Chenggang Qin <[email protected]>Cc Andrew Morton <[email protected]>Cc Arjan van de Ven <[email protected]>Cc David Ahern <[email protected]>Cc Ingo Molnar <[email protected]>Cc Mike Galbraith <[email protected]>Cc Namhyung Kim <[email protected]>Cc Paul Mackerras <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Wu Fengguang <[email protected]>Cc Yanmin Zhang <[email protected]>--- arch/x86/kernel/perf_regs.c | 3 +++ 1 file changed, 3 insertions(+)arch/x86/kernel/perf_regs.c +0
-0
arch/x86/kernel/perf_regs.c u64 perf_reg_value(struct pt_regs *regs, int idx) · line 65
Quoted 6 lines
arch/x86/kernel/perf_regs.c +3
-0@@ -60,6 +60,9 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)if (WARN_ON_ONCE (idx >=ARRAY_SIZE (pt_regs_offset)))return 0 ; +if (idx ==PERF_REG_X86_SP ) +return KSTK_ESP (current); +
This patch is probably fine, but KSTK_ESP seems to be bogus:
unsigned long KSTK_ESP(struct task_struct *task)
{
return (test_tsk_thread_flag(task, TIF_IA32)) ?
(task_pt_regs(task)->sp) : ((task)->thread.usersp);
}I swear that every time I've looked at anything that references TIF_IA32 in the last two weeks, it's been wrong. This should be something like:
if (task_thread_info(task)->status & TS_COMPAT)
return task_pt_regs(task)->sp; else if (task == current && task is in a syscall) return current_user_stack_pointer(); else if (task is not running && task is in a syscall) return task->thread.usersp; else if (task is not in a syscall) return task_pt_regs(task)->sp;
else
we're confused; give up.
What context are you using KSTK_ESP in?
--Andy
Quoted 4 lines
arch/x86/kernel/perf_regs.c +0
-0return regs_get_register (regs, pt_regs_offset[idx]); }
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 4 lines · 秦承刚(承刚)
The context is NMI (PMU) or IRQ (hrtimer). It is a bit complex. The process we want to sample is the current, so it is always running.
We need to distinguish between IRQ context, syscall or user context that are interrupted by NMI.
Oh. So you really are trying to get the user regs from NMI context even if you interrupted the kernel. This will be an unpleasant thing to do correctly.
Quoted 1 line
Syscall: sp =
current->thread.usersp;
Nope. For x86_64 at least, sp is in old_rsp, not usersp, *if* the syscall you interrupted was syscall64 instead of one of the ia32 entries, which are differently strange. If you've context switched out and back, then usersp will match it. If not, then you're looking at some random stale sp value.
Keep in mind that there is absolutely no guarantee that TIF_IA32 matches the syscall entry type.
Quoted 5 lines
old_rsp always point to current->thread.usersp. May be weshouldn't use
current_user_stack_pointer();
User: sp = task_pt_regs(task)->sp;
current'spt_regsare stored in kernel stack while NMI or IRQ
occured.
This is the only easy case.
Quoted 3 lines
IRQ: sp = task_pt_regs(task)->sp;
current'spt_regsare stored in kernel stack while IRQ which was
interrupted occured.
Sort of. It's true by the time you actually execute the IRQ handler.
I think that trying to do this is doomed to either failure or extreme complexity. You're in an NMI, so you could be part-way through a context switch or you could be in the very first instruction of the syscall handler.
On a quick look, there are plenty of other bugs in there besides just the stack pointer issue. The ABI check that uses TIF_IA32 in the perf core is completely wrong. TIF_IA32 may be equal to the actual userspace bitness by luck, but, if so, that's more or less just luck. And there's a user_mode test that should be user_mode_vm.
Also, it's not just sp that's wrong. There are various places that you can interrupt in which many of the registers have confusing locations. You could try using the cfi unwind data, but that's unlikely to work for regs like cs and ss, and, during context switch, this has very little chance of working.
What's the point of this feature? Honestly, my suggestion would be to delete it instead of trying to fix it. It's also not clear to me that there aren't serious security problems here -- it's entirely possible for sensitive *kernel* values to and up in task_pt_regs at certain times, and if you run during context switch and there's no code to suppress this dump during context switch, then you could be showing regs that belong to the wrong task.
--Andy
Quoted 95 lines
Regards
Chenggang------------------------------------------------------------------
发件人:Andy Lutomirski <[email protected]> 发送时间:2014年12月23日(星期二) 16:30
收件人:root <[email protected]>,linux-kernel <[email protected]>
抄 送:秦承刚(承刚) <[email protected]>,Andrew Morton <[email protected]>,Arjan van de Ven <[email protected]>,David Ahern <[email protected]>,Ingo Molnar <[email protected]>,Mike Galbraith <[email protected]>,Namhyung Kim <[email protected]>,Paul Mackerras <[email protected]>,Peter Zijlstra <[email protected]>,Wu Fengguang <[email protected]>,Yanmin Zhang <[email protected]> 主 题:Re: [PATCH] perf core: UseKSTK_ESP()instead ofpt_regs->spwhile output user regsQuoted 39 lines · root
From: Chenggang Qin <[email protected]>
For
x86_64, the exact value of user stack's esp should be got byKSTK_ESP(current).current->thread.userspis copied from PDA while enter ring0. Now, we output the value of sp frompt_regs. Butpt_regs->sphas changed before
it was pushed into kernel stack.So, we cannot get the correct callchain while unwind some user stacks. For example, if the stack contains __lll_unlock_wake()/__lll_lock_wait(), the
callchain will break some times with the latest version of libunwind. The root cause is the sp that is used by libunwind may be wrong.If we use
KSTK_ESP(current), the correct callchain can be got everytime. Other architectures also haveKSTK_ESP()macro.Signed-off-by Chenggang Qin <[email protected]>Cc Andrew Morton <[email protected]>Cc Arjan van de Ven <[email protected]>Cc David Ahern <[email protected]>Cc Ingo Molnar <[email protected]>Cc Mike Galbraith <[email protected]>Cc Namhyung Kim <[email protected]>Cc Paul Mackerras <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Wu Fengguang <[email protected]>Cc Yanmin Zhang <[email protected]>---
arch/x86/kernel/perf_regs.c| 3 +++1 file changed, 3 insertions(+)arch/x86/kernel/perf_regs.c +0
-0arch/x86/kernel/perf_regs.c u64 perf_reg_value(struct pt_regs *regs, int idx) · line 65
Quoted 6 lines
arch/x86/kernel/perf_regs.c +3
-0@@ -60,6 +60,9 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)if (WARN_ON_ONCE (idx >=ARRAY_SIZE (pt_regs_offset)))return 0 ; +if (idx ==PERF_REG_X86_SP ) +return KSTK_ESP (current); +This patch is probably fine, but
KSTK_ESPseems to be bogus:unsigned long
KSTK_ESP(struct task_struct *task){
return (test_tsk_thread_flag(task, TIF_IA32)) ? (task_pt_regs(task)->sp) : ((task)->thread.usersp); }I swear that every time I've looked at anything that references
TIF_IA32in the last two weeks, it's been wrong. This should be something like:if (task_thread_info(task)->status &
TS_COMPAT) return task_pt_regs(task)->sp;
else if (task == current && task is in a syscall) returncurrent_user_stack_pointer();
else if (task is not running && task is in a syscall) returntask->thread.usersp;
else if (task is not in a syscall)
return task_pt_regs(task)->sp;
else
we're confused; give up.What context are you using
KSTK_ESPin?--Andy
Quoted 4 lines
arch/x86/kernel/perf_regs.c +0
-0return regs_get_register (regs, pt_regs_offset[idx]); }
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
Quoted 46 lines · Andy Lutomirski
Quoted 4 lines · 秦承刚(承刚)
The context is NMI (PMU) or IRQ (hrtimer). It is a bit complex. The process we want to sample is the current, so it is always running.
We need to distinguish between IRQ context, syscall or user context that are interrupted by NMI.Oh. So you really are trying to get the user regs from NMI context even if you interrupted the kernel. This will be an unpleasant thing to do correctly.
Quoted 1 line
Syscall: sp =
current->thread.usersp;Nope. For
x86_64at least, sp is inold_rsp, not usersp, *if* the syscall you interrupted was syscall64 instead of one of the ia32 entries, which are differently strange. If you've context switched out and back, then usersp will match it. If not, then you're looking at some random stale sp value.Keep in mind that there is absolutely no guarantee that
TIF_IA32matches the syscall entry type.Quoted 5 lines
old_rsp always point to current->thread.usersp. May be weshouldn't use
current_user_stack_pointer();
User: sp = task_pt_regs(task)->sp;
current'spt_regsare stored in kernel stack while NMI or IRQ
occured.This is the only easy case.
Quoted 3 lines
IRQ: sp = task_pt_regs(task)->sp;
current'spt_regsare stored in kernel stack while IRQ which was
interrupted occured.Sort of. It's true by the time you actually execute the IRQ handler.
I think that trying to do this is doomed to either failure or extreme complexity. You're in an NMI, so you could be part-way through a context switch or you could be in the very first instruction of the syscall handler.
On a quick look, there are plenty of other bugs in there besides just the stack pointer issue. The ABI check that uses
TIF_IA32in the perf core is completely wrong.TIF_IA32may be equal to the actual userspace bitness by luck, but, if so, that's more or less just luck. And there's auser_modetest that should beuser_mode_vm.
Quoted 5 lines
Also, it's not just sp that's wrong. There are various places that you can interrupt in which many of the registers have confusing locations. You could try using the cfi unwind data, but that's unlikely to work for regs like cs and ss, and, during context switch, this has very little chance of working.
Even the unwinder won't be able to get rbx, rbp, r12, r13, r14, and r15 right -- good luck handling FORK_LIKE, PTREGSCALL, etc.
--Andy
Quoted 112 lines
What's the point of this feature? Honestly, my suggestion would be to delete it instead of trying to fix it. It's also not clear to me that there aren't serious security problems here -- it's entirely possible for sensitive *kernel* values to and up in
task_pt_regsat certain times, and if you run during context switch and there's no code to suppress this dump during context switch, then you could be showing regs that belong to the wrong task.--Andy
Quoted 87 lines
Regards
Chenggang------------------------------------------------------------------
发件人:Andy Lutomirski <[email protected]> 发送时间:2014年12月23日(星期二) 16:30
收件人:root <[email protected]>,linux-kernel <[email protected]>
抄 送:秦承刚(承刚) <[email protected]>,Andrew Morton <[email protected]>,Arjan van de Ven <[email protected]>,David Ahern <[email protected]>,Ingo Molnar <[email protected]>,Mike Galbraith <[email protected]>,Namhyung Kim <[email protected]>,Paul Mackerras <[email protected]>,Peter Zijlstra <[email protected]>,Wu Fengguang <[email protected]>,Yanmin Zhang <[email protected]> 主 题:Re: [PATCH] perf core: UseKSTK_ESP()instead ofpt_regs->spwhile output user regsQuoted 39 lines · root
From: Chenggang Qin <[email protected]>
For
x86_64, the exact value of user stack's esp should be got byKSTK_ESP(current).current->thread.userspis copied from PDA while enter ring0. Now, we output the value of sp frompt_regs. Butpt_regs->sphas changed before
it was pushed into kernel stack.So, we cannot get the correct callchain while unwind some user stacks. For example, if the stack contains __lll_unlock_wake()/__lll_lock_wait(), the
callchain will break some times with the latest version of libunwind. The root cause is the sp that is used by libunwind may be wrong.If we use
KSTK_ESP(current), the correct callchain can be got everytime. Other architectures also haveKSTK_ESP()macro.Signed-off-by Chenggang Qin <[email protected]>Cc Andrew Morton <[email protected]>Cc Arjan van de Ven <[email protected]>Cc David Ahern <[email protected]>Cc Ingo Molnar <[email protected]>Cc Mike Galbraith <[email protected]>Cc Namhyung Kim <[email protected]>Cc Paul Mackerras <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Wu Fengguang <[email protected]>Cc Yanmin Zhang <[email protected]>---
arch/x86/kernel/perf_regs.c| 3 +++1 file changed, 3 insertions(+)arch/x86/kernel/perf_regs.c +0
-0arch/x86/kernel/perf_regs.c u64 perf_reg_value(struct pt_regs *regs, int idx) · line 65
Quoted 6 lines
arch/x86/kernel/perf_regs.c +3
-0@@ -60,6 +60,9 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)if (WARN_ON_ONCE (idx >=ARRAY_SIZE (pt_regs_offset)))return 0 ; +if (idx ==PERF_REG_X86_SP ) +return KSTK_ESP (current); +This patch is probably fine, but
KSTK_ESPseems to be bogus:unsigned long
KSTK_ESP(struct task_struct *task){
return (test_tsk_thread_flag(task, TIF_IA32)) ? (task_pt_regs(task)->sp) : ((task)->thread.usersp); }I swear that every time I've looked at anything that references
TIF_IA32in the last two weeks, it's been wrong. This should be something like:if (task_thread_info(task)->status &
TS_COMPAT) return task_pt_regs(task)->sp;
else if (task == current && task is in a syscall) returncurrent_user_stack_pointer();
else if (task is not running && task is in a syscall) returntask->thread.usersp;
else if (task is not in a syscall)
return task_pt_regs(task)->sp;
else
we're confused; give up.Quoted 8 lines
What context are you using
KSTK_ESPin?--Andy
Quoted 4 lines
arch/x86/kernel/perf_regs.c +0
-0return regs_get_register (regs, pt_regs_offset[idx]); }--
Andy Lutomirski
AMA Capital Management, LLC
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
Peter Zijlstra
maintainerQuoted 12 lines · Andy Lutomirski
On a quick look, there are plenty of other bugs in there besides just the stack pointer issue. The ABI check that uses
TIF_IA32in the perf core is completely wrong.TIF_IA32may be equal to the actual userspace bitness by luck, but, if so, that's more or less just luck. And there's auser_modetest that should beuser_mode_vm.Also, it's not just sp that's wrong. There are various places that you can interrupt in which many of the registers have confusing locations. You could try using the cfi unwind data, but that's unlikely to work for regs like cs and ss, and, during context switch, this has very little chance of working.
Quoted 7 lines
What's the point of this feature? Honestly, my suggestion would be to delete it instead of trying to fix it. It's also not clear to me that there aren't serious security problems here -- it's entirely possible for sensitive *kernel* values to and up in
task_pt_regsat certain times, and if you run during context switch and there's no code to suppress this dump during context switch, then you could be showing regs that belong to the wrong task.
Of course the people who actually wrote the code are not on CC :/
There's two users of this iirc;
1) the dwarf stack unwinder thingy, which basically dumps the userspace regs and the top of userspace stack on 'event'.
2) the recent sample_regs_intr, which dumps the register set at 'event', be it kernel or userspace.
The first is somewhat usable when lacking framepointers while still desiring some unwind information, the second is useful to things like call argument profiling and the like.
Quoted 22 lines · Peter Zijlstra
Quoted 12 lines · Andy Lutomirski
On a quick look, there are plenty of other bugs in there besides just the stack pointer issue. The ABI check that uses
TIF_IA32in the perf core is completely wrong.TIF_IA32may be equal to the actual userspace bitness by luck, but, if so, that's more or less just luck. And there's auser_modetest that should beuser_mode_vm.Also, it's not just sp that's wrong. There are various places that you can interrupt in which many of the registers have confusing locations. You could try using the cfi unwind data, but that's unlikely to work for regs like cs and ss, and, during context switch, this has very little chance of working.
Quoted 7 lines
What's the point of this feature? Honestly, my suggestion would be to delete it instead of trying to fix it. It's also not clear to me that there aren't serious security problems here -- it's entirely possible for sensitive *kernel* values to and up in
task_pt_regsat certain times, and if you run during context switch and there's no code to suppress this dump during context switch, then you could be showing regs that belong to the wrong task.
Quoted 7 lines
Of course the people who actually wrote the code are not on CC :/
There's two users of this iirc;
1) the dwarf stack unwinder thingy, which basically dumps the userspace regs and the top of userspace stack on 'event'.
Given how the x86_64* entry code works, using task_pt_regs from anywhere except explicitly supported contexts (including exceptions that originated in userspace and a small handful of system calls) is asking for trouble. NMI context is especially bad.
How important is this feature, and which registers matter? It might be possible to use a dwarf unwinder on the kernel call stack to get most of the regs from most contexts, and it might also be possible to make small changes to the entry code to make it possible to get some of the registers reliably, but it's not currently possible to safely use task_pt_regs *at all* from NMI context unless you've at least blacklisted a handful of origin RIP values that give dangerously bogus results. (Using do_nmi's regs parameter if user_mode_vm(regs) is a different story.)
* I'm not nearly as familiar with the 32-bit entry code, so I don't know whether we have the same issues there.
Quoted 3 lines
2) the recent
sample_regs_intr, which dumps the register set at 'event', be it kernel or userspace.
What's wrong with the PMI's pt_regs for that? If we interrupted the kernel, they'll be kernel regs (with all their attendant security issues) and, if we interrupted userspace, then they'll be the full, correct userspace registers.
--Andy
Quoted 4 lines
The first is somewhat usable when lacking framepointers while still desiring some unwind information, the second is useful to things like call argument profiling and the like.
Quoted 36 lines · Andy Lutomirski
Quoted 22 lines · Peter Zijlstra
Quoted 12 lines · Andy Lutomirski
On a quick look, there are plenty of other bugs in there besides just the stack pointer issue. The ABI check that uses
TIF_IA32in the perf core is completely wrong.TIF_IA32may be equal to the actual userspace bitness by luck, but, if so, that's more or less just luck. And there's auser_modetest that should beuser_mode_vm.Also, it's not just sp that's wrong. There are various places that you can interrupt in which many of the registers have confusing locations. You could try using the cfi unwind data, but that's unlikely to work for regs like cs and ss, and, during context switch, this has very little chance of working.
Quoted 7 lines
What's the point of this feature? Honestly, my suggestion would be to delete it instead of trying to fix it. It's also not clear to me that there aren't serious security problems here -- it's entirely possible for sensitive *kernel* values to and up in
task_pt_regsat certain times, and if you run during context switch and there's no code to suppress this dump during context switch, then you could be showing regs that belong to the wrong task.Quoted 7 lines
Of course the people who actually wrote the code are not on CC :/
There's two users of this iirc;
1) the dwarf stack unwinder thingy, which basically dumps the userspace regs and the top of userspace stack on 'event'.
Given how the x86_64* entry code works, using
task_pt_regsfrom anywhere except explicitly supported contexts (including exceptions that originated in userspace and a small handful of system calls) is asking for trouble. NMI context is especially bad.
Quoted 9 lines
How important is this feature, and which registers matter? It might be possible to use a dwarf unwinder on the kernel call stack to get most of the regs from most contexts, and it might also be possible to make small changes to the entry code to make it possible to get some of the registers reliably, but it's not currently possible to safely use
task_pt_regs*at all* from NMI context unless you've at least blacklisted a handful of origin RIP values that give dangerously bogus results. (Using do_nmi's regs parameter ifuser_mode_vm(regs)is a different story.)
It's actually worse than just knowing the interrupted kernel RIP. If the call chain goes usermode -> IST exception -> NMI, then task_pt_regs is entirely uninitialized. Assuming all the CFI annotations are correct, the unwinder could still do it from the kernel.
Note that, as far as I know, Jan Beulich is the only person who uses the unwinder on kernel code. Jan, how do you do this?
Quoted 19 lines
* I'm not nearly as familiar with the 32-bit entry code, so I don't know whether we have the same issues there.
Quoted 3 lines
2) the recent
sample_regs_intr, which dumps the register set at 'event', be it kernel or userspace.What's wrong with the PMI's
pt_regsfor that? If we interrupted the kernel, they'll be kernel regs (with all their attendant security issues) and, if we interrupted userspace, then they'll be the full, correct userspace registers.--Andy
Quoted 4 lines
The first is somewhat usable when lacking framepointers while still desiring some unwind information, the second is useful to things like call argument profiling and the like.
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
Quoted 17 lines
Quoted 1 line
Quoted 1 line
Andy Lutomirski <[email protected]> 12/31/14 3:00 AM >>>
Quoted 5 lines · Andy Lutomirski
Given how the x86_64* entry code works, using
task_pt_regsfrom anywhere except explicitly supported contexts (including exceptions that originated in userspace and a small handful of system calls) is asking for trouble. NMI context is especially bad.Quoted 9 lines
How important is this feature, and which registers matter? It might be possible to use a dwarf unwinder on the kernel call stack to get most of the regs from most contexts, and it might also be possible to make small changes to the entry code to make it possible to get some of the registers reliably, but it's not currently possible to safely use
task_pt_regs*at all* from NMI context unless you've at least blacklisted a handful of origin RIP values that give dangerously bogus results. (Using do_nmi's regs parameter ifuser_mode_vm(regs)is a different story.)
Quoted 8 lines
It's actually worse than just knowing the interrupted kernel RIP. If the call chain goes usermode -> IST exception -> NMI, then
task_pt_regsis entirely uninitialized. Assuming all the CFI annotations are correct, the unwinder could still do it from the kernel.Note that, as far as I know, Jan Beulich is the only person who uses the unwinder on kernel code. Jan, how do you do this?
Trying to guess what you mean by "this": A stack switch gets expressed by CFI annotations just like any other frame pointer adjustments. See for example the CFI_DEF_CFA_REGISTER use in the SAVE_ARGS_IRQ macro.
If that wasn't your question, please be more precise.
Jan
Quoted 24 lines · Jan Beulich
Quoted 24 lines
Quoted 2 lines
Quoted 2 lines
Andy Lutomirski <[email protected]> 12/31/14 3:00 AM >>>
Quoted 5 lines · Andy Lutomirski
Given how the x86_64* entry code works, using
task_pt_regsfrom anywhere except explicitly supported contexts (including exceptions that originated in userspace and a small handful of system calls) is asking for trouble. NMI context is especially bad.Quoted 9 lines
How important is this feature, and which registers matter? It might be possible to use a dwarf unwinder on the kernel call stack to get most of the regs from most contexts, and it might also be possible to make small changes to the entry code to make it possible to get some of the registers reliably, but it's not currently possible to safely use
task_pt_regs*at all* from NMI context unless you've at least blacklisted a handful of origin RIP values that give dangerously bogus results. (Using do_nmi's regs parameter ifuser_mode_vm(regs)is a different story.)It's actually worse than just knowing the interrupted kernel RIP. If the call chain goes usermode -> IST exception -> NMI, then
task_pt_regsis entirely uninitialized. Assuming all the CFI annotations are correct, the unwinder could still do it from the kernel.
Quoted 8 lines
Quoted 2 lines
Note that, as far as I know, Jan Beulich is the only person who uses the unwinder on kernel code. Jan, how do you do this?
Trying to guess what you mean by "this": A stack switch gets expressed by CFI annotations just like any other frame pointer adjustments. See for example the
CFI_DEF_CFA_REGISTERuse in theSAVE_ARGS_IRQmacro.If that wasn't your question, please be more precise.
Sorry, my question was vague.
Is there any way to consume these annotations at runtime in the kernel? The goal would be for perf's NMI handler to consume the CFI data to figure out the userspace registers. I'm guessing that the answer might be no, because we seem to be compiling with -fno-asynchronous-unwind-tables and we don't seem to be putting any .eh_frame stuff into the final kernel image.
I had thought that someone implemented runtime DWARF unwinding, though.
--Andy
Quoted 3 lines
Jan
Quoted 10 lines
Quoted 1 line
Quoted 1 line
Andy Lutomirski <[email protected]> 01/02/15 7:03 PM >>>
Quoted 5 lines · Jan Beulich
Trying to guess what you mean by "this": A stack switch gets expressed by CFI annotations just like any other frame pointer adjustments. See for example the
CFI_DEF_CFA_REGISTERuse in theSAVE_ARGS_IRQmacro.If that wasn't your question, please be more precise.
Sorry, my question was vague.
Quoted 8 lines
Is there any way to consume these annotations at runtime in the kernel? The goal would be for perf's NMI handler to consume the CFI data to figure out the userspace registers. I'm guessing that the answer might be no, because we seem to be compiling with -fno-asynchronous-unwind-tables and we don't seem to be putting any .eh_frame stuff into the final kernel image.
I had thought that someone implemented runtime DWARF unwinding, though.
Yes, we do have such code in our kernels, but Linus continues to veto it going into his tree.
Jan
Quoted 21 lines · Peter Zijlstra
Quoted 12 lines · Andy Lutomirski
On a quick look, there are plenty of other bugs in there besides just the stack pointer issue. The ABI check that uses
TIF_IA32in the perf core is completely wrong.TIF_IA32may be equal to the actual userspace bitness by luck, but, if so, that's more or less just luck. And there's auser_modetest that should beuser_mode_vm.Also, it's not just sp that's wrong. There are various places that you can interrupt in which many of the registers have confusing locations. You could try using the cfi unwind data, but that's unlikely to work for regs like cs and ss, and, during context switch, this has very little chance of working.
Quoted 7 lines
What's the point of this feature? Honestly, my suggestion would be to delete it instead of trying to fix it. It's also not clear to me that there aren't serious security problems here -- it's entirely possible for sensitive *kernel* values to and up in
task_pt_regsat certain times, and if you run during context switch and there's no code to suppress this dump during context switch, then you could be showing regs that belong to the wrong task.
Quoted 6 lines
Of course the people who actually wrote the code are not on CC :/
There's two users of this iirc;
1) the dwarf stack unwinder thingy, which basically dumps the userspace regs and the top of userspace stack on 'event'.
looks like this solves the issue I was trying to fix long time ago:
http://marc.info/?l=linux-kernel&m=134934717011451&w=2
this seems a lot simpler ;-) I'll test..
jirka
Quoted 27 lines · Jiri Olsa
Quoted 25 lines · Peter Zijlstra
Quoted 12 lines · Andy Lutomirski
On a quick look, there are plenty of other bugs in there besides just the stack pointer issue. The ABI check that uses
TIF_IA32in the perf core is completely wrong.TIF_IA32may be equal to the actual userspace bitness by luck, but, if so, that's more or less just luck. And there's auser_modetest that should beuser_mode_vm.Also, it's not just sp that's wrong. There are various places that you can interrupt in which many of the registers have confusing locations. You could try using the cfi unwind data, but that's unlikely to work for regs like cs and ss, and, during context switch, this has very little chance of working.
Quoted 7 lines
What's the point of this feature? Honestly, my suggestion would be to delete it instead of trying to fix it. It's also not clear to me that there aren't serious security problems here -- it's entirely possible for sensitive *kernel* values to and up in
task_pt_regsat certain times, and if you run during context switch and there's no code to suppress this dump during context switch, then you could be showing regs that belong to the wrong task.Of course the people who actually wrote the code are not on CC :/
There's two users of this iirc;
Quoted 8 lines
Quoted 2 lines
1) the dwarf stack unwinder thingy, which basically dumps the userspace regs and the top of userspace stack on 'event'.
looks like this solves the issue I was trying to fix long time ago:
http://marc.info/?l=linux-kernel&m=134934717011451&w=2this seems a lot simpler ;-) I'll test..
I suspect that, if it works, then it's pure luck. (task)->thread.usersp in KSTK_ESP is bogus -- your code was more accurate.
I think we should seriously consider making use of this feature by non-root users require an explicit sysctl. Sending values to user code that are, at best, free of sensitive kernel data most of the time is IMO inappropriate for an unprivileged API.
I'm currently working on a patch to try to clean this up better.
--Andy
Quoted 2 lines
jirka
Quoted 41 lines · Andy Lutomirski
Quoted 27 lines · Jiri Olsa
Quoted 25 lines · Peter Zijlstra
Quoted 12 lines · Andy Lutomirski
On a quick look, there are plenty of other bugs in there besides just the stack pointer issue. The ABI check that uses
TIF_IA32in the perf core is completely wrong.TIF_IA32may be equal to the actual userspace bitness by luck, but, if so, that's more or less just luck. And there's auser_modetest that should beuser_mode_vm.Also, it's not just sp that's wrong. There are various places that you can interrupt in which many of the registers have confusing locations. You could try using the cfi unwind data, but that's unlikely to work for regs like cs and ss, and, during context switch, this has very little chance of working.
Quoted 7 lines
What's the point of this feature? Honestly, my suggestion would be to delete it instead of trying to fix it. It's also not clear to me that there aren't serious security problems here -- it's entirely possible for sensitive *kernel* values to and up in
task_pt_regsat certain times, and if you run during context switch and there's no code to suppress this dump during context switch, then you could be showing regs that belong to the wrong task.Of course the people who actually wrote the code are not on CC :/
There's two users of this iirc;
Quoted 8 lines
Quoted 2 lines
1) the dwarf stack unwinder thingy, which basically dumps the userspace regs and the top of userspace stack on 'event'.
looks like this solves the issue I was trying to fix long time ago:
http://marc.info/?l=linux-kernel&m=134934717011451&w=2this seems a lot simpler ;-) I'll test..
I suspect that, if it works, then it's pure luck. (task)->thread.usersp in
KSTK_ESPis bogus -- your code was more accurate.
Quoted 6 lines
I think we should seriously consider making use of this feature by non-root users require an explicit sysctl. Sending values to user code that are, at best, free of sensitive kernel data most of the time is IMO inappropriate for an unprivileged API.
I'm currently working on a patch to try to clean this up better.
ook.. well FWIW I tested and if I used KSTK_ESP as a perf user stack pointer (attached patch) I've got all callchains resolved properly, as when I tested my original patch
NOTE the patch from Chenggang Qin isnt enough to fix my issue for perf
dwarf unwind, I needed to use attached change
I'd be happy to test any change you make for this, because this has been pain for a long time :-\
thanks,
jirka
---
arch/x86/kernel/perf_regs.c +5 -0
@@ -66,6 +66,11 @@ u64 perf_reg_value(struct pt_regs *regs, int idx)return regs_get_register (regs, pt_regs_offset[idx]); } +unsigned long arch_perf_user_stack_pointer (struct pt_regs *regs) +{ +return KSTK_ESP (current); +} +#define REG_RESERVED (~((1ULL << PERF_REG_X86_MAX) - 1ULL))#ifdef CONFIG_X86_32
kernel/events/core.c +13 -0
@@ -4486,6 +4486,19 @@ static void perf_sample_regs_intr(struct perf_regs *regs_intr, regs_intr->abi =perf_reg_abi (current); } +#ifdef CONFIG_HAVE_PERF_USER_STACK_DUMP +unsigned long __weak arch_perf_user_stack_pointer (struct pt_regs *regs) +{ +return user_stack_pointer (regs); +} + +static unsigned long perf_user_stack_pointer (struct pt_regs *regs) +{ +return arch_perf_user_stack_pointer (regs); +} +#else +#define perf_user_stack_pointer (regs) 0 +#endif /* CONFIG_HAVE_PERF_USER_STACK_DUMP */ /* * Get remaining task size from user stack pointer.
kernel/events/internal.h +2 -4
@@ -180,19 +180,17 @@ static inline void put_recursion_context(int *recursion, int rctx) }#ifdef CONFIG_HAVE_PERF_USER_STACK_DUMP +unsigned long arch_perf_user_stack_pointer (struct pt_regs *regs); +static inline bool arch_perf_have_user_stack_dump (void ) {return true; } - -#define perf_user_stack_pointer (regs) user_stack_pointer(regs)#else static inline bool arch_perf_have_user_stack_dump (void ) {return false; } - -#define perf_user_stack_pointer (regs) 0#endif /* CONFIG_HAVE_PERF_USER_STACK_DUMP */ #endif /* _KERNEL_EVENTS_INTERNAL_H */
cover perf: Improve user regs sampling
This seems to improve the situation on x86_64 from almost entirely broken to mostly working. Tested with:
perf record -e cycles --call-graph=dwarf ls
Andy Lutomirski (2):
perf: Move task_pt_regs sampling into arch code
x86_64, perf: Improve user regs sampling
arch/arm/kernel/perf_regs.c | 8 ++++ arch/arm64/kernel/perf_regs.c | 8 ++++ arch/x86/kernel/perf_regs.c | 90 +++++++++++++++++++++++++++++++++++++++++++ include/linux/perf_event.h | 12 +++--- include/linux/perf_regs.h | 16 ++++++++ kernel/events/core.c | 19 ++++----- 6 files changed, 137 insertions(+), 16 deletions(-)
Signature · 1 line
2.1.0
PATCH 1/2 perf: Move task_pt_regs sampling into arch code
On x86_64, at least, task_pt_regs may be only partially initialized in many contexts, so x86_64 should not use it without extra care from interrupt context, let alone NMI context.
This will allow x86_64 to override the logic and will supply some scratch space to use to make a cleaner copy of user regs.
--- arch/arm/kernel/perf_regs.c | 8 ++++++++ arch/arm64/kernel/perf_regs.c | 8 ++++++++ arch/x86/kernel/perf_regs.c | 16 ++++++++++++++++ include/linux/perf_event.h | 12 +++++++----- include/linux/perf_regs.h | 16 ++++++++++++++++ kernel/events/core.c | 19 ++++++++----------- 6 files changed, 63 insertions(+), 16 deletions(-)
arch/arm/kernel/perf_regs.c +8 -0
@@ -28,3 +28,11 @@ u64 perf_reg_abi(struct task_struct *task) {return PERF_SAMPLE_REGS_ABI_32 ; } + +void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}
arch/arm64/kernel/perf_regs.c +8 -0
@@ -50,3 +50,11 @@ u64 perf_reg_abi(struct task_struct *task)else return PERF_SAMPLE_REGS_ABI_64 ; } + +void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}
arch/x86/kernel/perf_regs.c +16 -0
@@ -78,6 +78,14 @@ u64 perf_reg_abi(struct task_struct *task) {return PERF_SAMPLE_REGS_ABI_32 ; } + +void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}#else /* CONFIG_X86_64 */ #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ (1ULL << PERF_REG_X86_ES) | \
@@ -102,4 +110,12 @@ u64 perf_reg_abi(struct task_struct *task) else return PERF_SAMPLE_REGS_ABI_64; } + +void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}#endif /* CONFIG_X86_32 */
include/linux/perf_event.h +7 -5
@@ -79,11 +79,6 @@ struct perf_branch_stack {struct perf_branch_entry entries[0 ]; }; -struct perf_regs { -__u64 abi; -struct pt_regs *regs; -}; -struct task_struct ;/*
@@ -610,7 +605,14 @@ struct perf_sample_data {u32 reserved; } cpu_entry;struct perf_callchain_entry *callchain; + +/* +* regs_user may point to task_pt_regs or to regs_user_copy, depending +* on arch details. +*/ struct perf_regs regs_user; +struct pt_regs regs_user_copy; +struct perf_regs regs_intr;u64 stack_user_size; }____cacheline_aligned ;
include/linux/perf_regs.h +16 -0
@@ -1,11 +1,19 @@ #ifndef _LINUX_PERF_REGS_H #define _LINUX_PERF_REGS_H +struct perf_regs { + __u64 abi; + struct pt_regs *regs; +}; + #ifdef CONFIG_HAVE_PERF_REGS #include <asm/perf_regs.h> u64 perf_reg_value(struct pt_regs *regs, int idx); int perf_reg_validate(u64 mask); u64 perf_reg_abi(struct task_struct *task); +void perf_get_regs_user(struct perf_regs *regs_user, + struct pt_regs *regs, + struct pt_regs *regs_user_copy); #else static inline u64 perf_reg_value(struct pt_regs *regs, int idx) {
@@ -21,5 +29,13 @@ static inline u64 perf_reg_abi(struct task_struct *task) {return PERF_SAMPLE_REGS_ABI_NONE ; } + +static inline void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}#endif /* CONFIG_HAVE_PERF_REGS */ #endif /* _LINUX_PERF_REGS_H */
kernel/events/core.c +8 -11
@@ -4461,18 +4461,14 @@ perf_output_sample_regs(struct perf_output_handle *handle, }static void perf_sample_regs_user (struct perf_regs *regs_user, -struct pt_regs *regs) +struct pt_regs *regs, +struct pt_regs *regs_user_copy) { -if (!user_mode (regs)) { -if (current->mm) - regs =task_pt_regs (current); -else - regs =NULL ; - } - -if (regs) { - regs_user->abi =perf_reg_abi (current); +if (user_mode (regs)) { + regs_user->abi =perf_reg_abi (current); regs_user->regs = regs; + }else if (current->mm) { +perf_get_regs_user (regs_user, regs, regs_user_copy); }else { regs_user->abi =PERF_SAMPLE_REGS_ABI_NONE ; regs_user->regs =NULL ;
@@ -4951,7 +4947,8 @@ void perf_prepare_sample(struct perf_event_header *header, }if (sample_type & (PERF_SAMPLE_REGS_USER |PERF_SAMPLE_STACK_USER )) -perf_sample_regs_user (&data->regs_user, regs); +perf_sample_regs_user (&data->regs_user, regs, + &data->regs_user_copy);if (sample_type &PERF_SAMPLE_REGS_USER ) {/* regs dump ABI info */
Signature · 1 line
2.1.0
Peter Zijlstra
maintainerRe: [PATCH 1/2] perf: Move task_pt_regs sampling into arch code
Quoted 6 lines · Andy Lutomirski
On
x86_64, at least,task_pt_regsmay be only partially initialized in many contexts, sox86_64should not use it without extra care from interrupt context, let alone NMI context.This will allow
x86_64to override the logic and will supply some scratch space to use to make a cleaner copy of user regs.
Just wondering how bad it would be to fill out the actual pt_regs that was previously partially initialized?
Re: [PATCH 1/2] perf: Move task_pt_regs sampling into arch code
Quoted 11 lines · Peter Zijlstra
Quoted 6 lines · Andy Lutomirski
On
x86_64, at least,task_pt_regsmay be only partially initialized in many contexts, sox86_64should not use it without extra care from interrupt context, let alone NMI context.This will allow
x86_64to override the logic and will supply some scratch space to use to make a cleaner copy of user regs.Just wondering how bad it would be to fill out the actual
pt_regsthat was previously partially initialized?
Bad, for at least two reasons.
One is that we don't actually know which regs are initialized. bx, bp, r12 etc are particularly bad in this regard, due to the FORK_LIKE mechanism and similar optimizations.
The other is that the uninitialized part of task_pt_regs can be used for something else entirely. If we have a syscall instruction immediately followed by a regular interrupt, then the interrupt's hardware frame will overlap task_pt_regs. (I'm not going to claim that this design is sensible, but it is what it is. IIRC Denys Vlasenko had some patches to partially clean this up.)
It would be possible to rework the code to avoid an extra pt_regs copy, but I don't see an obvious way to do it cleanly.
--Andy
Peter Zijlstra
maintainerRe: [PATCH 1/2] perf: Move task_pt_regs sampling into arch code
Quoted 8 lines · Andy Lutomirski
Quoted 2 lines
Just wondering how bad it would be to fill out the actual
pt_regsthat was previously partially initialized?Bad, for at least two reasons.
One is that we don't actually know which regs are initialized. bx, bp, r12 etc are particularly bad in this regard, due to the
FORK_LIKEmechanism and similar optimizations.
Right, but you need to deal with that anyhow.
Quoted 6 lines
The other is that the uninitialized part of
task_pt_regscan be used for something else entirely. If we have a syscall instruction immediately followed by a regular interrupt, then the interrupt's hardware frame will overlaptask_pt_regs. (I'm not going to claim that this design is sensible, but it is what it is. IIRC Denys Vlasenko had some patches to partially clean this up.)
Ah, urgh. Yes painful that.
Quoted 2 lines
It would be possible to rework the code to avoid an extra
pt_regscopy, but I don't see an obvious way to do it cleanly.
Yeah, we'll see how this works, I was just curious on the exact need for the copy, but if as you say, the original structure might not even exist properly (even though we have a pointer to it) that's bad (TM).
Re: [PATCH 1/2] perf: Move task_pt_regs sampling into arch code
Quoted 11 lines · Peter Zijlstra
Quoted 8 lines · Andy Lutomirski
Quoted 2 lines
Just wondering how bad it would be to fill out the actual
pt_regsthat was previously partially initialized?Bad, for at least two reasons.
One is that we don't actually know which regs are initialized. bx, bp, r12 etc are particularly bad in this regard, due to the
FORK_LIKEmechanism and similar optimizations.Right, but you need to deal with that anyhow.
I cheated by assuming they're uninitialized. If we actually wrote -1 over them and they were initialized, then we'd be screwed.
Quoted 8 lines
Quoted 7 lines
The other is that the uninitialized part of
task_pt_regscan be used for something else entirely. If we have a syscall instruction immediately followed by a regular interrupt, then the interrupt's hardware frame will overlaptask_pt_regs. (I'm not going to claim that this design is sensible, but it is what it is. IIRC Denys Vlasenko had some patches to partially clean this up.)
Quoted 8 lines
Ah, urgh. Yes painful that.
Quoted 2 lines
It would be possible to rework the code to avoid an extra
pt_regscopy, but I don't see an obvious way to do it cleanly.Yeah, we'll see how this works, I was just curious on the exact need for the copy, but if as you say, the original structure might not even exist properly (even though we have a pointer to it) that's bad (TM).
How much freedom do we have to redesign the whole mechanism? Instead of recording the user state from the PMI, we could set a flag so that we'd sample the user state before return to user mode. This would reliably get all of the registers, except in the case where the PMI was part way through the return-to-user code. In that case, we'd want to set another flag to catch the next entry to avoid writing a bogus sample.
I don't think we could do this without changing the ABI, though.
This would waste ~300 cycles if we set the flag from a fast-path syscall, at least until my magic sysret optimization goes in.
--Andy
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
tip-bot for Andy Lutomirski
merge bot[tip:perf/urgent] perf: Move task_pt_regs sampling into arch code
In reply to Andy Lutomirskiperf: Move task_pt_regs sampling into arch code
Commit-ID: 88a7c26af8dab2f2d69f5a6067eb670694ec38c0 Gitweb: http://git.kernel.org/tip/88a7c26af8dab2f2d69f5a6067eb670694ec38c0 Author: Andy Lutomirski <[email protected]> AuthorDate: Sun, 4 Jan 2015 10:36:19 -0800 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 9 Jan 2015 11:12:28 +0100
perf: Move task_pt_regs sampling into arch code
On x86_64, at least, task_pt_regs may be only partially initialized in many contexts, so x86_64 should not use it without extra care from interrupt context, let alone NMI context.
This will allow x86_64 to override the logic and will supply some scratch space to use to make a cleaner copy of user regs.
--- arch/arm/kernel/perf_regs.c | 8 ++++++++ arch/arm64/kernel/perf_regs.c | 8 ++++++++ arch/x86/kernel/perf_regs.c | 16 ++++++++++++++++ include/linux/perf_event.h | 12 +++++++----- include/linux/perf_regs.h | 16 ++++++++++++++++ kernel/events/core.c | 19 ++++++++----------- 6 files changed, 63 insertions(+), 16 deletions(-)
arch/arm/kernel/perf_regs.c +8 -0
@@ -28,3 +28,11 @@ u64 perf_reg_abi(struct task_struct *task) {return PERF_SAMPLE_REGS_ABI_32 ; } + +void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}
arch/arm64/kernel/perf_regs.c +8 -0
@@ -50,3 +50,11 @@ u64 perf_reg_abi(struct task_struct *task)else return PERF_SAMPLE_REGS_ABI_64 ; } + +void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}
arch/x86/kernel/perf_regs.c +16 -0
@@ -78,6 +78,14 @@ u64 perf_reg_abi(struct task_struct *task) {return PERF_SAMPLE_REGS_ABI_32 ; } + +void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}#else /* CONFIG_X86_64 */ #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ (1ULL << PERF_REG_X86_ES) | \
@@ -102,4 +110,12 @@ u64 perf_reg_abi(struct task_struct *task) else return PERF_SAMPLE_REGS_ABI_64; } + +void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}#endif /* CONFIG_X86_32 */
include/linux/perf_event.h +7 -5
@@ -79,11 +79,6 @@ struct perf_branch_stack {struct perf_branch_entry entries[0 ]; }; -struct perf_regs { -__u64 abi; -struct pt_regs *regs; -}; -struct task_struct ;/*
@@ -610,7 +605,14 @@ struct perf_sample_data {u32 reserved; } cpu_entry;struct perf_callchain_entry *callchain; + +/* +* regs_user may point to task_pt_regs or to regs_user_copy, depending +* on arch details. +*/ struct perf_regs regs_user; +struct pt_regs regs_user_copy; +struct perf_regs regs_intr;u64 stack_user_size; }____cacheline_aligned ;
include/linux/perf_regs.h +16 -0
@@ -1,11 +1,19 @@ #ifndef _LINUX_PERF_REGS_H #define _LINUX_PERF_REGS_H +struct perf_regs { + __u64 abi; + struct pt_regs *regs; +}; + #ifdef CONFIG_HAVE_PERF_REGS #include <asm/perf_regs.h> u64 perf_reg_value(struct pt_regs *regs, int idx); int perf_reg_validate(u64 mask); u64 perf_reg_abi(struct task_struct *task); +void perf_get_regs_user(struct perf_regs *regs_user, + struct pt_regs *regs, + struct pt_regs *regs_user_copy); #else static inline u64 perf_reg_value(struct pt_regs *regs, int idx) {
@@ -21,5 +29,13 @@ static inline u64 perf_reg_abi(struct task_struct *task) {return PERF_SAMPLE_REGS_ABI_NONE ; } + +static inline void perf_get_regs_user (struct perf_regs *regs_user, +struct pt_regs *regs, +struct pt_regs *regs_user_copy) +{ + regs_user->regs =task_pt_regs (current); + regs_user->abi =perf_reg_abi (current); +}#endif /* CONFIG_HAVE_PERF_REGS */ #endif /* _LINUX_PERF_REGS_H */
kernel/events/core.c +8 -11
@@ -4461,18 +4461,14 @@ perf_output_sample_regs(struct perf_output_handle *handle, }static void perf_sample_regs_user (struct perf_regs *regs_user, -struct pt_regs *regs) +struct pt_regs *regs, +struct pt_regs *regs_user_copy) { -if (!user_mode (regs)) { -if (current->mm) - regs =task_pt_regs (current); -else - regs =NULL ; - } - -if (regs) { - regs_user->abi =perf_reg_abi (current); +if (user_mode (regs)) { + regs_user->abi =perf_reg_abi (current); regs_user->regs = regs; + }else if (current->mm) { +perf_get_regs_user (regs_user, regs, regs_user_copy); }else { regs_user->abi =PERF_SAMPLE_REGS_ABI_NONE ; regs_user->regs =NULL ;
@@ -4951,7 +4947,8 @@ void perf_prepare_sample(struct perf_event_header *header, }if (sample_type & (PERF_SAMPLE_REGS_USER |PERF_SAMPLE_STACK_USER )) -perf_sample_regs_user (&data->regs_user, regs); +perf_sample_regs_user (&data->regs_user, regs, + &data->regs_user_copy);if (sample_type &PERF_SAMPLE_REGS_USER ) {/* regs dump ABI info */
PATCH 2/2 x86_64, perf: Improve user regs sampling
In reply to Andy LutomirskiPerf reports user regs for kernel-mode samples so that samples can be backtraced through user code. The old code was very broken in syscall context, resulting in useless backtraces.
The new code, in contrast, is still dangerously racy, but it should at least work most of the time.
--- arch/x86/kernel/perf_regs.c | 78 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-)
arch/x86/kernel/perf_regs.c +76 -2
@@ -115,7 +115,81 @@ void perf_get_regs_user(struct perf_regs *regs_user,struct pt_regs *regs, struct pt_regs *regs_user_copy) { - regs_user->regs = task_pt_regs(current); - regs_user->abi =perf_reg_abi (current); + struct pt_regs *user_regs = task_pt_regs(current); + +/* +* If we're in an NMI that interrupted task_pt_regs setup, then +* we can't sample user regs at all. This check isn't really +* sufficient, though, as we could be in an NMI inside an interrupt +* that happened during task_pt_regs setup. +*/ +if (regs->sp > (unsigned long )&user_regs->r11 && + regs->sp <= (unsigned long )(user_regs +1 )) { + regs_user->abi =PERF_SAMPLE_REGS_ABI_NONE ; + regs_user->regs =NULL ; +return ; + } + +/* +* RIP, flags, and the argument registers are usually saved. +* orig_ax is probably okay, too. +*/ + regs_user_copy->ip = user_regs->ip; + regs_user_copy->cx = user_regs->cx; + regs_user_copy->dx = user_regs->dx; + regs_user_copy->si = user_regs->si; + regs_user_copy->di = user_regs->di; + regs_user_copy->r8 = user_regs->r8; + regs_user_copy->r9 = user_regs->r9; + regs_user_copy->r10 = user_regs->r10; + regs_user_copy->r11 = user_regs->r11; + regs_user_copy->orig_ax = user_regs->orig_ax; + regs_user_copy->flags = user_regs->flags; + +/* +* Don't even try to report the "rest" regs. +*/ + regs_user_copy->bx =-1 ; + regs_user_copy->bp =-1 ; + regs_user_copy->r12 =-1 ; + regs_user_copy->r13 =-1 ; + regs_user_copy->r14 =-1 ; + regs_user_copy->r15 =-1 ; + +/* +* For this to be at all useful, we need a reasonable guess for +* sp and the ABI. Be careful: we're in NMI context, and we're +* considering current to be the current task, so we should +* be careful not to look at any other percpu variables that might +* change during context switches. +*/ +if (IS_ENABLED (CONFIG_IA32_EMULATION ) && +task_thread_info (current)->status &TS_COMPAT ) { +/* Easy case: we're in a compat syscall. */ + regs_user->abi =PERF_SAMPLE_REGS_ABI_32 ; + regs_user_copy->sp = user_regs->sp; + regs_user_copy->cs = user_regs->cs; + regs_user_copy->ss = user_regs->ss; + }else if (user_regs->orig_ax !=-1 ) { +/* +* We're probably in a 64-bit syscall. +* Warning: this code is severely racy. At least it's better +* than just blindly copying user_regs. +*/ + regs_user->abi =PERF_SAMPLE_REGS_ABI_64 ; + regs_user_copy->sp =this_cpu_read (old_rsp); + regs_user_copy->cs = __USER_CS; + regs_user_copy->ss = __USER_DS; + regs_user_copy->cx =-1 ;/* usually contains garbage */ + }else { +/* We're probably in an interrupt or exception. */ + regs_user->abi =user_64bit_mode (user_regs) ? +PERF_SAMPLE_REGS_ABI_64 :PERF_SAMPLE_REGS_ABI_32 ; + regs_user_copy->sp = user_regs->sp; + regs_user_copy->cs = user_regs->cs; + regs_user_copy->ss = user_regs->ss; + } + + regs_user->regs = regs_user_copy; }#endif /* CONFIG_X86_32 */
Signature · 1 line
2.1.0
tip-bot for Andy Lutomirski
merge bot[tip:perf/urgent] perf/x86_64: Improve user regs sampling
perf/x86_64: Improve user regs sampling
Commit-ID: 86c269fea37334687b1c0789e6444be0d750e8a6 Gitweb: http://git.kernel.org/tip/86c269fea37334687b1c0789e6444be0d750e8a6 Author: Andy Lutomirski <[email protected]> AuthorDate: Sun, 4 Jan 2015 10:36:20 -0800 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 9 Jan 2015 11:12:29 +0100
perf/x86_64: Improve user regs sampling
Perf reports user regs for kernel-mode samples so that samples can be backtraced through user code. The old code was very broken in syscall context, resulting in useless backtraces.
The new code, in contrast, is still dangerously racy, but it should at least work most of the time.
--- arch/x86/kernel/perf_regs.c | 78 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-)
arch/x86/kernel/perf_regs.c +76 -2
@@ -115,7 +115,81 @@ void perf_get_regs_user(struct perf_regs *regs_user,struct pt_regs *regs, struct pt_regs *regs_user_copy) { - regs_user->regs = task_pt_regs(current); - regs_user->abi =perf_reg_abi (current); + struct pt_regs *user_regs = task_pt_regs(current); + +/* +* If we're in an NMI that interrupted task_pt_regs setup, then +* we can't sample user regs at all. This check isn't really +* sufficient, though, as we could be in an NMI inside an interrupt +* that happened during task_pt_regs setup. +*/ +if (regs->sp > (unsigned long )&user_regs->r11 && + regs->sp <= (unsigned long )(user_regs +1 )) { + regs_user->abi =PERF_SAMPLE_REGS_ABI_NONE ; + regs_user->regs =NULL ; +return ; + } + +/* +* RIP, flags, and the argument registers are usually saved. +* orig_ax is probably okay, too. +*/ + regs_user_copy->ip = user_regs->ip; + regs_user_copy->cx = user_regs->cx; + regs_user_copy->dx = user_regs->dx; + regs_user_copy->si = user_regs->si; + regs_user_copy->di = user_regs->di; + regs_user_copy->r8 = user_regs->r8; + regs_user_copy->r9 = user_regs->r9; + regs_user_copy->r10 = user_regs->r10; + regs_user_copy->r11 = user_regs->r11; + regs_user_copy->orig_ax = user_regs->orig_ax; + regs_user_copy->flags = user_regs->flags; + +/* +* Don't even try to report the "rest" regs. +*/ + regs_user_copy->bx =-1 ; + regs_user_copy->bp =-1 ; + regs_user_copy->r12 =-1 ; + regs_user_copy->r13 =-1 ; + regs_user_copy->r14 =-1 ; + regs_user_copy->r15 =-1 ; + +/* +* For this to be at all useful, we need a reasonable guess for +* sp and the ABI. Be careful: we're in NMI context, and we're +* considering current to be the current task, so we should +* be careful not to look at any other percpu variables that might +* change during context switches. +*/ +if (IS_ENABLED (CONFIG_IA32_EMULATION ) && +task_thread_info (current)->status &TS_COMPAT ) { +/* Easy case: we're in a compat syscall. */ + regs_user->abi =PERF_SAMPLE_REGS_ABI_32 ; + regs_user_copy->sp = user_regs->sp; + regs_user_copy->cs = user_regs->cs; + regs_user_copy->ss = user_regs->ss; + }else if (user_regs->orig_ax !=-1 ) { +/* +* We're probably in a 64-bit syscall. +* Warning: this code is severely racy. At least it's better +* than just blindly copying user_regs. +*/ + regs_user->abi =PERF_SAMPLE_REGS_ABI_64 ; + regs_user_copy->sp =this_cpu_read (old_rsp); + regs_user_copy->cs = __USER_CS; + regs_user_copy->ss = __USER_DS; + regs_user_copy->cx =-1 ;/* usually contains garbage */ + }else { +/* We're probably in an interrupt or exception. */ + regs_user->abi =user_64bit_mode (user_regs) ? +PERF_SAMPLE_REGS_ABI_64 :PERF_SAMPLE_REGS_ABI_32 ; + regs_user_copy->sp = user_regs->sp; + regs_user_copy->cs = user_regs->cs; + regs_user_copy->ss = user_regs->ss; + } + + regs_user->regs = regs_user_copy; }#endif /* CONFIG_X86_32 */
Re: [PATCH 0/2] perf: Improve user regs sampling
In reply to Andy LutomirskiQuoted 4 lines · Andy Lutomirski
This seems to improve the situation on
x86_64from almost entirely broken to mostly working. Tested with:perf record -e cycles --call-graph=dwarf ls
looks good to me.. also:
thanks,
jirka
Quoted 16 lines
Andy Lutomirski (2):
perf: Movetask_pt_regssampling into arch code
x86_64, perf: Improve user regs samplingarch/arm/kernel/perf_regs.c | 8 ++++ arch/arm64/kernel/perf_regs.c | 8 ++++ arch/x86/kernel/perf_regs.c | 90 +++++++++++++++++++++++++++++++++++++++++++ include/linux/perf_event.h | 12 +++--- include/linux/perf_regs.h | 16 ++++++++ kernel/events/core.c | 19 ++++----- 6 files changed, 137 insertions(+), 16 deletions(-)Signature · 1 line
2.1.0