Skip to contentSkip to conversation

perf core: Use KSTK_ESP() instead of pt_regs->sp while output user regs

PATCH In review root 6 participants 23 messages Dec 23, 2014 – Jan 9, 2015 1 message not in the archive

Subsystems (5)
Arm (32-bit) x86 perf events arm64 +1 more subsystems
lore
Share & tools
lore

root

author
#0 lore raw

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.

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 +3 -0
index e309cc5..5da8df8 100644
@@ -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
#1 lore raw
Quoted 36 lines · root

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.

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
index e309cc5..5da8df8 100644

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 -0
 	return 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]

#3 lore raw
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 we

shouldn't use current_user_stack_pointer();
User: sp = task_pt_regs(task)->sp;
current's pt_regs are 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's pt_regs are 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: Use KSTK_ESP() instead of pt_regs->sp while output user regs

Quoted 39 lines · root

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.

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
index e309cc5..5da8df8 100644

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 -0
 return regs_get_register(regs, pt_regs_offset[idx]);
 }
 
Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
#4 lore raw
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_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 we

shouldn't use current_user_stack_pointer();
User: sp = task_pt_regs(task)->sp;
current's pt_regs are 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's pt_regs are 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.

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_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 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: Use KSTK_ESP() instead of pt_regs->sp while output user regs

Quoted 39 lines · root

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.

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
index e309cc5..5da8df8 100644

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.

Quoted 8 lines

What context are you using KSTK_ESP in?

--Andy

Quoted 4 lines
arch/x86/kernel/perf_regs.c +0 -0
 return regs_get_register(regs, pt_regs_offset[idx]);
 }
 

--
Andy Lutomirski
AMA Capital Management, LLC

Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
maintainer
#5 lore raw
In reply to Andy Lutomirski
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_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.

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_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.

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.

#6 lore raw
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_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.

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_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.

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.

#7 lore raw
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_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.

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_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.

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.

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 if user_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_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.

Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
#8 lore raw
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_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.

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 if user_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_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?

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

#9 lore raw
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_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.

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 if user_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.

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_REGISTER use in the SAVE_ARGS_IRQ macro.

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

#10 lore raw
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_REGISTER use in the SAVE_ARGS_IRQ macro.

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

#11 lore raw
In reply to Peter Zijlstra
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_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.

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_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.

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

#12 lore raw
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_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.

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_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.

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=2

this 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

#13 lore raw
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_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.

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_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.

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=2

this 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.

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
index 5da8df8303cf..eb7c385a6f8e 100644
@@ -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
index af0a5ba4e21d..d99236173f3b 100644
@@ -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
index 569b218782ad..b85e4fd52980 100644
@@ -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 */
#14 lore raw

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
#15 lore raw

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
index 6e4379c67cbc..592dda3f21ff 100644
@@ -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
index 6762ad705587..3f62b35fb6f1 100644
@@ -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
index e309cc5c276e..3bbbb1a4fb52 100644
@@ -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
index 486e84ccb1f9..4f7a61ca4b39 100644
@@ -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
index 3c73d5fe18be..a5f98d53d732 100644
@@ -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
index 4c1ee7f2bebc..882f835a0d85 100644
@@ -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
maintainer
#16 lore raw

Re: [PATCH 1/2] perf: Move task_pt_regs sampling into arch code

Quoted 6 lines · Andy Lutomirski

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.

Just wondering how bad it would be to fill out the actual pt_regs that was previously partially initialized?

#17 lore raw

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_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.

Just wondering how bad it would be to fill out the actual pt_regs that 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

maintainer
#18 lore raw

Re: [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_regs that 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.

Right, but you need to deal with that anyhow.

Quoted 6 lines

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.)

Ah, urgh. Yes painful that.

Quoted 2 lines

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.

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).

#19 lore raw

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_regs that 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.

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_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.)

Quoted 8 lines

Ah, urgh. Yes painful that.

Quoted 2 lines

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.

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
#20 lore raw

[tip:perf/urgent] perf: Move task_pt_regs sampling into arch code

In reply to Andy Lutomirski
perf: 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
index 6e4379c..592dda3 100644
@@ -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
index 6762ad7..3f62b35 100644
@@ -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
index e309cc5..3bbbb1a 100644
@@ -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
index 486e84c..4f7a61c 100644
@@ -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
index 3c73d5f..a5f98d5 100644
@@ -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
index 4c1ee7f..882f835 100644
@@ -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 */
#21 lore raw

PATCH 2/2 x86_64, perf: Improve user regs sampling

In reply to Andy Lutomirski

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
index 3bbbb1a4fb52..781861cc5ee8 100644
@@ -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
#22 lore raw

[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
index 3bbbb1a..781861c 100644
@@ -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 */
Tested-by on the v1 series
#23 lore raw

Re: [PATCH 0/2] perf: Improve user regs sampling

In reply to Andy Lutomirski
Quoted 4 lines · Andy Lutomirski

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

looks good to me.. also:

thanks,
jirka

Quoted 16 lines

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