Subject: Re: [PATCH] powerpc64/modules: fix ool-ftrace-stub vs. livepatch relocation corruption

From: Joe Lawrence <[email-masked]>
Date: Wed, 3 Sep 2025 22:37:39 -0400

On Wed, Sep 03, 2025 at 10:29:50PM -0400, Joe Lawrence wrote:
> The powerpc64 module .stubs section holds ppc64_stub_entry[] code
> trampolines that are generated at module load time. These stubs are
> necessary for function calls to external symbols that are too far away
> for a simple relative branch.
> 
> Logic for finding an available ppc64_stub_entry has relied on a sentinel
> value in the funcdata member to indicate a used slot. Code iterates
> through the array, inspecting .funcdata to find the first unused (zeroed)
> entry:
> 
>   for (i = 0; stub_func_addr(stubs[i].funcdata); i++)
> 
> To support CONFIG_PPC_FTRACE_OUT_OF_LINE, a new setup_ftrace_ool_stubs()
> function extended the .stubs section by adding an array of
> ftrace_ool_stub structures for each patchable function. A side effect
> of writing these smaller structures is that the funcdata sentinel
> convention is not maintained. This is not a problem for an ordinary
> kernel module, as this occurs in module_finalize(), after which no
> further .stubs updates are needed.
> 
> However, when loading a livepatch module that contains klp-relocations,
> apply_relocate_add() is executed a second time, after the out-of-line
> ftrace stubs have been set up.
> 
> When apply_relocate_add() then calls stub_for_addr() to handle a
> klp-relocation, its search for the next available ppc64_stub_entry[]
> slot may stop prematurely in the middle of the ftrace_ool_stub array. A
> full ppc64_stub_entry is then written, corrupting the ftrace stubs.
> 
> Fix this by explicitly tracking the count of used ppc64_stub_entrys.
> Rather than relying on an inline funcdata sentinel value, a new
> stub_count is used as the explicit boundary for searching and allocating
> stubs. This simplifies the code, eliminates the "one extra reloc" that
> was required for the sentinel check, and resolves the memory corruption.
> 

Apologies if this is too wordy, I wrote it as a bit of a braindump to
summarize the longer analysis at the bottom of the reply ...

> Fixes: eec37961a56a ("powerpc64/ftrace: Move ftrace sequence out of line")
> Signed-off-by: Joe Lawrence <[email-masked]>
> ---
>  arch/powerpc/include/asm/module.h |  1 +
>  arch/powerpc/kernel/module_64.c   | 26 ++++++++------------------
>  2 files changed, 9 insertions(+), 18 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
> index e1ee5026ac4a..864e22deaa2c 100644
> --- a/arch/powerpc/include/asm/module.h
> +++ b/arch/powerpc/include/asm/module.h
> @@ -27,6 +27,7 @@ struct ppc_plt_entry {
>  struct mod_arch_specific {
>  #ifdef __powerpc64__
>  	unsigned int stubs_section;	/* Index of stubs section in module */
> +	unsigned int stub_count;	/* Number of stubs used */
>  #ifdef CONFIG_PPC_KERNEL_PCREL
>  	unsigned int got_section;	/* What section is the GOT? */
>  	unsigned int pcpu_section;	/* .data..percpu section */
> diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
> index 126bf3b06ab7..2a44bc8e2439 100644
> --- a/arch/powerpc/kernel/module_64.c
> +++ b/arch/powerpc/kernel/module_64.c
> @@ -209,8 +209,7 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
>  				    char *secstrings,
>  				    struct module *me)
>  {
> -	/* One extra reloc so it's always 0-addr terminated */
> -	unsigned long relocs = 1;
> +	unsigned long relocs = 0;
>  	unsigned i;
>  
>  	/* Every relocated section... */
> @@ -705,7 +704,7 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs,
>  
>  	/* Find this stub, or if that fails, the next avail. entry */
>  	stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr;
> -	for (i = 0; stub_func_addr(stubs[i].funcdata); i++) {
> +	for (i = 0; i < me->arch.stub_count; i++) {
>  		if (WARN_ON(i >= num_stubs))
>  			return 0;
>  
> @@ -716,6 +715,7 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs,
>  	if (!create_stub(sechdrs, &stubs[i], addr, me, name))
>  		return 0;
>  
> +	me->arch.stub_count++;
>  	return (unsigned long)&stubs[i];
>  }
>  
> @@ -1118,29 +1118,19 @@ int module_trampoline_target(struct module *mod, unsigned long addr,
>  static int setup_ftrace_ool_stubs(const Elf64_Shdr *sechdrs, unsigned long addr, struct module *me)
>  {
>  #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE
> -	unsigned int i, total_stubs, num_stubs;
> +	unsigned int total_stubs, num_stubs;
>  	struct ppc64_stub_entry *stub;
>  
>  	total_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stub);
>  	num_stubs = roundup(me->arch.ool_stub_count * sizeof(struct ftrace_ool_stub),
>  			    sizeof(struct ppc64_stub_entry)) / sizeof(struct ppc64_stub_entry);
>  
> -	/* Find the next available entry */
> -	stub = (void *)sechdrs[me->arch.stubs_section].sh_addr;
> -	for (i = 0; stub_func_addr(stub[i].funcdata); i++)
> -		if (WARN_ON(i >= total_stubs))
> -			return -1;
> -
> -	if (WARN_ON(i + num_stubs > total_stubs))
> +	if (WARN_ON(me->arch.stub_count + num_stubs > total_stubs))
>  		return -1;
>  
> -	stub += i;
> -	me->arch.ool_stubs = (struct ftrace_ool_stub *)stub;
> -
> -	/* reserve stubs */
> -	for (i = 0; i < num_stubs; i++)
> -		if (patch_u32((void *)&stub->funcdata, PPC_RAW_NOP()))
> -			return -1;

At first I thought the bug was that this loop was re-writting the same
PPC_RAW_NOP() to the same funcdata (i.e, should have been something
like: patch_u32((void *)stub[i]->funcdata, PPC_RAW_NOP())), but that
didn't work and seemed fragile anyway.

> +	stub = (void *)sechdrs[me->arch.stubs_section].sh_addr;
> +	me->arch.ool_stubs = (struct ftrace_ool_stub *)(stub + me->arch.stub_count);
> +	me->arch.stub_count += num_stubs;
>  #endif
>  
>  	return 0;
> -- 
> 2.50.1
> 

Original problem
================

I noticed an issue when loading a livepatch module with a klp-relocation
(see Documentation/livepatch/module-elf-format.rst) and then tried
ftracing one of its functions:

  $ SYSFS=/sys/kernel/debug/tracing
  $ echo 0 > $SYSFS/tracing_on
  $ echo > $SYSFS/set_ftrace_filter
  $ echo function > $SYSFS/current_tracer
  $ echo 'xfs_stats_format:mod:livepatch_module' >> $SYSFS/set_ftrace_filter
 
  # Double check the ftrace filter  
  $ cat $SYSFS/set_ftrace_filter
  xfs_stats_format [livepatch_module]
 
  # Bombs away 
  $ echo 1 > $SYSFS/tracing_on
  $ grep kpatch /sys/fs/xfs/stats/stats

  [ ... sometimes a crash, sometimes nothing at all, but never a
        $SYSFS/trace entry ... ]

This module is different from any of the in-kernel livepatching
kselftests as it was created by kpatch-build, and included a
klp-relocation:

  $ readelf --wide --relocs livepatch-module.ko | awk '/.klp.rela.xfs..text.xfs_stats_format/' RS="\n\n"
  Relocation section '.klp.rela.xfs..text.xfs_stats_format' at offset 0x18e010 contains 1 entry:
      Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
  00000000000000d0  0000008c0000000a R_PPC64_REL24          0000000000000000 .klp.sym.xfs.counter_val,1 + 0

And when looking at the module's out-of-line ftrace stubs on a live system, I
saw the corruption.

crash utility analysis
----------------------

Dump the arch-specific module section to find the OOL-ftrace-sub area:

  crash> mod | grep livepatch_module
  c008000007d30500  livepatch_module                   c008000007d70000   262144  (not loaded)  [CONFIG_KALLSYMS]
  crash> struct module.arch c008000007d30500
    arch = {
      stubs_section = 72,
      toc_section = 50,
      toc_fixed = true,
      tramp = 0xc008000007d70d80,
      tramp_regs = 0xc008000007d70da8,
      ool_stubs = 0xc008000007d70dd0 <xfs_stats_format+0x558>,
      ool_stub_count = 7,
      ool_stub_index = 7
    },

Figure out how many instructions fit inside the ftrace_ool_stub[] area:

  crash> p/d (sizeof(struct ftrace_ool_stub) * 7) / 4
  $5 = 42

The crash utility disassembly below shows the memory corruption. After a
valid ftrace_ool_stub at index 1, a full ppc64_stub_entry for a
livepatch relocation has been incorrectly written at
xfs_stats_format+0x580, overwriting the memory where ftrace_ool_stub[1..3]
were supposed to be:

  [ edited for readability: ppc64=ppc64_stub_entry and ftrace=ftrace_ool_stub ]

  crash> dis 0xc008000007d70dd0 42
  ppc64[ ]   ftrace[0]    <xfs_stats_format+0x558>:    .long 0x0
                          <xfs_stats_format+0x55c>:    .long 0x0
                          <xfs_stats_format+0x560>:    mflr    r0
                          <xfs_stats_format+0x564>:    bl      0xc008000007d70d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x568>:    mtlr    r0
                          <xfs_stats_format+0x56c>:    b       0xc008000007d70014 <patch_free_livepatch+0xc>
             ftrace[1]    <xfs_stats_format+0x570>:    .long 0x0
                          <xfs_stats_format+0x574>:    .long 0x0
                          <xfs_stats_format+0x578>:    mflr    r0
                          <xfs_stats_format+0x57c>:    bl      0xc008000007d70d80 <xfs_stats_format+0x508>
  ppc64[ ]                <xfs_stats_format+0x580>:    addis   r11,r2,4                                         << This looks like a full
                          <xfs_stats_format+0x584>:    addi    r11,r11,-29448                                   << ppc64_stub_entry
             ftrace[2]    <xfs_stats_format+0x588>:    std     r2,24(r1)                                        << dropped in the middle
                          <xfs_stats_format+0x58c>:    ld      r12,32(r11)                                      << of the ool_stubs array
                          <xfs_stats_format+0x590>:    mtctr   r12                                              << of ftrace_ool_stub[]
                          <xfs_stats_format+0x594>:    bctr                                                     <<
                          <xfs_stats_format+0x598>:    mtlr    r0                                               <<
                          <xfs_stats_format+0x59c>:    andi.   r20,r27,30050                                    <<
             ftrace[3]    <xfs_stats_format+0x5a0>:    .long 0x54e92b8                                          <<
                          <xfs_stats_format+0x5a4>:    lfs     f0,0(r8)                                         <<
  ppc64[ ]                <xfs_stats_format+0x5a8>:    mflr    r0
                          <xfs_stats_format+0x5ac>:    bl      0xc008000007d70d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x5b0>:    mtlr    r0
                          <xfs_stats_format+0x5b4>:    b       0xc008000007d7037c <add_callbacks_to_patch_objects+0xc>
             ftrace[4]    <xfs_stats_format+0x5b8>:    .long 0x0
                          <xfs_stats_format+0x5bc>:    .long 0x0
                          <xfs_stats_format+0x5c0>:    mflr    r0
                          <xfs_stats_format+0x5c4>:    bl      0xc008000007d70d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x5c8>:    mtlr    r0
                          <xfs_stats_format+0x5cc>:    b       0xc008000007d705ac <init_module+0xc>
  ppc64[ ]   ftrace[5]    <xfs_stats_format+0x5d0>:    .long 0x0
                          <xfs_stats_format+0x5d4>:    .long 0x0
                          <xfs_stats_format+0x5d8>:    mflr    r0
                          <xfs_stats_format+0x5dc>:    bl      0xc008000007d70d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x5e0>:    mtlr    r0
                          <xfs_stats_format+0x5e4>:    b       0xc008000007d70864 <kpatch_string+0xc>
             ftrace[6]    <xfs_stats_format+0x5e8>:    .long 0x0
                          <xfs_stats_format+0x5ec>:    .long 0x0
                          <xfs_stats_format+0x5f0>:    mflr    r0
                          <xfs_stats_format+0x5f4>:    bl      0xc008000007d70d80 <xfs_stats_format+0x508>
  ppc64[ ]                <xfs_stats_format+0x5f8>:    mtlr    r0
                          <xfs_stats_format+0x5fc>:    b       0xc008000007d70884 <xfs_stats_format+0xc>

crash utility analysis (post-fix)
--------------------------------

Get our bearings from the arch-specific section of the module:

  crash> mod | grep livepatch_module
  c008000009ae0500  livepatch_module                   c0080000099c0000   262144  (not loaded)  [CONFIG_KALLSYMS]
  crash> struct module.arch c008000009ae0500
    arch = {
      stubs_section = 72,
      stub_count = 20,                                          << new count of all ppc64_stub_entry[]
      toc_section = 50,
      toc_fixed = true,
      tramp = 0xc0080000099c0d80,
      tramp_regs = 0xc0080000099c0da8,
      ool_stubs = 0xc0080000099c0dd0 <xfs_stats_format+0x558>,  << where ftrace_ool_stub[] begins
      ool_stub_count = 7,                                       << count of ftrace_ool_stubs
      ool_stub_index = 7
    },

Disassmble the ftrace_ool_stub[] area for the livepatch_module.  Figure out
how many instructions will fit into the ppc64_stub_entry and ftrace_ool_stub
structures so we can mark them along the way.

  crash> p/d sizeof(struct ppc64_stub_entry) / 4
  $1 = 10
  crash> p/d sizeof(struct ftrace_ool_stub) / 4
  $2 = 6
  crash> p/d (sizeof(struct ppc64_stub_entry) * 20) / 4
  $3 = 200
  crash> dis 0xc0080000099c0ba0 200

The new disassembly shows that the regular relocation stubs
ppc64_stub_entry[0..13] are followed by the correctly formatted
ftrace_ool_stub area (ftrace_ool_stub[0..6]).  The livepatch-specific
ppc64_stub_entry is now safely placed at the end of the section (at
ppc64_stub_entry[19]), avoiding any collision:

  [ edited for readability: ppc64=ppc64_stub_entry and ftrace=ftrace_ool_stub ]

  ppc64[0]                <xfs_stats_format+0x328>:    addis   r11,r2,-18
                          <xfs_stats_format+0x32c>:    addi    r11,r11,-30048
                          <xfs_stats_format+0x330>:    std     r2,24(r1)
                          <xfs_stats_format+0x334>:    ld      r12,32(r11)
                          <xfs_stats_format+0x338>:    mtctr   r12
                          <xfs_stats_format+0x33c>:    bctr
                          <xfs_stats_format+0x340>:    .long 0x0
                          <xfs_stats_format+0x344>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x348>:    .long 0x123fef0
                          <xfs_stats_format+0x34c>:    lfs     f0,0(0)
  ppc64[1]                <xfs_stats_format+0x350>:    addis   r11,r2,-18
                          <xfs_stats_format+0x354>:    addi    r11,r11,-30008
                          <xfs_stats_format+0x358>:    std     r2,24(r1)
                          <xfs_stats_format+0x35c>:    ld      r12,32(r11)
                          <xfs_stats_format+0x360>:    mtctr   r12
                          <xfs_stats_format+0x364>:    bctr
                          <xfs_stats_format+0x368>:    .long 0x0
                          <xfs_stats_format+0x36c>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x370>:    .long 0xa7c9f0
                          <xfs_stats_format+0x374>:    lfs     f0,0(0)
  ppc64[2]                <xfs_stats_format+0x378>:    addis   r11,r2,-18
                          <xfs_stats_format+0x37c>:    addi    r11,r11,-29968
                          <xfs_stats_format+0x380>:    std     r2,24(r1)
                          <xfs_stats_format+0x384>:    ld      r12,32(r11)
                          <xfs_stats_format+0x388>:    mtctr   r12
                          <xfs_stats_format+0x38c>:    bctr
                          <xfs_stats_format+0x390>:    .long 0x0
                          <xfs_stats_format+0x394>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x398>:    .long 0x2f0a94
                          <xfs_stats_format+0x39c>:    lfs     f0,0(0)
  ppc64[3]                <xfs_stats_format+0x3a0>:    addis   r11,r2,-18
                          <xfs_stats_format+0x3a4>:    addi    r11,r11,-29928
                          <xfs_stats_format+0x3a8>:    std     r2,24(r1)
                          <xfs_stats_format+0x3ac>:    ld      r12,32(r11)
                          <xfs_stats_format+0x3b0>:    mtctr   r12
                          <xfs_stats_format+0x3b4>:    bctr
                          <xfs_stats_format+0x3b8>:    .long 0x0
                          <xfs_stats_format+0x3bc>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x3c0>:    .long 0x67e440
                          <xfs_stats_format+0x3c4>:    lfs     f0,0(0)
  ppc64[4]                <xfs_stats_format+0x3c8>:    addis   r11,r2,-18
                          <xfs_stats_format+0x3cc>:    addi    r11,r11,-29888
                          <xfs_stats_format+0x3d0>:    std     r2,24(r1)
                          <xfs_stats_format+0x3d4>:    ld      r12,32(r11)
                          <xfs_stats_format+0x3d8>:    mtctr   r12
                          <xfs_stats_format+0x3dc>:    bctr
                          <xfs_stats_format+0x3e0>:    .long 0x0
                          <xfs_stats_format+0x3e4>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x3e8>:    .long 0x683da0
                          <xfs_stats_format+0x3ec>:    lfs     f0,0(0)
  ppc64[5]                <xfs_stats_format+0x3f0>:    addis   r11,r2,-18
                          <xfs_stats_format+0x3f4>:    addi    r11,r11,-29848
                          <xfs_stats_format+0x3f8>:    std     r2,24(r1)
                          <xfs_stats_format+0x3fc>:    ld      r12,32(r11)
                          <xfs_stats_format+0x400>:    mtctr   r12
                          <xfs_stats_format+0x404>:    bctr
                          <xfs_stats_format+0x408>:    .long 0x0
                          <xfs_stats_format+0x40c>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x410>:    .long 0xa7c8a0
                          <xfs_stats_format+0x414>:    lfs     f0,0(0)
  ppc64[6]                <xfs_stats_format+0x418>:    addis   r11,r2,-18
                          <xfs_stats_format+0x41c>:    addi    r11,r11,-29808
                          <xfs_stats_format+0x420>:    std     r2,24(r1)
                          <xfs_stats_format+0x424>:    ld      r12,32(r11)
                          <xfs_stats_format+0x428>:    mtctr   r12
                          <xfs_stats_format+0x42c>:    bctr
                          <xfs_stats_format+0x430>:    .long 0x0
                          <xfs_stats_format+0x434>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x438>:    .long 0x32fad0
                          <xfs_stats_format+0x43c>:    lfs     f0,0(0)
  ppc64[7]                <xfs_stats_format+0x440>:    addis   r11,r2,-18
                          <xfs_stats_format+0x444>:    addi    r11,r11,-29768
                          <xfs_stats_format+0x448>:    std     r2,24(r1)
                          <xfs_stats_format+0x44c>:    ld      r12,32(r11)
                          <xfs_stats_format+0x450>:    mtctr   r12
                          <xfs_stats_format+0x454>:    bctr
                          <xfs_stats_format+0x458>:    .long 0x0
                          <xfs_stats_format+0x45c>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x460>:    .long 0x59a5b0
                          <xfs_stats_format+0x464>:    lfs     f0,0(0)
  ppc64[8]                <xfs_stats_format+0x468>:    addis   r11,r2,-18
                          <xfs_stats_format+0x46c>:    addi    r11,r11,-29728
                          <xfs_stats_format+0x470>:    std     r2,24(r1)
                          <xfs_stats_format+0x474>:    ld      r12,32(r11)
                          <xfs_stats_format+0x478>:    mtctr   r12
                          <xfs_stats_format+0x47c>:    bctr
                          <xfs_stats_format+0x480>:    .long 0x0
                          <xfs_stats_format+0x484>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x488>:    .long 0xaabd70
                          <xfs_stats_format+0x48c>:    lfs     f0,0(0)
  ppc64[9]                <xfs_stats_format+0x490>:    addis   r11,r2,-18
                          <xfs_stats_format+0x494>:    addi    r11,r11,-29688
                          <xfs_stats_format+0x498>:    std     r2,24(r1)
                          <xfs_stats_format+0x49c>:    ld      r12,32(r11)
                          <xfs_stats_format+0x4a0>:    mtctr   r12
                          <xfs_stats_format+0x4a4>:    bctr
                          <xfs_stats_format+0x4a8>:    .long 0x0
                          <xfs_stats_format+0x4ac>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x4b0>:    .long 0xa55f20
                          <xfs_stats_format+0x4b4>:    lfs     f0,0(0)
  ppc64[10]               <xfs_stats_format+0x4b8>:    addis   r11,r2,-18
                          <xfs_stats_format+0x4bc>:    addi    r11,r11,-29648
                          <xfs_stats_format+0x4c0>:    std     r2,24(r1)
                          <xfs_stats_format+0x4c4>:    ld      r12,32(r11)
                          <xfs_stats_format+0x4c8>:    mtctr   r12
                          <xfs_stats_format+0x4cc>:    bctr
                          <xfs_stats_format+0x4d0>:    .long 0x0
                          <xfs_stats_format+0x4d4>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x4d8>:    .long 0x59a730
                          <xfs_stats_format+0x4dc>:    lfs     f0,0(0)
  ppc64[11]               <xfs_stats_format+0x4e0>:    addis   r11,r2,-18
                          <xfs_stats_format+0x4e4>:    addi    r11,r11,-29608
                          <xfs_stats_format+0x4e8>:    std     r2,24(r1)
                          <xfs_stats_format+0x4ec>:    ld      r12,32(r11)
                          <xfs_stats_format+0x4f0>:    mtctr   r12
                          <xfs_stats_format+0x4f4>:    bctr
                          <xfs_stats_format+0x4f8>:    .long 0x0
                          <xfs_stats_format+0x4fc>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x500>:    .long 0x1249840
                          <xfs_stats_format+0x504>:    lfs     f0,0(0)
  ppc64[12]               <xfs_stats_format+0x508>:    ld      r12,16(r13)
                          <xfs_stats_format+0x50c>:    addis   r12,r12,-464
                          <xfs_stats_format+0x510>:    addi    r12,r12,3568
                          <xfs_stats_format+0x514>:    mtctr   r12
                          <xfs_stats_format+0x518>:    bctr
                          <xfs_stats_format+0x51c>:    .long 0x0
                          <xfs_stats_format+0x520>:    .long 0x0
                          <xfs_stats_format+0x524>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x528>:    .long 0x78ef0
                          <xfs_stats_format+0x52c>:    lfs     f0,0(0)
  ppc64[13]               <xfs_stats_format+0x530>:    ld      r12,16(r13)
                          <xfs_stats_format+0x534>:    addis   r12,r12,-464
                          <xfs_stats_format+0x538>:    addi    r12,r12,3104
                          <xfs_stats_format+0x53c>:    mtctr   r12
                          <xfs_stats_format+0x540>:    bctr
                          <xfs_stats_format+0x544>:    .long 0x0
                          <xfs_stats_format+0x548>:    .long 0x0
                          <xfs_stats_format+0x54c>:    andi.   r20,r27,30050
                          <xfs_stats_format+0x550>:    .long 0x78d20
                          <xfs_stats_format+0x554>:    lfs     f0,0(0)
  ppc64[14]  ftrace[0]    <xfs_stats_format+0x558>:    .long 0x0
                          <xfs_stats_format+0x55c>:    .long 0x0
                          <xfs_stats_format+0x560>:    mflr    r0
                          <xfs_stats_format+0x564>:    bl      0xc0080000099c0d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x568>:    mtlr    r0
                          <xfs_stats_format+0x56c>:    b       0xc0080000099c0014 <patch_free_livepatch+0xc>
             ftrace[1]    <xfs_stats_format+0x570>:    .long 0x0
                          <xfs_stats_format+0x574>:    .long 0x0
                          <xfs_stats_format+0x578>:    mflr    r0
                          <xfs_stats_format+0x57c>:    bl      0xc0080000099c0d80 <xfs_stats_format+0x508>
  ppc64[15]               <xfs_stats_format+0x580>:    mtlr    r0
                          <xfs_stats_format+0x584>:    b       0xc0080000099c0100 <patch_free_scaffold+0xc>
             ftrace[2]    <xfs_stats_format+0x588>:    .long 0x0
                          <xfs_stats_format+0x58c>:    .long 0x0
                          <xfs_stats_format+0x590>:    mflr    r0
                          <xfs_stats_format+0x594>:    bl      0xc0080000099c0d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x598>:    mtlr    r0
                          <xfs_stats_format+0x59c>:    b       0xc0080000099c0244 <patch_find_object_by_name+0xc>
             ftrace[3]    <xfs_stats_format+0x5a0>:    .long 0x0
                          <xfs_stats_format+0x5a4>:    .long 0x0
  ppc64[16]               <xfs_stats_format+0x5a8>:    mflr    r0
                          <xfs_stats_format+0x5ac>:    bl      0xc0080000099c0d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x5b0>:    mtlr    r0
                          <xfs_stats_format+0x5b4>:    b       0xc0080000099c037c <add_callbacks_to_patch_objects+0xc>
             ftrace[4]    <xfs_stats_format+0x5b8>:    .long 0x0
                          <xfs_stats_format+0x5bc>:    .long 0x0
                          <xfs_stats_format+0x5c0>:    mflr    r0
                          <xfs_stats_format+0x5c4>:    bl      0xc0080000099c0d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x5c8>:    mtlr    r0
                          <xfs_stats_format+0x5cc>:    b       0xc0080000099c05ac <init_module+0xc>
  ppc64[17]  ftrace[5]    <xfs_stats_format+0x5d0>:    .long 0x0
                          <xfs_stats_format+0x5d4>:    .long 0x0
                          <xfs_stats_format+0x5d8>:    mflr    r0
                          <xfs_stats_format+0x5dc>:    bl      0xc0080000099c0d80 <xfs_stats_format+0x508>
                          <xfs_stats_format+0x5e0>:    mtlr    r0
                          <xfs_stats_format+0x5e4>:    b       0xc0080000099c0864 <kpatch_string+0xc>
             ftrace[6]    <xfs_stats_format+0x5e8>:    .long 0x0
                          <xfs_stats_format+0x5ec>:    .long 0x0
                          <xfs_stats_format+0x5f0>:    mflr    r0
                          <xfs_stats_format+0x5f4>:    bl      0xc0080000099c0d80 <xfs_stats_format+0x508>
  ppc64[18]               <xfs_stats_format+0x5f8>:    mtlr    r0
                          <xfs_stats_format+0x5fc>:    b       0xc0080000099c0884 <xfs_stats_format+0xc>
             (unused)     <xfs_stats_format+0x600>:    .long 0x0
                ..        <xfs_stats_format+0x604>:    .long 0x0
                ..        <xfs_stats_format+0x608>:    .long 0x0
                ..        <xfs_stats_format+0x60c>:    .long 0x0
                ..        <xfs_stats_format+0x610>:    .long 0x0
                ..        <xfs_stats_format+0x614>:    .long 0x0
                ..        <xfs_stats_format+0x618>:    .long 0x0
                ..        <xfs_stats_format+0x61c>:    .long 0x0
  ppc64[19]  full-        <xfs_stats_format+0x620>:    addis   r11,r2,-18                                       << klp-relocation
             klp-reloc    <xfs_stats_format+0x624>:    addi    r11,r11,-29288                                   << ppc64_stub_entry
             stub[0]      <xfs_stats_format+0x628>:    std     r2,24(r1)                                        << now tacked on *after*
                          <xfs_stats_format+0x62c>:    ld      r12,32(r11)                                      << the OOL-ftrace
                          <xfs_stats_format+0x630>:    mtctr   r12                                              << stubs
                          <xfs_stats_format+0x634>:    bctr                                                     <<
                          <xfs_stats_format+0x638>:    .long 0x0                                                <<
                          <xfs_stats_format+0x63c>:    andi.   r20,r27,30050                                    <<
                          <xfs_stats_format+0x640>:    .long 0x54e92b8                                          <<
                          <xfs_stats_format+0x644>:    lfs     f0,0(r8)                                         <<

And finally re-test the original problem, ftrace the livepatch-provided
function:

  $ SYSFS=/sys/kernel/debug/tracing
  $ echo 0 > $SYSFS/tracing_on
  $ echo > $SYSFS/set_ftrace_filter
  $ echo function > $SYSFS/current_tracer
  $ echo 'xfs_stats_format:mod:livepatch_module' >> $SYSFS/set_ftrace_filter
 
  # Double check the ftrace filter  
  $ cat $SYSFS/set_ftrace_filter
  xfs_stats_format [livepatch_module]
 
  # Bombs away 
  $ echo 1 > $SYSFS/tracing_on
  $ grep kpatch /sys/fs/xfs/stats/stats
  kpatch
  $ cat $SYSFS/trace
  # tracer: function
  #
  # entries-in-buffer/entries-written: 1/1   #P:8
  #
  #                                _-----=> irqs-off/BH-disabled
  #                               / _----=> need-resched
  #                              | / _---=> hardirq/softirq
  #                              || / _--=> preempt-depth
  #                              ||| / _-=> migrate-disable
  #                              |||| /     delay
  #           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
  #              | |         |   |||||     |         |
              grep-71247   [001] ..... 53756.730711: xfs_stats_format <-livepatch_handler

An entry in the trace buffer and no crash :)

--
Joe