All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/1] arm64: Add stack unwinder unit tests
@ 2022-06-24 14:09 ` Ivan T. Ivanov
  0 siblings, 0 replies; 10+ messages in thread
From: Ivan T. Ivanov @ 2022-06-24 14:09 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon
  Cc: Mark Brown, Mark Rutland, Madhavan T . Venkataraman,
	linux-arm-kernel, linux-kernel, Ivan T. Ivanov

Hi,

Here is my initial arm64 unwinder test support. Code is based on s390
unwinder tests, while errors introduced are mine. I am hopping that it
will be useful for testing and validating upcoming arm64
arch_stack_walk_reliable() support.

This patch is was tested on top of latest Madhavan work [1].

As expected unwinding trough IRQ fails. Here is the output of current
statue of affair:

# modprobe test_unwind
[   14.905945]     # Subtest: test_unwind
[   14.905948]     1..1
[   14.905949]         # Subtest: test_unwind_flags
[   14.906243]         ok 1 - UWM_DEFAULT
[   14.906375]         ok 2 - UWM_REGS
[   14.906902]         ok 3 - UWM_CALLER
[   14.907010]         ok 4 - UWM_CALLER | UWM_REGS
[   14.907220]         ok 5 - UWM_THREAD
[   14.907445]         ok 6 - UWM_THREAD | UWM_CALLER
[   14.912600]     # test_unwind_flags:  test_unwind+0x70/0x280 [test_unwind]
[   14.912602]     # test_unwind_flags:  unwindme_func4+0x64/0x260 [test_unwind]
[   14.912604]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   14.912605]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.912606]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.912607]     # test_unwind_flags:  unwindme_timer_fn+0x38/0x54 [test_unwind]
[   14.912608]     # test_unwind_flags:  call_timer_fn+0x38/0x1e0
[   14.912609]     # test_unwind_flags:  run_timer_softirq+0x500/0x550
[   14.912610]     # test_unwind_flags:  __do_softirq+0x124/0x3bc
[   14.912611]     # test_unwind_flags:  irq_exit_rcu+0xf0/0x110
[   14.912611]     # test_unwind_flags:  el1_interrupt+0x3c/0x70
[   14.912612]     # test_unwind_flags:  el1h_64_irq_handler+0x18/0x28
[   14.912612]     # test_unwind_flags:  el1h_64_irq+0x64/0x68
[   14.912613]     # test_unwind_flags:  arch_cpu_idle+0x18/0x28
[   14.912614]     # test_unwind_flags:  default_idle_call+0x6c/0x1d0
[   14.912614]     # test_unwind_flags:  do_idle+0x1f0/0x290
[   14.912615]     # test_unwind_flags:  cpu_startup_entry+0x30/0x38
[   14.912615]     # test_unwind_flags:  kernel_init+0x0/0x140
[   14.912616]     # test_unwind_flags:  arch_post_acpi_subsys_init+0x0/0x28
[   14.912617]     # test_unwind_flags:  start_kernel+0x6dc/0x718
[   14.912618]     # test_unwind_flags:  __primary_switched+0xc0/0xc8
[   14.912618]     # test_unwind_flags:
[   14.912644]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:501
[   14.912644]     Expected 0 == test_unwind_irq(&u), but
[   14.912644]         test_unwind_irq(&u) == -22
[   14.912656]         not ok 7 - UWM_IRQ
[   14.924625]     # test_unwind_flags:  unwindme_func4+0xa0/0x260 [test_unwind]
[   14.924630]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   14.924632]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.924634]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.924636]     # test_unwind_flags:  unwindme_timer_fn+0x38/0x54 [test_unwind]
[   14.924638]     # test_unwind_flags:  call_timer_fn+0x38/0x1e0
[   14.924639]     # test_unwind_flags:  run_timer_softirq+0x500/0x550
[   14.924641]     # test_unwind_flags:  __do_softirq+0x124/0x3bc
[   14.924643]     # test_unwind_flags:  irq_exit_rcu+0xf0/0x110
[   14.924645]     # test_unwind_flags:  el1_interrupt+0x3c/0x70
[   14.924646]     # test_unwind_flags:  el1h_64_irq_handler+0x18/0x28
[   14.924647]     # test_unwind_flags:  el1h_64_irq+0x64/0x68
[   14.924648]     # test_unwind_flags:  arch_cpu_idle+0x18/0x28
[   14.924649]     # test_unwind_flags:  default_idle_call+0x6c/0x1d0
[   14.924650]     # test_unwind_flags:  do_idle+0x1f0/0x290
[   14.924651]     # test_unwind_flags:  cpu_startup_entry+0x30/0x38
[   14.924653]     # test_unwind_flags:  kernel_init+0x0/0x140
[   14.924654]     # test_unwind_flags:  arch_post_acpi_subsys_init+0x0/0x28
[   14.924655]     # test_unwind_flags:  start_kernel+0x6dc/0x718
[   14.924656]     # test_unwind_flags:  __primary_switched+0xc0/0xc8
[   14.924657]     # test_unwind_flags:
[   14.924677]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:501
[   14.924677]     Expected 0 == test_unwind_irq(&u), but
[   14.924677]         test_unwind_irq(&u) == -22
[   14.924724]         not ok 8 - UWM_IRQ | UWM_REGS
[   14.936710]     # test_unwind_flags:  test_unwind+0x70/0x280 [test_unwind]
[   14.936717]     # test_unwind_flags:  unwindme_func4+0x64/0x260 [test_unwind]
[   14.936720]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   14.936722]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.936725]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.936727]     # test_unwind_flags:  unwindme_timer_fn+0x38/0x54 [test_unwind]
[   14.936729]     # test_unwind_flags:  call_timer_fn+0x38/0x1e0
[   14.936732]     # test_unwind_flags:  run_timer_softirq+0x500/0x550
[   14.936734]     # test_unwind_flags:  __do_softirq+0x124/0x3bc
[   14.936736]     # test_unwind_flags:  irq_exit_rcu+0xf0/0x110
[   14.936738]     # test_unwind_flags:  el1_interrupt+0x3c/0x70
[   14.936739]     # test_unwind_flags:  el1h_64_irq_handler+0x18/0x28
[   14.936741]     # test_unwind_flags:  el1h_64_irq+0x64/0x68
[   14.936743]     # test_unwind_flags:  arch_cpu_idle+0x18/0x28
[   14.936744]     # test_unwind_flags:  default_idle_call+0x6c/0x1d0
[   14.936746]     # test_unwind_flags:  do_idle+0x1f0/0x290
[   14.936748]     # test_unwind_flags:  cpu_startup_entry+0x30/0x38
[   14.936749]     # test_unwind_flags:  secondary_start_kernel+0x198/0x1c8
[   14.936751]     # test_unwind_flags:  __secondary_switched+0xa0/0xa4
[   14.936753]     # test_unwind_flags:
[   14.936839]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:501
[   14.936839]     Expected 0 == test_unwind_irq(&u), but
[   14.936839]         test_unwind_irq(&u) == -22
[   14.936874]         not ok 9 - UWM_IRQ | UWM_CALLER
[   14.944623]     # test_unwind_flags:  unwindme_func3+0x3c/0x80 [test_unwind]
[   14.944628]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.944631]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.944633]     # test_unwind_flags:  unwindme_timer_fn+0x38/0x54 [test_unwind]
[   14.944636]     # test_unwind_flags:  call_timer_fn+0x38/0x1e0
[   14.944638]     # test_unwind_flags:  run_timer_softirq+0x500/0x550
[   14.944641]     # test_unwind_flags:  __do_softirq+0x124/0x3bc
[   14.944643]     # test_unwind_flags:  irq_exit_rcu+0xf0/0x110
[   14.944646]     # test_unwind_flags:  el1_interrupt+0x3c/0x70
[   14.944649]     # test_unwind_flags:  el1h_64_irq_handler+0x18/0x28
[   14.944650]     # test_unwind_flags:  el1h_64_irq+0x64/0x68
[   14.944652]     # test_unwind_flags:  arch_cpu_idle+0x18/0x28
[   14.944654]     # test_unwind_flags:  default_idle_call+0x6c/0x1d0
[   14.944656]     # test_unwind_flags:  do_idle+0x1f0/0x290
[   14.944657]     # test_unwind_flags:  cpu_startup_entry+0x2c/0x38
[   14.944659]     # test_unwind_flags:  secondary_start_kernel+0x198/0x1c8
[   14.944661]     # test_unwind_flags:  __secondary_switched+0xa0/0xa4
[   14.944662]     # test_unwind_flags:
[   14.944687]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:501
[   14.944687]     Expected 0 == test_unwind_irq(&u), but
[   14.944687]         test_unwind_irq(&u) == -22
[   14.944737]         not ok 10 - UWM_IRQ | UWM_CALLER | UWM_REGS
[   14.945009]         ok 11 - UWM_REGS
[   14.945174]         ok 12 - UWM_KPROBE_ON_FTRACE
[   14.945299]         ok 13 - UWM_KPROBE_ON_FTRACE | UWM_REGS
[   14.946894]     # test_unwind_flags:  test_unwind+0x70/0x280 [test_unwind]
[   14.946902]     # test_unwind_flags:  test_unwind_ftrace_handler+0x28/0x40 [test_unwind]
[   14.946904]     # test_unwind_flags:  arch_ftrace_ops_list_func+0xd8/0x1b0
[   14.946906]     # test_unwind_flags:  ftrace_call+0x4/0x2c
[   14.946908]     # test_unwind_flags:  test_unwind_ftraced_func+0x8/0x28 [test_unwind]
[   14.946910]     # test_unwind_flags:  unwindme_func4+0x1b4/0x260 [test_unwind]
[   14.946912]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   14.946915]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.946917]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.946918]     # test_unwind_flags:  test_unwind_flags+0x5c/0x288 [test_unwind]
[   14.946920]     # test_unwind_flags:  kunit_try_run_case+0x58/0xc0 [kunit]
[   14.946921]     # test_unwind_flags:  kunit_generic_run_threadfn_adapter+0x28/0x38 [kunit]
[   14.946923]     # test_unwind_flags:  kthread+0x118/0x128
[   14.946924]     # test_unwind_flags:  ret_from_fork+0x10/0x20
[   14.946925]     # test_unwind_flags:
[   15.104639]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:503
[   15.104639]     Expected 0 == unwindme_func1(&u), but
[   15.104639]         unwindme_func1(&u) == -22
[   15.104691]         not ok 14 - UWM_FTRACE
[   15.240652]         ok 15 - UWM_FTRACE | UWM_REGS
[   15.264620]         ok 16 - UWM_KRETPROBE
[   15.292603]         ok 17 - UWM_KRETPROBE | UWM_REGS
[   15.293173]     # test_unwind_flags:  test_unwind+0x70/0x280 [test_unwind]
[   15.293175]     # test_unwind_flags:  kretprobe_ret_handler+0x38/0x50 [test_unwind]
[   15.293177]     # test_unwind_flags:  __kretprobe_trampoline_handler+0xec/0x188
[   15.293178]     # test_unwind_flags:  trampoline_probe_handler+0x30/0x50
[   15.293180]     # test_unwind_flags:  __kretprobe_trampoline+0x74/0xc8
[   15.293181]     # test_unwind_flags:  test_unwind_kretprobed_func_caller+0x18/0x28 [test_unwind]
[   15.293183]     # test_unwind_flags:  test_unwind_kretprobe+0xac/0x148 [test_unwind]
[   15.293185]     # test_unwind_flags:  unwindme_func4+0xf0/0x260 [test_unwind]
[   15.293186]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   15.293187]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   15.293188]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   15.293189]     # test_unwind_flags:  test_unwind_flags+0x5c/0x288 [test_unwind]
[   15.293190]     # test_unwind_flags:  kunit_try_run_case+0x58/0xc0 [kunit]
[   15.293191]     # test_unwind_flags:  kunit_generic_run_threadfn_adapter+0x28/0x38 [kunit]
[   15.293192]     # test_unwind_flags:  kthread+0x118/0x128
[   15.293192]     # test_unwind_flags:  ret_from_fork+0x10/0x20
[   15.293193]     # test_unwind_flags:
[   15.308570]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:503
[   15.308570]     Expected 0 == unwindme_func1(&u), but
[   15.308570]         unwindme_func1(&u) == -22
[   15.308611]         not ok 18 - UWM_KRETPROBE_HANDLER
[   15.324616]         ok 19 - UWM_KRETPROBE_HANDLER | UWM_REGS
[   15.324620]     # test_unwind_flags: pass:11 fail:6 skip:2 total:19
[   15.324623]     not ok 1 - test_unwind_flags
[   15.324626] # Totals: pass:11 fail:6 skip:2 total:19
[   15.324628] not ok 1 - test_unwind

Regards,
Ivan

[1] 20220617210717.27126-1-madvenka@linux.microsoft.com

Ivan T. Ivanov (1):
  arm64: Add initial set of stack unwinder self tests

 arch/arm64/Kconfig.debug        |  12 +
 arch/arm64/kernel/Makefile      |   1 +
 arch/arm64/kernel/stacktrace.c  |  23 ++
 arch/arm64/kernel/test_unwind.c | 518 ++++++++++++++++++++++++++++++++
 include/linux/stacktrace.h      |   1 +
 5 files changed, 555 insertions(+)
 create mode 100644 arch/arm64/kernel/test_unwind.c

-- 
2.35.3


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 0/1] arm64: Add stack unwinder unit tests
@ 2022-06-24 14:09 ` Ivan T. Ivanov
  0 siblings, 0 replies; 10+ messages in thread
From: Ivan T. Ivanov @ 2022-06-24 14:09 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon
  Cc: Mark Brown, Mark Rutland, Madhavan T . Venkataraman,
	linux-arm-kernel, linux-kernel, Ivan T. Ivanov

Hi,

Here is my initial arm64 unwinder test support. Code is based on s390
unwinder tests, while errors introduced are mine. I am hopping that it
will be useful for testing and validating upcoming arm64
arch_stack_walk_reliable() support.

This patch is was tested on top of latest Madhavan work [1].

As expected unwinding trough IRQ fails. Here is the output of current
statue of affair:

# modprobe test_unwind
[   14.905945]     # Subtest: test_unwind
[   14.905948]     1..1
[   14.905949]         # Subtest: test_unwind_flags
[   14.906243]         ok 1 - UWM_DEFAULT
[   14.906375]         ok 2 - UWM_REGS
[   14.906902]         ok 3 - UWM_CALLER
[   14.907010]         ok 4 - UWM_CALLER | UWM_REGS
[   14.907220]         ok 5 - UWM_THREAD
[   14.907445]         ok 6 - UWM_THREAD | UWM_CALLER
[   14.912600]     # test_unwind_flags:  test_unwind+0x70/0x280 [test_unwind]
[   14.912602]     # test_unwind_flags:  unwindme_func4+0x64/0x260 [test_unwind]
[   14.912604]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   14.912605]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.912606]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.912607]     # test_unwind_flags:  unwindme_timer_fn+0x38/0x54 [test_unwind]
[   14.912608]     # test_unwind_flags:  call_timer_fn+0x38/0x1e0
[   14.912609]     # test_unwind_flags:  run_timer_softirq+0x500/0x550
[   14.912610]     # test_unwind_flags:  __do_softirq+0x124/0x3bc
[   14.912611]     # test_unwind_flags:  irq_exit_rcu+0xf0/0x110
[   14.912611]     # test_unwind_flags:  el1_interrupt+0x3c/0x70
[   14.912612]     # test_unwind_flags:  el1h_64_irq_handler+0x18/0x28
[   14.912612]     # test_unwind_flags:  el1h_64_irq+0x64/0x68
[   14.912613]     # test_unwind_flags:  arch_cpu_idle+0x18/0x28
[   14.912614]     # test_unwind_flags:  default_idle_call+0x6c/0x1d0
[   14.912614]     # test_unwind_flags:  do_idle+0x1f0/0x290
[   14.912615]     # test_unwind_flags:  cpu_startup_entry+0x30/0x38
[   14.912615]     # test_unwind_flags:  kernel_init+0x0/0x140
[   14.912616]     # test_unwind_flags:  arch_post_acpi_subsys_init+0x0/0x28
[   14.912617]     # test_unwind_flags:  start_kernel+0x6dc/0x718
[   14.912618]     # test_unwind_flags:  __primary_switched+0xc0/0xc8
[   14.912618]     # test_unwind_flags:
[   14.912644]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:501
[   14.912644]     Expected 0 == test_unwind_irq(&u), but
[   14.912644]         test_unwind_irq(&u) == -22
[   14.912656]         not ok 7 - UWM_IRQ
[   14.924625]     # test_unwind_flags:  unwindme_func4+0xa0/0x260 [test_unwind]
[   14.924630]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   14.924632]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.924634]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.924636]     # test_unwind_flags:  unwindme_timer_fn+0x38/0x54 [test_unwind]
[   14.924638]     # test_unwind_flags:  call_timer_fn+0x38/0x1e0
[   14.924639]     # test_unwind_flags:  run_timer_softirq+0x500/0x550
[   14.924641]     # test_unwind_flags:  __do_softirq+0x124/0x3bc
[   14.924643]     # test_unwind_flags:  irq_exit_rcu+0xf0/0x110
[   14.924645]     # test_unwind_flags:  el1_interrupt+0x3c/0x70
[   14.924646]     # test_unwind_flags:  el1h_64_irq_handler+0x18/0x28
[   14.924647]     # test_unwind_flags:  el1h_64_irq+0x64/0x68
[   14.924648]     # test_unwind_flags:  arch_cpu_idle+0x18/0x28
[   14.924649]     # test_unwind_flags:  default_idle_call+0x6c/0x1d0
[   14.924650]     # test_unwind_flags:  do_idle+0x1f0/0x290
[   14.924651]     # test_unwind_flags:  cpu_startup_entry+0x30/0x38
[   14.924653]     # test_unwind_flags:  kernel_init+0x0/0x140
[   14.924654]     # test_unwind_flags:  arch_post_acpi_subsys_init+0x0/0x28
[   14.924655]     # test_unwind_flags:  start_kernel+0x6dc/0x718
[   14.924656]     # test_unwind_flags:  __primary_switched+0xc0/0xc8
[   14.924657]     # test_unwind_flags:
[   14.924677]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:501
[   14.924677]     Expected 0 == test_unwind_irq(&u), but
[   14.924677]         test_unwind_irq(&u) == -22
[   14.924724]         not ok 8 - UWM_IRQ | UWM_REGS
[   14.936710]     # test_unwind_flags:  test_unwind+0x70/0x280 [test_unwind]
[   14.936717]     # test_unwind_flags:  unwindme_func4+0x64/0x260 [test_unwind]
[   14.936720]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   14.936722]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.936725]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.936727]     # test_unwind_flags:  unwindme_timer_fn+0x38/0x54 [test_unwind]
[   14.936729]     # test_unwind_flags:  call_timer_fn+0x38/0x1e0
[   14.936732]     # test_unwind_flags:  run_timer_softirq+0x500/0x550
[   14.936734]     # test_unwind_flags:  __do_softirq+0x124/0x3bc
[   14.936736]     # test_unwind_flags:  irq_exit_rcu+0xf0/0x110
[   14.936738]     # test_unwind_flags:  el1_interrupt+0x3c/0x70
[   14.936739]     # test_unwind_flags:  el1h_64_irq_handler+0x18/0x28
[   14.936741]     # test_unwind_flags:  el1h_64_irq+0x64/0x68
[   14.936743]     # test_unwind_flags:  arch_cpu_idle+0x18/0x28
[   14.936744]     # test_unwind_flags:  default_idle_call+0x6c/0x1d0
[   14.936746]     # test_unwind_flags:  do_idle+0x1f0/0x290
[   14.936748]     # test_unwind_flags:  cpu_startup_entry+0x30/0x38
[   14.936749]     # test_unwind_flags:  secondary_start_kernel+0x198/0x1c8
[   14.936751]     # test_unwind_flags:  __secondary_switched+0xa0/0xa4
[   14.936753]     # test_unwind_flags:
[   14.936839]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:501
[   14.936839]     Expected 0 == test_unwind_irq(&u), but
[   14.936839]         test_unwind_irq(&u) == -22
[   14.936874]         not ok 9 - UWM_IRQ | UWM_CALLER
[   14.944623]     # test_unwind_flags:  unwindme_func3+0x3c/0x80 [test_unwind]
[   14.944628]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.944631]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.944633]     # test_unwind_flags:  unwindme_timer_fn+0x38/0x54 [test_unwind]
[   14.944636]     # test_unwind_flags:  call_timer_fn+0x38/0x1e0
[   14.944638]     # test_unwind_flags:  run_timer_softirq+0x500/0x550
[   14.944641]     # test_unwind_flags:  __do_softirq+0x124/0x3bc
[   14.944643]     # test_unwind_flags:  irq_exit_rcu+0xf0/0x110
[   14.944646]     # test_unwind_flags:  el1_interrupt+0x3c/0x70
[   14.944649]     # test_unwind_flags:  el1h_64_irq_handler+0x18/0x28
[   14.944650]     # test_unwind_flags:  el1h_64_irq+0x64/0x68
[   14.944652]     # test_unwind_flags:  arch_cpu_idle+0x18/0x28
[   14.944654]     # test_unwind_flags:  default_idle_call+0x6c/0x1d0
[   14.944656]     # test_unwind_flags:  do_idle+0x1f0/0x290
[   14.944657]     # test_unwind_flags:  cpu_startup_entry+0x2c/0x38
[   14.944659]     # test_unwind_flags:  secondary_start_kernel+0x198/0x1c8
[   14.944661]     # test_unwind_flags:  __secondary_switched+0xa0/0xa4
[   14.944662]     # test_unwind_flags:
[   14.944687]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:501
[   14.944687]     Expected 0 == test_unwind_irq(&u), but
[   14.944687]         test_unwind_irq(&u) == -22
[   14.944737]         not ok 10 - UWM_IRQ | UWM_CALLER | UWM_REGS
[   14.945009]         ok 11 - UWM_REGS
[   14.945174]         ok 12 - UWM_KPROBE_ON_FTRACE
[   14.945299]         ok 13 - UWM_KPROBE_ON_FTRACE | UWM_REGS
[   14.946894]     # test_unwind_flags:  test_unwind+0x70/0x280 [test_unwind]
[   14.946902]     # test_unwind_flags:  test_unwind_ftrace_handler+0x28/0x40 [test_unwind]
[   14.946904]     # test_unwind_flags:  arch_ftrace_ops_list_func+0xd8/0x1b0
[   14.946906]     # test_unwind_flags:  ftrace_call+0x4/0x2c
[   14.946908]     # test_unwind_flags:  test_unwind_ftraced_func+0x8/0x28 [test_unwind]
[   14.946910]     # test_unwind_flags:  unwindme_func4+0x1b4/0x260 [test_unwind]
[   14.946912]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   14.946915]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   14.946917]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   14.946918]     # test_unwind_flags:  test_unwind_flags+0x5c/0x288 [test_unwind]
[   14.946920]     # test_unwind_flags:  kunit_try_run_case+0x58/0xc0 [kunit]
[   14.946921]     # test_unwind_flags:  kunit_generic_run_threadfn_adapter+0x28/0x38 [kunit]
[   14.946923]     # test_unwind_flags:  kthread+0x118/0x128
[   14.946924]     # test_unwind_flags:  ret_from_fork+0x10/0x20
[   14.946925]     # test_unwind_flags:
[   15.104639]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:503
[   15.104639]     Expected 0 == unwindme_func1(&u), but
[   15.104639]         unwindme_func1(&u) == -22
[   15.104691]         not ok 14 - UWM_FTRACE
[   15.240652]         ok 15 - UWM_FTRACE | UWM_REGS
[   15.264620]         ok 16 - UWM_KRETPROBE
[   15.292603]         ok 17 - UWM_KRETPROBE | UWM_REGS
[   15.293173]     # test_unwind_flags:  test_unwind+0x70/0x280 [test_unwind]
[   15.293175]     # test_unwind_flags:  kretprobe_ret_handler+0x38/0x50 [test_unwind]
[   15.293177]     # test_unwind_flags:  __kretprobe_trampoline_handler+0xec/0x188
[   15.293178]     # test_unwind_flags:  trampoline_probe_handler+0x30/0x50
[   15.293180]     # test_unwind_flags:  __kretprobe_trampoline+0x74/0xc8
[   15.293181]     # test_unwind_flags:  test_unwind_kretprobed_func_caller+0x18/0x28 [test_unwind]
[   15.293183]     # test_unwind_flags:  test_unwind_kretprobe+0xac/0x148 [test_unwind]
[   15.293185]     # test_unwind_flags:  unwindme_func4+0xf0/0x260 [test_unwind]
[   15.293186]     # test_unwind_flags:  unwindme_func3+0x58/0x80 [test_unwind]
[   15.293187]     # test_unwind_flags:  unwindme_func2+0x18/0x28 [test_unwind]
[   15.293188]     # test_unwind_flags:  unwindme_func1+0x18/0x28 [test_unwind]
[   15.293189]     # test_unwind_flags:  test_unwind_flags+0x5c/0x288 [test_unwind]
[   15.293190]     # test_unwind_flags:  kunit_try_run_case+0x58/0xc0 [kunit]
[   15.293191]     # test_unwind_flags:  kunit_generic_run_threadfn_adapter+0x28/0x38 [kunit]
[   15.293192]     # test_unwind_flags:  kthread+0x118/0x128
[   15.293192]     # test_unwind_flags:  ret_from_fork+0x10/0x20
[   15.293193]     # test_unwind_flags:
[   15.308570]     # test_unwind_flags: EXPECTATION FAILED at arch/arm64/kernel/test_unwind.c:503
[   15.308570]     Expected 0 == unwindme_func1(&u), but
[   15.308570]         unwindme_func1(&u) == -22
[   15.308611]         not ok 18 - UWM_KRETPROBE_HANDLER
[   15.324616]         ok 19 - UWM_KRETPROBE_HANDLER | UWM_REGS
[   15.324620]     # test_unwind_flags: pass:11 fail:6 skip:2 total:19
[   15.324623]     not ok 1 - test_unwind_flags
[   15.324626] # Totals: pass:11 fail:6 skip:2 total:19
[   15.324628] not ok 1 - test_unwind

Regards,
Ivan

[1] 20220617210717.27126-1-madvenka@linux.microsoft.com

Ivan T. Ivanov (1):
  arm64: Add initial set of stack unwinder self tests

 arch/arm64/Kconfig.debug        |  12 +
 arch/arm64/kernel/Makefile      |   1 +
 arch/arm64/kernel/stacktrace.c  |  23 ++
 arch/arm64/kernel/test_unwind.c | 518 ++++++++++++++++++++++++++++++++
 include/linux/stacktrace.h      |   1 +
 5 files changed, 555 insertions(+)
 create mode 100644 arch/arm64/kernel/test_unwind.c

-- 
2.35.3


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/1] arm64: Add initial set of stack unwinder self tests
  2022-06-24 14:09 ` Ivan T. Ivanov
@ 2022-06-24 14:10   ` Ivan T. Ivanov
  -1 siblings, 0 replies; 10+ messages in thread
From: Ivan T. Ivanov @ 2022-06-24 14:10 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon
  Cc: Mark Brown, Mark Rutland, Madhavan T . Venkataraman,
	linux-arm-kernel, linux-kernel, Ivan T. Ivanov

Add kunit tests for obvious cases where stack unwind could be needed.
Like these:

 * Unwind a separate task
 * Unwind starting from caller
 * Unwind from irq context
 * Unwind from kprobe handler called via ftrace
 * Unwind from ftrace handler
 * Unwind through kretprobed function
 * Unwind from kretprobe handler

Tests are completely based on code used in s390 unwinder tests.
Cases which where not relevant to aarch64 where removed and
some places where adjusted to address aarch64 specifics.

Cc: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>
Signed-off-by: Ivan T. Ivanov <iivanov@suse.de>
---
 arch/arm64/Kconfig.debug        |  12 +
 arch/arm64/kernel/Makefile      |   1 +
 arch/arm64/kernel/stacktrace.c  |  23 ++
 arch/arm64/kernel/test_unwind.c | 518 ++++++++++++++++++++++++++++++++
 include/linux/stacktrace.h      |   1 +
 5 files changed, 555 insertions(+)
 create mode 100644 arch/arm64/kernel/test_unwind.c

diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index 265c4461031f..a48ab884754e 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -20,4 +20,16 @@ config ARM64_RELOC_TEST
 	depends on m
 	tristate "Relocation testing module"
 
+config ARM64_UNWIND_SELFTEST
+	def_tristate n
+	depends on KUNIT
+	default KUNIT_ALL_TESTS
+	prompt "Test unwind functions"
+	help
+	  This option enables aarch64 specific stack unwinder testing kernel
+	  module. This option is not useful for distributions or general
+	  kernels, but only for kernel developers working on architecture code.
+
+	  Say N if you are unsure.
+
 source "drivers/hwtracing/coresight/Kconfig"
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index fa7981d0d917..014da14dd25c 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_ARM64_PTR_AUTH)		+= pointer_auth.o
 obj-$(CONFIG_ARM64_MTE)			+= mte.o
 obj-y					+= vdso-wrap.o
 obj-$(CONFIG_COMPAT_VDSO)		+= vdso32-wrap.o
+obj-$(CONFIG_ARM64_UNWIND_SELFTEST)	+= test_unwind.o
 
 # Force dependency (vdso*-wrap.S includes vdso.so through incbin)
 $(obj)/vdso-wrap.o: $(obj)/vdso/vdso.so
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index 8016ba0e2c96..6dc39b356cc2 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -406,3 +406,26 @@ noinline int notrace arch_stack_walk_reliable(
 	reliable = unwind(&state, consume_entry, cookie);
 	return reliable ? 0 : -EINVAL;
 }
+
+#if IS_ENABLED(CONFIG_ARM64_UNWIND_SELFTEST)
+noinline notrace int test_arch_stack_walk(stack_trace_consume_fn consume_entry,
+					  void *cookie,
+					  struct task_struct *task,
+					  struct pt_regs *regs)
+{
+	struct unwind_state state;
+
+	if (regs) {
+		if (task != current)
+			return -EINVAL;
+		unwind_init_from_regs(&state, regs);
+	} else if (task == current) {
+		unwind_init_from_caller(&state);
+	} else {
+		unwind_init_from_task(&state, task);
+	}
+
+	return unwind(&state, consume_entry, cookie) ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(test_arch_stack_walk);
+#endif
diff --git a/arch/arm64/kernel/test_unwind.c b/arch/arm64/kernel/test_unwind.c
new file mode 100644
index 000000000000..fc09400f3d12
--- /dev/null
+++ b/arch/arm64/kernel/test_unwind.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test module for aarch64 stack unwinder functionality.
+ * Based on s390 unwinder tests.
+ */
+
+#include <kunit/test.h>
+#include <linux/completion.h>
+#include <linux/kallsyms.h>
+#include <linux/kthread.h>
+#include <linux/ftrace.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/kprobes.h>
+#include <linux/wait.h>
+#include <asm/irq.h>
+#include <asm/stacktrace.h>
+
+static struct kunit *current_test;
+
+#define BT_BUF_SIZE (PAGE_SIZE * 4)
+
+static bool force_bt;
+module_param_named(backtrace, force_bt, bool, 0444);
+MODULE_PARM_DESC(backtrace, "print backtraces for all tests");
+
+struct unwind_tracker {
+	int frame_count;
+	int prev_is_func2;
+	int seen_func2_func1;
+	int seen_kretprobe_trampoline;
+	char *bt_buff;
+	size_t bt_pos;
+};
+
+/*
+ * To avoid printk line limit split backtrace by lines
+ */
+static void print_backtrace(char *bt)
+{
+	char *p;
+
+	while (true) {
+		p = strsep(&bt, "\n");
+		if (!p)
+			break;
+		kunit_err(current_test, "%s\n", p);
+	}
+}
+
+static bool check_backtrace_entry(void *arg, unsigned long addr)
+{
+	struct unwind_tracker *tracker = arg;
+	const int max_frames = 128;
+	char sym[KSYM_SYMBOL_LEN];
+
+	if (tracker->frame_count++ == max_frames) {
+		kunit_err(current_test, "stack too deep\n");
+		return false;
+	}
+
+	if (!addr) {
+		kunit_err(current_test, "addr is 0\n");
+		return false;
+	}
+
+	sprint_symbol(sym, addr);
+	if (tracker->prev_is_func2 && str_has_prefix(sym, "unwindme_func1"))
+		tracker->seen_func2_func1 = 1;
+
+	tracker->prev_is_func2 = str_has_prefix(sym, "unwindme_func2");
+
+	if (str_has_prefix(sym, "__kretprobe_trampoline+0x0/"))
+		tracker->seen_kretprobe_trampoline = 1;
+
+	if (tracker->bt_pos >= BT_BUF_SIZE) {
+		kunit_err(current_test, "backtrace buffer is too small\n");
+		return true;
+	}
+
+	tracker->bt_pos += snprintf(tracker->bt_buff + tracker->bt_pos,
+				    BT_BUF_SIZE - tracker->bt_pos, " %pSR\n",
+				    (void *)addr);
+	return true;
+}
+
+int test_arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
+			 struct task_struct *task, struct pt_regs *regs);
+
+/*
+ * Calls test_arch_stack_walk() which is handy wrapper of aarch64 unwind
+ * functionality, and verifies that the result contains unwindme_func2
+ *followed by unwindme_func1.
+ */
+static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs)
+{
+	struct unwind_tracker tracker;
+	int ret = 0;
+
+	/* Unwind. */
+	tracker.frame_count = 0;
+	tracker.prev_is_func2 = 0;
+	tracker.seen_func2_func1 = 0;
+	tracker.seen_kretprobe_trampoline = 0;
+
+	tracker.bt_pos = 0;
+	tracker.bt_buff = kmalloc(BT_BUF_SIZE, GFP_ATOMIC);
+	if (!tracker.bt_buff) {
+		kunit_err(current_test,
+			  "failed to allocate backtrace buffer\n");
+		return -ENOMEM;
+	}
+
+	ret = test_arch_stack_walk(check_backtrace_entry, &tracker, task, regs);
+
+	if (!tracker.seen_func2_func1) {
+		kunit_err(current_test,
+			  "unwindme_func2 and unwindme_func1 not found\n");
+		ret = -EINVAL;
+	}
+	if (tracker.frame_count == 128) {
+		kunit_err(current_test, "Maximum number of frames exceeded\n");
+		ret = -EINVAL;
+	}
+	if (tracker.seen_kretprobe_trampoline) {
+		kunit_err(current_test,
+			  "__kretprobe_trampoline+0x0 in unwinding results\n");
+		ret = -EINVAL;
+	}
+
+	if (ret || force_bt)
+		print_backtrace(tracker.bt_buff);
+
+	kfree(tracker.bt_buff);
+	return ret;
+}
+
+/* State of the task being unwound. */
+struct unwindme {
+	int flags;
+	int ret;
+	struct task_struct *task;
+	struct completion task_ready;
+	wait_queue_head_t task_wq;
+};
+
+static struct unwindme *unwindme;
+
+/* Values of unwindme.flags. */
+#define UWM_DEFAULT		0x0
+#define UWM_THREAD		0x1	/* Unwind a separate task. */
+#define UWM_REGS		0x2	/* Pass regs to test_unwind(). */
+#define UWM_CALLER		0x8	/* Unwind starting from caller. */
+#define UWM_IRQ			0x20	/* Unwind from irq context. */
+#define UWM_KPROBE_ON_FTRACE	0x80	/* Unwind from kprobe handler called via ftrace. */
+#define UWM_FTRACE		0x100	/* Unwind from ftrace handler. */
+#define UWM_KRETPROBE		0x200	/* Unwind through kretprobed function. */
+#define UWM_KRETPROBE_HANDLER	0x400	/* Unwind from kretprobe handler. */
+
+static notrace noinline struct pt_regs fake_pt_regs(void)
+{
+	struct pt_regs regs;
+
+	memset(&regs, 0, sizeof(regs));
+
+	regs.regs[29] = (unsigned long)__builtin_frame_address(1);
+	regs.pc = (unsigned long)__builtin_return_address(0);
+
+	return regs;
+}
+
+static int kretprobe_ret_handler(struct kretprobe_instance *ri,
+				 struct pt_regs *regs)
+{
+	struct unwindme *u = unwindme;
+
+	if (!(u->flags & UWM_KRETPROBE_HANDLER))
+		return 0;
+
+	u->ret = test_unwind(current, (u->flags & UWM_REGS) ? regs : NULL);
+
+	return 0;
+}
+
+static noinline notrace int test_unwind_kretprobed_func(struct unwindme *u)
+{
+	struct pt_regs regs;
+
+	if (!(u->flags & UWM_KRETPROBE))
+		return 0;
+
+	regs = fake_pt_regs();
+	return test_unwind(current, (u->flags & UWM_REGS) ? &regs : NULL);
+}
+
+static noinline int test_unwind_kretprobed_func_caller(struct unwindme *u)
+{
+	return test_unwind_kretprobed_func(u);
+}
+
+static int test_unwind_kretprobe(struct unwindme *u)
+{
+	int ret;
+	struct kretprobe my_kretprobe;
+
+	if (!IS_ENABLED(CONFIG_KPROBES))
+		kunit_skip(current_test, "requires CONFIG_KPROBES");
+
+	u->ret = -1; /* make sure kprobe is called */
+	unwindme = u;
+
+	memset(&my_kretprobe, 0, sizeof(my_kretprobe));
+	my_kretprobe.handler = kretprobe_ret_handler;
+	my_kretprobe.maxactive = 1;
+	my_kretprobe.kp.addr = (kprobe_opcode_t *)test_unwind_kretprobed_func;
+
+	ret = register_kretprobe(&my_kretprobe);
+
+	if (ret < 0) {
+		kunit_err(current_test, "register_kretprobe failed %d\n", ret);
+		return -EINVAL;
+	}
+
+	ret = test_unwind_kretprobed_func_caller(u);
+	unregister_kretprobe(&my_kretprobe);
+	unwindme = NULL;
+	if (u->flags & UWM_KRETPROBE_HANDLER)
+		ret = u->ret;
+	return ret;
+}
+
+static int kprobe_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+	struct unwindme *u = unwindme;
+
+	u->ret = test_unwind(current, (u->flags & UWM_REGS) ? regs : NULL);
+	return 0;
+}
+
+extern const char test_unwind_kprobed_insn[];
+
+static noinline void test_unwind_kprobed_func(void)
+{
+	asm volatile("	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "test_unwind_kprobed_insn:\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     :);
+}
+
+static int test_unwind_kprobe(struct unwindme *u)
+{
+	struct kprobe kp;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_KPROBES))
+		kunit_skip(current_test, "requires CONFIG_KPROBES");
+	if (!IS_ENABLED(CONFIG_KPROBES_ON_FTRACE) &&
+	    u->flags & UWM_KPROBE_ON_FTRACE)
+		kunit_skip(current_test, "requires CONFIG_KPROBES_ON_FTRACE");
+
+	u->ret = -1; /* make sure kprobe is called */
+	unwindme = u;
+	memset(&kp, 0, sizeof(kp));
+	kp.pre_handler = kprobe_pre_handler;
+	kp.addr = u->flags & UWM_KPROBE_ON_FTRACE ?
+			  (kprobe_opcode_t *)test_unwind_kprobed_func :
+			  (kprobe_opcode_t *)test_unwind_kprobed_insn;
+	ret = register_kprobe(&kp);
+	if (ret < 0) {
+		kunit_err(current_test, "register_kprobe failed %d\n", ret);
+		return -EINVAL;
+	}
+
+	test_unwind_kprobed_func();
+	unregister_kprobe(&kp);
+	unwindme = NULL;
+	return u->ret;
+}
+
+static void notrace __used test_unwind_ftrace_handler(unsigned long ip,
+						      unsigned long parent_ip,
+						      struct ftrace_ops *fops,
+						      struct ftrace_regs *fregs)
+{
+	struct unwindme *u = (struct unwindme *)fregs->regs.regs[0];
+
+	u->ret = test_unwind(current,
+			     (u->flags & UWM_REGS) ? &fregs->regs : NULL);
+}
+
+static noinline int test_unwind_ftraced_func(struct unwindme *u)
+{
+	return READ_ONCE(u)->ret;
+}
+
+static int test_unwind_ftrace(struct unwindme *u)
+{
+	int ret;
+#ifdef CONFIG_DYNAMIC_FTRACE
+	struct ftrace_ops *fops;
+
+	fops = kunit_kzalloc(current_test, sizeof(*fops), GFP_KERNEL);
+	fops->func = test_unwind_ftrace_handler;
+	fops->flags = FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_RECURSION |
+		      FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_PERMANENT;
+#else
+	kunit_skip(current_test, "requires CONFIG_DYNAMIC_FTRACE");
+#endif
+
+	ret = ftrace_set_filter_ip(
+		fops, (unsigned long)test_unwind_ftraced_func, 0, 0);
+	if (ret) {
+		kunit_err(current_test, "failed to set ftrace filter (%d)\n",
+			  ret);
+		return -1;
+	}
+
+	ret = register_ftrace_function(fops);
+	if (!ret) {
+		ret = test_unwind_ftraced_func(u);
+		unregister_ftrace_function(fops);
+	} else {
+		kunit_err(current_test,
+			  "failed to register ftrace handler (%d)\n", ret);
+	}
+
+	ftrace_set_filter_ip(fops, (unsigned long)test_unwind_ftraced_func, 1,
+			     0);
+	return ret;
+}
+
+/* This function may or may not appear in the backtrace. */
+static noinline int unwindme_func4(struct unwindme *u, struct pt_regs *regs)
+{
+	if (!(u->flags & UWM_CALLER))
+		*regs = fake_pt_regs();
+	if (u->flags & UWM_THREAD) {
+		complete(&u->task_ready);
+		wait_event(u->task_wq, kthread_should_park());
+		kthread_parkme();
+		return 0;
+	} else if (u->flags & (UWM_KPROBE_ON_FTRACE)) {
+		return test_unwind_kprobe(u);
+	} else if (u->flags & (UWM_KRETPROBE | UWM_KRETPROBE_HANDLER)) {
+		return test_unwind_kretprobe(u);
+	} else if (u->flags & UWM_FTRACE) {
+		return test_unwind_ftrace(u);
+	} else {
+		return test_unwind(current, (u->flags & UWM_REGS) ? regs : NULL);
+	}
+}
+
+/* This function may or may not appear in the backtrace. */
+static noinline int unwindme_func3(struct unwindme *u)
+{
+	struct pt_regs regs;
+
+	regs = fake_pt_regs();
+	return unwindme_func4(u, &regs);
+}
+
+/* This function must appear in the backtrace. */
+static noinline int unwindme_func2(struct unwindme *u)
+{
+	return unwindme_func3(u);
+}
+
+/* This function must follow unwindme_func2 in the backtrace. */
+static noinline int unwindme_func1(void *u)
+{
+	return unwindme_func2((struct unwindme *)u);
+}
+
+static void unwindme_timer_fn(struct timer_list *unused)
+{
+	struct unwindme *u = READ_ONCE(unwindme);
+
+	if (u) {
+		unwindme = NULL;
+		u->task = NULL;
+		u->ret = unwindme_func1(u);
+		complete(&u->task_ready);
+	}
+}
+
+static struct timer_list unwind_timer;
+
+static int test_unwind_irq(struct unwindme *u)
+{
+	unwindme = u;
+	init_completion(&u->task_ready);
+	timer_setup(&unwind_timer, unwindme_timer_fn, 0);
+	mod_timer(&unwind_timer, jiffies + 1);
+	wait_for_completion(&u->task_ready);
+	return u->ret;
+}
+
+/* Spawns a task and passes it to test_unwind(). */
+static int test_unwind_task(struct unwindme *u)
+{
+	struct task_struct *task;
+	int ret;
+
+	/* Initialize thread-related fields. */
+	init_completion(&u->task_ready);
+	init_waitqueue_head(&u->task_wq);
+
+	/*
+	 * Start the task and wait until it reaches unwindme_func4() and sleeps
+	 * in (task_ready, unwind_done] range.
+	 */
+	task = kthread_run(unwindme_func1, u, "test_unwind_task");
+	if (IS_ERR(task)) {
+		kunit_err(current_test, "kthread_run() failed\n");
+		return PTR_ERR(task);
+	}
+
+	/*
+	 * Make sure task reaches unwindme_func4 before parking it,
+	 * we might park it before kthread function has been executed otherwise
+	 */
+	wait_for_completion(&u->task_ready);
+	kthread_park(task);
+	/* Unwind. */
+	ret = test_unwind(task, NULL);
+	kthread_stop(task);
+	return ret;
+}
+
+struct test_params {
+	int flags;
+	char *name;
+};
+
+/*
+ * Create required parameter list for tests
+ */
+#define TEST_WITH_FLAGS(f)	{ .flags = f, .name = #f }
+static const struct test_params param_list[] = {
+	TEST_WITH_FLAGS(UWM_DEFAULT),
+	TEST_WITH_FLAGS(UWM_REGS),
+	TEST_WITH_FLAGS(UWM_CALLER),
+	TEST_WITH_FLAGS(UWM_CALLER | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_THREAD),
+	TEST_WITH_FLAGS(UWM_THREAD | UWM_CALLER),
+	TEST_WITH_FLAGS(UWM_IRQ),
+	TEST_WITH_FLAGS(UWM_IRQ | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_IRQ | UWM_CALLER),
+	TEST_WITH_FLAGS(UWM_IRQ | UWM_CALLER | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_REGS),
+	TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE),
+	TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_FTRACE),
+	TEST_WITH_FLAGS(UWM_FTRACE | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_KRETPROBE),
+	TEST_WITH_FLAGS(UWM_KRETPROBE | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_KRETPROBE_HANDLER),
+	TEST_WITH_FLAGS(UWM_KRETPROBE_HANDLER | UWM_REGS),
+};
+
+/*
+ * Parameter description generator: required for KUNIT_ARRAY_PARAM()
+ */
+static void get_desc(const struct test_params *params, char *desc)
+{
+	strscpy(desc, params->name, KUNIT_PARAM_DESC_SIZE);
+}
+
+/*
+ * Create test_unwind_gen_params
+ */
+KUNIT_ARRAY_PARAM(test_unwind, param_list, get_desc);
+
+static void test_unwind_flags(struct kunit *test)
+{
+	struct unwindme u;
+	const struct test_params *params;
+
+	current_test = test;
+	params = (const struct test_params *)test->param_value;
+	u.flags = params->flags;
+	if (u.flags & UWM_THREAD)
+		KUNIT_EXPECT_EQ(test, 0, test_unwind_task(&u));
+	else if (u.flags & UWM_IRQ)
+		KUNIT_EXPECT_EQ(test, 0, test_unwind_irq(&u));
+	else
+		KUNIT_EXPECT_EQ(test, 0, unwindme_func1(&u));
+}
+
+static struct kunit_case unwind_test_cases[] = {
+	KUNIT_CASE_PARAM(test_unwind_flags, test_unwind_gen_params),
+	{}
+};
+
+static struct kunit_suite test_unwind_suite = {
+	.name = "test_unwind",
+	.test_cases = unwind_test_cases,
+};
+
+kunit_test_suites(&test_unwind_suite);
+
+MODULE_LICENSE("GPL");
diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h
index 97455880ac41..3262c6875176 100644
--- a/include/linux/stacktrace.h
+++ b/include/linux/stacktrace.h
@@ -60,6 +60,7 @@ int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, void *cookie,
 
 void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
 			  const struct pt_regs *regs);
+
 #endif /* CONFIG_ARCH_STACKWALK */
 
 #ifdef CONFIG_STACKTRACE
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 1/1] arm64: Add initial set of stack unwinder self tests
@ 2022-06-24 14:10   ` Ivan T. Ivanov
  0 siblings, 0 replies; 10+ messages in thread
From: Ivan T. Ivanov @ 2022-06-24 14:10 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon
  Cc: Mark Brown, Mark Rutland, Madhavan T . Venkataraman,
	linux-arm-kernel, linux-kernel, Ivan T. Ivanov

Add kunit tests for obvious cases where stack unwind could be needed.
Like these:

 * Unwind a separate task
 * Unwind starting from caller
 * Unwind from irq context
 * Unwind from kprobe handler called via ftrace
 * Unwind from ftrace handler
 * Unwind through kretprobed function
 * Unwind from kretprobe handler

Tests are completely based on code used in s390 unwinder tests.
Cases which where not relevant to aarch64 where removed and
some places where adjusted to address aarch64 specifics.

Cc: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>
Signed-off-by: Ivan T. Ivanov <iivanov@suse.de>
---
 arch/arm64/Kconfig.debug        |  12 +
 arch/arm64/kernel/Makefile      |   1 +
 arch/arm64/kernel/stacktrace.c  |  23 ++
 arch/arm64/kernel/test_unwind.c | 518 ++++++++++++++++++++++++++++++++
 include/linux/stacktrace.h      |   1 +
 5 files changed, 555 insertions(+)
 create mode 100644 arch/arm64/kernel/test_unwind.c

diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index 265c4461031f..a48ab884754e 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -20,4 +20,16 @@ config ARM64_RELOC_TEST
 	depends on m
 	tristate "Relocation testing module"
 
+config ARM64_UNWIND_SELFTEST
+	def_tristate n
+	depends on KUNIT
+	default KUNIT_ALL_TESTS
+	prompt "Test unwind functions"
+	help
+	  This option enables aarch64 specific stack unwinder testing kernel
+	  module. This option is not useful for distributions or general
+	  kernels, but only for kernel developers working on architecture code.
+
+	  Say N if you are unsure.
+
 source "drivers/hwtracing/coresight/Kconfig"
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index fa7981d0d917..014da14dd25c 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_ARM64_PTR_AUTH)		+= pointer_auth.o
 obj-$(CONFIG_ARM64_MTE)			+= mte.o
 obj-y					+= vdso-wrap.o
 obj-$(CONFIG_COMPAT_VDSO)		+= vdso32-wrap.o
+obj-$(CONFIG_ARM64_UNWIND_SELFTEST)	+= test_unwind.o
 
 # Force dependency (vdso*-wrap.S includes vdso.so through incbin)
 $(obj)/vdso-wrap.o: $(obj)/vdso/vdso.so
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index 8016ba0e2c96..6dc39b356cc2 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -406,3 +406,26 @@ noinline int notrace arch_stack_walk_reliable(
 	reliable = unwind(&state, consume_entry, cookie);
 	return reliable ? 0 : -EINVAL;
 }
+
+#if IS_ENABLED(CONFIG_ARM64_UNWIND_SELFTEST)
+noinline notrace int test_arch_stack_walk(stack_trace_consume_fn consume_entry,
+					  void *cookie,
+					  struct task_struct *task,
+					  struct pt_regs *regs)
+{
+	struct unwind_state state;
+
+	if (regs) {
+		if (task != current)
+			return -EINVAL;
+		unwind_init_from_regs(&state, regs);
+	} else if (task == current) {
+		unwind_init_from_caller(&state);
+	} else {
+		unwind_init_from_task(&state, task);
+	}
+
+	return unwind(&state, consume_entry, cookie) ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(test_arch_stack_walk);
+#endif
diff --git a/arch/arm64/kernel/test_unwind.c b/arch/arm64/kernel/test_unwind.c
new file mode 100644
index 000000000000..fc09400f3d12
--- /dev/null
+++ b/arch/arm64/kernel/test_unwind.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test module for aarch64 stack unwinder functionality.
+ * Based on s390 unwinder tests.
+ */
+
+#include <kunit/test.h>
+#include <linux/completion.h>
+#include <linux/kallsyms.h>
+#include <linux/kthread.h>
+#include <linux/ftrace.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/kprobes.h>
+#include <linux/wait.h>
+#include <asm/irq.h>
+#include <asm/stacktrace.h>
+
+static struct kunit *current_test;
+
+#define BT_BUF_SIZE (PAGE_SIZE * 4)
+
+static bool force_bt;
+module_param_named(backtrace, force_bt, bool, 0444);
+MODULE_PARM_DESC(backtrace, "print backtraces for all tests");
+
+struct unwind_tracker {
+	int frame_count;
+	int prev_is_func2;
+	int seen_func2_func1;
+	int seen_kretprobe_trampoline;
+	char *bt_buff;
+	size_t bt_pos;
+};
+
+/*
+ * To avoid printk line limit split backtrace by lines
+ */
+static void print_backtrace(char *bt)
+{
+	char *p;
+
+	while (true) {
+		p = strsep(&bt, "\n");
+		if (!p)
+			break;
+		kunit_err(current_test, "%s\n", p);
+	}
+}
+
+static bool check_backtrace_entry(void *arg, unsigned long addr)
+{
+	struct unwind_tracker *tracker = arg;
+	const int max_frames = 128;
+	char sym[KSYM_SYMBOL_LEN];
+
+	if (tracker->frame_count++ == max_frames) {
+		kunit_err(current_test, "stack too deep\n");
+		return false;
+	}
+
+	if (!addr) {
+		kunit_err(current_test, "addr is 0\n");
+		return false;
+	}
+
+	sprint_symbol(sym, addr);
+	if (tracker->prev_is_func2 && str_has_prefix(sym, "unwindme_func1"))
+		tracker->seen_func2_func1 = 1;
+
+	tracker->prev_is_func2 = str_has_prefix(sym, "unwindme_func2");
+
+	if (str_has_prefix(sym, "__kretprobe_trampoline+0x0/"))
+		tracker->seen_kretprobe_trampoline = 1;
+
+	if (tracker->bt_pos >= BT_BUF_SIZE) {
+		kunit_err(current_test, "backtrace buffer is too small\n");
+		return true;
+	}
+
+	tracker->bt_pos += snprintf(tracker->bt_buff + tracker->bt_pos,
+				    BT_BUF_SIZE - tracker->bt_pos, " %pSR\n",
+				    (void *)addr);
+	return true;
+}
+
+int test_arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
+			 struct task_struct *task, struct pt_regs *regs);
+
+/*
+ * Calls test_arch_stack_walk() which is handy wrapper of aarch64 unwind
+ * functionality, and verifies that the result contains unwindme_func2
+ *followed by unwindme_func1.
+ */
+static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs)
+{
+	struct unwind_tracker tracker;
+	int ret = 0;
+
+	/* Unwind. */
+	tracker.frame_count = 0;
+	tracker.prev_is_func2 = 0;
+	tracker.seen_func2_func1 = 0;
+	tracker.seen_kretprobe_trampoline = 0;
+
+	tracker.bt_pos = 0;
+	tracker.bt_buff = kmalloc(BT_BUF_SIZE, GFP_ATOMIC);
+	if (!tracker.bt_buff) {
+		kunit_err(current_test,
+			  "failed to allocate backtrace buffer\n");
+		return -ENOMEM;
+	}
+
+	ret = test_arch_stack_walk(check_backtrace_entry, &tracker, task, regs);
+
+	if (!tracker.seen_func2_func1) {
+		kunit_err(current_test,
+			  "unwindme_func2 and unwindme_func1 not found\n");
+		ret = -EINVAL;
+	}
+	if (tracker.frame_count == 128) {
+		kunit_err(current_test, "Maximum number of frames exceeded\n");
+		ret = -EINVAL;
+	}
+	if (tracker.seen_kretprobe_trampoline) {
+		kunit_err(current_test,
+			  "__kretprobe_trampoline+0x0 in unwinding results\n");
+		ret = -EINVAL;
+	}
+
+	if (ret || force_bt)
+		print_backtrace(tracker.bt_buff);
+
+	kfree(tracker.bt_buff);
+	return ret;
+}
+
+/* State of the task being unwound. */
+struct unwindme {
+	int flags;
+	int ret;
+	struct task_struct *task;
+	struct completion task_ready;
+	wait_queue_head_t task_wq;
+};
+
+static struct unwindme *unwindme;
+
+/* Values of unwindme.flags. */
+#define UWM_DEFAULT		0x0
+#define UWM_THREAD		0x1	/* Unwind a separate task. */
+#define UWM_REGS		0x2	/* Pass regs to test_unwind(). */
+#define UWM_CALLER		0x8	/* Unwind starting from caller. */
+#define UWM_IRQ			0x20	/* Unwind from irq context. */
+#define UWM_KPROBE_ON_FTRACE	0x80	/* Unwind from kprobe handler called via ftrace. */
+#define UWM_FTRACE		0x100	/* Unwind from ftrace handler. */
+#define UWM_KRETPROBE		0x200	/* Unwind through kretprobed function. */
+#define UWM_KRETPROBE_HANDLER	0x400	/* Unwind from kretprobe handler. */
+
+static notrace noinline struct pt_regs fake_pt_regs(void)
+{
+	struct pt_regs regs;
+
+	memset(&regs, 0, sizeof(regs));
+
+	regs.regs[29] = (unsigned long)__builtin_frame_address(1);
+	regs.pc = (unsigned long)__builtin_return_address(0);
+
+	return regs;
+}
+
+static int kretprobe_ret_handler(struct kretprobe_instance *ri,
+				 struct pt_regs *regs)
+{
+	struct unwindme *u = unwindme;
+
+	if (!(u->flags & UWM_KRETPROBE_HANDLER))
+		return 0;
+
+	u->ret = test_unwind(current, (u->flags & UWM_REGS) ? regs : NULL);
+
+	return 0;
+}
+
+static noinline notrace int test_unwind_kretprobed_func(struct unwindme *u)
+{
+	struct pt_regs regs;
+
+	if (!(u->flags & UWM_KRETPROBE))
+		return 0;
+
+	regs = fake_pt_regs();
+	return test_unwind(current, (u->flags & UWM_REGS) ? &regs : NULL);
+}
+
+static noinline int test_unwind_kretprobed_func_caller(struct unwindme *u)
+{
+	return test_unwind_kretprobed_func(u);
+}
+
+static int test_unwind_kretprobe(struct unwindme *u)
+{
+	int ret;
+	struct kretprobe my_kretprobe;
+
+	if (!IS_ENABLED(CONFIG_KPROBES))
+		kunit_skip(current_test, "requires CONFIG_KPROBES");
+
+	u->ret = -1; /* make sure kprobe is called */
+	unwindme = u;
+
+	memset(&my_kretprobe, 0, sizeof(my_kretprobe));
+	my_kretprobe.handler = kretprobe_ret_handler;
+	my_kretprobe.maxactive = 1;
+	my_kretprobe.kp.addr = (kprobe_opcode_t *)test_unwind_kretprobed_func;
+
+	ret = register_kretprobe(&my_kretprobe);
+
+	if (ret < 0) {
+		kunit_err(current_test, "register_kretprobe failed %d\n", ret);
+		return -EINVAL;
+	}
+
+	ret = test_unwind_kretprobed_func_caller(u);
+	unregister_kretprobe(&my_kretprobe);
+	unwindme = NULL;
+	if (u->flags & UWM_KRETPROBE_HANDLER)
+		ret = u->ret;
+	return ret;
+}
+
+static int kprobe_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+	struct unwindme *u = unwindme;
+
+	u->ret = test_unwind(current, (u->flags & UWM_REGS) ? regs : NULL);
+	return 0;
+}
+
+extern const char test_unwind_kprobed_insn[];
+
+static noinline void test_unwind_kprobed_func(void)
+{
+	asm volatile("	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "test_unwind_kprobed_insn:\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     "	nop\n"
+		     :);
+}
+
+static int test_unwind_kprobe(struct unwindme *u)
+{
+	struct kprobe kp;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_KPROBES))
+		kunit_skip(current_test, "requires CONFIG_KPROBES");
+	if (!IS_ENABLED(CONFIG_KPROBES_ON_FTRACE) &&
+	    u->flags & UWM_KPROBE_ON_FTRACE)
+		kunit_skip(current_test, "requires CONFIG_KPROBES_ON_FTRACE");
+
+	u->ret = -1; /* make sure kprobe is called */
+	unwindme = u;
+	memset(&kp, 0, sizeof(kp));
+	kp.pre_handler = kprobe_pre_handler;
+	kp.addr = u->flags & UWM_KPROBE_ON_FTRACE ?
+			  (kprobe_opcode_t *)test_unwind_kprobed_func :
+			  (kprobe_opcode_t *)test_unwind_kprobed_insn;
+	ret = register_kprobe(&kp);
+	if (ret < 0) {
+		kunit_err(current_test, "register_kprobe failed %d\n", ret);
+		return -EINVAL;
+	}
+
+	test_unwind_kprobed_func();
+	unregister_kprobe(&kp);
+	unwindme = NULL;
+	return u->ret;
+}
+
+static void notrace __used test_unwind_ftrace_handler(unsigned long ip,
+						      unsigned long parent_ip,
+						      struct ftrace_ops *fops,
+						      struct ftrace_regs *fregs)
+{
+	struct unwindme *u = (struct unwindme *)fregs->regs.regs[0];
+
+	u->ret = test_unwind(current,
+			     (u->flags & UWM_REGS) ? &fregs->regs : NULL);
+}
+
+static noinline int test_unwind_ftraced_func(struct unwindme *u)
+{
+	return READ_ONCE(u)->ret;
+}
+
+static int test_unwind_ftrace(struct unwindme *u)
+{
+	int ret;
+#ifdef CONFIG_DYNAMIC_FTRACE
+	struct ftrace_ops *fops;
+
+	fops = kunit_kzalloc(current_test, sizeof(*fops), GFP_KERNEL);
+	fops->func = test_unwind_ftrace_handler;
+	fops->flags = FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_RECURSION |
+		      FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_PERMANENT;
+#else
+	kunit_skip(current_test, "requires CONFIG_DYNAMIC_FTRACE");
+#endif
+
+	ret = ftrace_set_filter_ip(
+		fops, (unsigned long)test_unwind_ftraced_func, 0, 0);
+	if (ret) {
+		kunit_err(current_test, "failed to set ftrace filter (%d)\n",
+			  ret);
+		return -1;
+	}
+
+	ret = register_ftrace_function(fops);
+	if (!ret) {
+		ret = test_unwind_ftraced_func(u);
+		unregister_ftrace_function(fops);
+	} else {
+		kunit_err(current_test,
+			  "failed to register ftrace handler (%d)\n", ret);
+	}
+
+	ftrace_set_filter_ip(fops, (unsigned long)test_unwind_ftraced_func, 1,
+			     0);
+	return ret;
+}
+
+/* This function may or may not appear in the backtrace. */
+static noinline int unwindme_func4(struct unwindme *u, struct pt_regs *regs)
+{
+	if (!(u->flags & UWM_CALLER))
+		*regs = fake_pt_regs();
+	if (u->flags & UWM_THREAD) {
+		complete(&u->task_ready);
+		wait_event(u->task_wq, kthread_should_park());
+		kthread_parkme();
+		return 0;
+	} else if (u->flags & (UWM_KPROBE_ON_FTRACE)) {
+		return test_unwind_kprobe(u);
+	} else if (u->flags & (UWM_KRETPROBE | UWM_KRETPROBE_HANDLER)) {
+		return test_unwind_kretprobe(u);
+	} else if (u->flags & UWM_FTRACE) {
+		return test_unwind_ftrace(u);
+	} else {
+		return test_unwind(current, (u->flags & UWM_REGS) ? regs : NULL);
+	}
+}
+
+/* This function may or may not appear in the backtrace. */
+static noinline int unwindme_func3(struct unwindme *u)
+{
+	struct pt_regs regs;
+
+	regs = fake_pt_regs();
+	return unwindme_func4(u, &regs);
+}
+
+/* This function must appear in the backtrace. */
+static noinline int unwindme_func2(struct unwindme *u)
+{
+	return unwindme_func3(u);
+}
+
+/* This function must follow unwindme_func2 in the backtrace. */
+static noinline int unwindme_func1(void *u)
+{
+	return unwindme_func2((struct unwindme *)u);
+}
+
+static void unwindme_timer_fn(struct timer_list *unused)
+{
+	struct unwindme *u = READ_ONCE(unwindme);
+
+	if (u) {
+		unwindme = NULL;
+		u->task = NULL;
+		u->ret = unwindme_func1(u);
+		complete(&u->task_ready);
+	}
+}
+
+static struct timer_list unwind_timer;
+
+static int test_unwind_irq(struct unwindme *u)
+{
+	unwindme = u;
+	init_completion(&u->task_ready);
+	timer_setup(&unwind_timer, unwindme_timer_fn, 0);
+	mod_timer(&unwind_timer, jiffies + 1);
+	wait_for_completion(&u->task_ready);
+	return u->ret;
+}
+
+/* Spawns a task and passes it to test_unwind(). */
+static int test_unwind_task(struct unwindme *u)
+{
+	struct task_struct *task;
+	int ret;
+
+	/* Initialize thread-related fields. */
+	init_completion(&u->task_ready);
+	init_waitqueue_head(&u->task_wq);
+
+	/*
+	 * Start the task and wait until it reaches unwindme_func4() and sleeps
+	 * in (task_ready, unwind_done] range.
+	 */
+	task = kthread_run(unwindme_func1, u, "test_unwind_task");
+	if (IS_ERR(task)) {
+		kunit_err(current_test, "kthread_run() failed\n");
+		return PTR_ERR(task);
+	}
+
+	/*
+	 * Make sure task reaches unwindme_func4 before parking it,
+	 * we might park it before kthread function has been executed otherwise
+	 */
+	wait_for_completion(&u->task_ready);
+	kthread_park(task);
+	/* Unwind. */
+	ret = test_unwind(task, NULL);
+	kthread_stop(task);
+	return ret;
+}
+
+struct test_params {
+	int flags;
+	char *name;
+};
+
+/*
+ * Create required parameter list for tests
+ */
+#define TEST_WITH_FLAGS(f)	{ .flags = f, .name = #f }
+static const struct test_params param_list[] = {
+	TEST_WITH_FLAGS(UWM_DEFAULT),
+	TEST_WITH_FLAGS(UWM_REGS),
+	TEST_WITH_FLAGS(UWM_CALLER),
+	TEST_WITH_FLAGS(UWM_CALLER | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_THREAD),
+	TEST_WITH_FLAGS(UWM_THREAD | UWM_CALLER),
+	TEST_WITH_FLAGS(UWM_IRQ),
+	TEST_WITH_FLAGS(UWM_IRQ | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_IRQ | UWM_CALLER),
+	TEST_WITH_FLAGS(UWM_IRQ | UWM_CALLER | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_REGS),
+	TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE),
+	TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_FTRACE),
+	TEST_WITH_FLAGS(UWM_FTRACE | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_KRETPROBE),
+	TEST_WITH_FLAGS(UWM_KRETPROBE | UWM_REGS),
+	TEST_WITH_FLAGS(UWM_KRETPROBE_HANDLER),
+	TEST_WITH_FLAGS(UWM_KRETPROBE_HANDLER | UWM_REGS),
+};
+
+/*
+ * Parameter description generator: required for KUNIT_ARRAY_PARAM()
+ */
+static void get_desc(const struct test_params *params, char *desc)
+{
+	strscpy(desc, params->name, KUNIT_PARAM_DESC_SIZE);
+}
+
+/*
+ * Create test_unwind_gen_params
+ */
+KUNIT_ARRAY_PARAM(test_unwind, param_list, get_desc);
+
+static void test_unwind_flags(struct kunit *test)
+{
+	struct unwindme u;
+	const struct test_params *params;
+
+	current_test = test;
+	params = (const struct test_params *)test->param_value;
+	u.flags = params->flags;
+	if (u.flags & UWM_THREAD)
+		KUNIT_EXPECT_EQ(test, 0, test_unwind_task(&u));
+	else if (u.flags & UWM_IRQ)
+		KUNIT_EXPECT_EQ(test, 0, test_unwind_irq(&u));
+	else
+		KUNIT_EXPECT_EQ(test, 0, unwindme_func1(&u));
+}
+
+static struct kunit_case unwind_test_cases[] = {
+	KUNIT_CASE_PARAM(test_unwind_flags, test_unwind_gen_params),
+	{}
+};
+
+static struct kunit_suite test_unwind_suite = {
+	.name = "test_unwind",
+	.test_cases = unwind_test_cases,
+};
+
+kunit_test_suites(&test_unwind_suite);
+
+MODULE_LICENSE("GPL");
diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h
index 97455880ac41..3262c6875176 100644
--- a/include/linux/stacktrace.h
+++ b/include/linux/stacktrace.h
@@ -60,6 +60,7 @@ int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, void *cookie,
 
 void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
 			  const struct pt_regs *regs);
+
 #endif /* CONFIG_ARCH_STACKWALK */
 
 #ifdef CONFIG_STACKTRACE
-- 
2.35.3


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] arm64: Add initial set of stack unwinder self tests
  2022-06-24 14:10   ` Ivan T. Ivanov
@ 2022-06-29 15:59     ` Mark Brown
  -1 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2022-06-29 15:59 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Catalin Marinas, Will Deacon, Mark Rutland,
	Madhavan T . Venkataraman, linux-arm-kernel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1476 bytes --]

On Fri, Jun 24, 2022 at 05:10:00PM +0300, Ivan T. Ivanov wrote:
> Add kunit tests for obvious cases where stack unwind could be needed.
> Like these:
> 
>  * Unwind a separate task
>  * Unwind starting from caller
>  * Unwind from irq context
>  * Unwind from kprobe handler called via ftrace
>  * Unwind from ftrace handler
>  * Unwind through kretprobed function
>  * Unwind from kretprobe handler
> 
> Tests are completely based on code used in s390 unwinder tests.
> Cases which where not relevant to aarch64 where removed and
> some places where adjusted to address aarch64 specifics.

I think this would be a bit easier to digest if it were a series which
builds things up with the test cases in individual patches, or at least
things like ftrace and kprobes split out a bit more, rather than every
single test all at once.  I've got a few *very* superficial comments
below, I think the code is fine but there's several moving pieces to
check.

> +/*
> + * Calls test_arch_stack_walk() which is handy wrapper of aarch64 unwind
> + * functionality, and verifies that the result contains unwindme_func2
> + *followed by unwindme_func1.

Missing space.

> +	ret = register_ftrace_function(fops);
> +	if (!ret) {
> +		ret = test_unwind_ftraced_func(u);
> +		unregister_ftrace_function(fops);
> +	} else {
> +		kunit_err(current_test,
> +			  "failed to register ftrace handler (%d)\n", ret);
> +	}

Shouldn't we return an error here?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] arm64: Add initial set of stack unwinder self tests
@ 2022-06-29 15:59     ` Mark Brown
  0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2022-06-29 15:59 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Catalin Marinas, Will Deacon, Mark Rutland,
	Madhavan T . Venkataraman, linux-arm-kernel, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1476 bytes --]

On Fri, Jun 24, 2022 at 05:10:00PM +0300, Ivan T. Ivanov wrote:
> Add kunit tests for obvious cases where stack unwind could be needed.
> Like these:
> 
>  * Unwind a separate task
>  * Unwind starting from caller
>  * Unwind from irq context
>  * Unwind from kprobe handler called via ftrace
>  * Unwind from ftrace handler
>  * Unwind through kretprobed function
>  * Unwind from kretprobe handler
> 
> Tests are completely based on code used in s390 unwinder tests.
> Cases which where not relevant to aarch64 where removed and
> some places where adjusted to address aarch64 specifics.

I think this would be a bit easier to digest if it were a series which
builds things up with the test cases in individual patches, or at least
things like ftrace and kprobes split out a bit more, rather than every
single test all at once.  I've got a few *very* superficial comments
below, I think the code is fine but there's several moving pieces to
check.

> +/*
> + * Calls test_arch_stack_walk() which is handy wrapper of aarch64 unwind
> + * functionality, and verifies that the result contains unwindme_func2
> + *followed by unwindme_func1.

Missing space.

> +	ret = register_ftrace_function(fops);
> +	if (!ret) {
> +		ret = test_unwind_ftraced_func(u);
> +		unregister_ftrace_function(fops);
> +	} else {
> +		kunit_err(current_test,
> +			  "failed to register ftrace handler (%d)\n", ret);
> +	}

Shouldn't we return an error here?

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] arm64: Add initial set of stack unwinder self tests
  2022-06-29 15:59     ` Mark Brown
@ 2022-06-30  6:55       ` Ivan T. Ivanov
  -1 siblings, 0 replies; 10+ messages in thread
From: Ivan T. Ivanov @ 2022-06-30  6:55 UTC (permalink / raw)
  To: Mark Brown
  Cc: Catalin Marinas, Will Deacon, Mark Rutland,
	Madhavan T . Venkataraman, linux-arm-kernel, linux-kernel

Hi,

On 06-29 16:59, Mark Brown wrote:
> 
> On Fri, Jun 24, 2022 at 05:10:00PM +0300, Ivan T. Ivanov wrote:
> > Add kunit tests for obvious cases where stack unwind could be needed.
> > Like these:
> > 
> >  * Unwind a separate task
> >  * Unwind starting from caller
> >  * Unwind from irq context
> >  * Unwind from kprobe handler called via ftrace
> >  * Unwind from ftrace handler
> >  * Unwind through kretprobed function
> >  * Unwind from kretprobe handler
> > 
> > Tests are completely based on code used in s390 unwinder tests.
> > Cases which where not relevant to aarch64 where removed and
> > some places where adjusted to address aarch64 specifics.
> 
> I think this would be a bit easier to digest if it were a series which
> builds things up with the test cases in individual patches, or at least
> things like ftrace and kprobes split out a bit more, rather than every
> single test all at once.  I've got a few *very* superficial comments
> below, I think the code is fine but there's several moving pieces to
> check.

Ok. I will split and resend.

> 
> > +/*
> > + * Calls test_arch_stack_walk() which is handy wrapper of aarch64 unwind
> > + * functionality, and verifies that the result contains unwindme_func2
> > + *followed by unwindme_func1.
> 
> Missing space.

Sure.

> 
> > +	ret = register_ftrace_function(fops);
> > +	if (!ret) {
> > +		ret = test_unwind_ftraced_func(u);
> > +		unregister_ftrace_function(fops);
> > +	} else {
> > +		kunit_err(current_test,
> > +			  "failed to register ftrace handler (%d)\n", ret);
> > +	}
> 
> Shouldn't we return an error here?

Error will be returned once we remove test_unwind_ftraced_func
address from ftrace filters.

Regards,
Ivan


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] arm64: Add initial set of stack unwinder self tests
@ 2022-06-30  6:55       ` Ivan T. Ivanov
  0 siblings, 0 replies; 10+ messages in thread
From: Ivan T. Ivanov @ 2022-06-30  6:55 UTC (permalink / raw)
  To: Mark Brown
  Cc: Catalin Marinas, Will Deacon, Mark Rutland,
	Madhavan T . Venkataraman, linux-arm-kernel, linux-kernel

Hi,

On 06-29 16:59, Mark Brown wrote:
> 
> On Fri, Jun 24, 2022 at 05:10:00PM +0300, Ivan T. Ivanov wrote:
> > Add kunit tests for obvious cases where stack unwind could be needed.
> > Like these:
> > 
> >  * Unwind a separate task
> >  * Unwind starting from caller
> >  * Unwind from irq context
> >  * Unwind from kprobe handler called via ftrace
> >  * Unwind from ftrace handler
> >  * Unwind through kretprobed function
> >  * Unwind from kretprobe handler
> > 
> > Tests are completely based on code used in s390 unwinder tests.
> > Cases which where not relevant to aarch64 where removed and
> > some places where adjusted to address aarch64 specifics.
> 
> I think this would be a bit easier to digest if it were a series which
> builds things up with the test cases in individual patches, or at least
> things like ftrace and kprobes split out a bit more, rather than every
> single test all at once.  I've got a few *very* superficial comments
> below, I think the code is fine but there's several moving pieces to
> check.

Ok. I will split and resend.

> 
> > +/*
> > + * Calls test_arch_stack_walk() which is handy wrapper of aarch64 unwind
> > + * functionality, and verifies that the result contains unwindme_func2
> > + *followed by unwindme_func1.
> 
> Missing space.

Sure.

> 
> > +	ret = register_ftrace_function(fops);
> > +	if (!ret) {
> > +		ret = test_unwind_ftraced_func(u);
> > +		unregister_ftrace_function(fops);
> > +	} else {
> > +		kunit_err(current_test,
> > +			  "failed to register ftrace handler (%d)\n", ret);
> > +	}
> 
> Shouldn't we return an error here?

Error will be returned once we remove test_unwind_ftraced_func
address from ftrace filters.

Regards,
Ivan


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/1] arm64: Add stack unwinder unit tests
  2022-06-24 14:09 ` Ivan T. Ivanov
@ 2022-07-06 12:31   ` Will Deacon
  -1 siblings, 0 replies; 10+ messages in thread
From: Will Deacon @ 2022-07-06 12:31 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Catalin Marinas, Mark Brown, Mark Rutland,
	Madhavan T . Venkataraman, linux-arm-kernel, linux-kernel

On Fri, Jun 24, 2022 at 05:09:59PM +0300, Ivan T. Ivanov wrote:
> Here is my initial arm64 unwinder test support. Code is based on s390
> unwinder tests, while errors introduced are mine. I am hopping that it
> will be useful for testing and validating upcoming arm64
> arch_stack_walk_reliable() support.

Thanks for doing this! Tests are always welcome.

Since you based this on the s390 code, how much scope is there for moving
the tests out of the arch/ directories and unifying them somewhere like
kselftest or lkdtm so that other archs can benefit too?

Will

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/1] arm64: Add stack unwinder unit tests
@ 2022-07-06 12:31   ` Will Deacon
  0 siblings, 0 replies; 10+ messages in thread
From: Will Deacon @ 2022-07-06 12:31 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Catalin Marinas, Mark Brown, Mark Rutland,
	Madhavan T . Venkataraman, linux-arm-kernel, linux-kernel

On Fri, Jun 24, 2022 at 05:09:59PM +0300, Ivan T. Ivanov wrote:
> Here is my initial arm64 unwinder test support. Code is based on s390
> unwinder tests, while errors introduced are mine. I am hopping that it
> will be useful for testing and validating upcoming arm64
> arch_stack_walk_reliable() support.

Thanks for doing this! Tests are always welcome.

Since you based this on the s390 code, how much scope is there for moving
the tests out of the arch/ directories and unifying them somewhere like
kselftest or lkdtm so that other archs can benefit too?

Will

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2022-07-06 12:32 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-24 14:09 [PATCH 0/1] arm64: Add stack unwinder unit tests Ivan T. Ivanov
2022-06-24 14:09 ` Ivan T. Ivanov
2022-06-24 14:10 ` [PATCH 1/1] arm64: Add initial set of stack unwinder self tests Ivan T. Ivanov
2022-06-24 14:10   ` Ivan T. Ivanov
2022-06-29 15:59   ` Mark Brown
2022-06-29 15:59     ` Mark Brown
2022-06-30  6:55     ` Ivan T. Ivanov
2022-06-30  6:55       ` Ivan T. Ivanov
2022-07-06 12:31 ` [PATCH 0/1] arm64: Add stack unwinder unit tests Will Deacon
2022-07-06 12:31   ` Will Deacon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.