Message ID | 20171004152516.25803-1-kamalesh@linux.vnet.ibm.com (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
Series | [v2] kernel/module_64.c: Add REL24 relocation support of livepatch symbols | expand |
On 2017/10/04 03:25PM, Kamalesh Babulal wrote: > With commit 425595a7fc20 ("livepatch: reuse module loader code to > write relocations") livepatch uses module loader to write relocations > of livepatch symbols, instead of managing them by arch-dependent > klp_write_module_reloc() function. > > livepatch module managed relocation entries are written to sections > marked with SHF_RELA_LIVEPATCH flag and livepatch symbols within the > section are marked with SHN_LIVEPATCH symbol section index. When the > livepatching module is loaded, the livepatch symbols are resolved > before calling apply_relocate_add() to apply the relocations. > > R_PPC64_REL24 relocation type resolves to a function address, those may > be local to the livepatch module or available in kernel/other modules. > For every such non-local function, apply_relocate_add() constructs a > stub (a.k.a trampoline) to branch to a function. Stub code is > responsible to save toc onto the stack, before calling the function via > the global entry point. A NOP instruction is expected after every non > local function branch, i.e. after the REL24 relocation. Which in-turn > is replaced by toc restore instruction by apply_relocate_add(). > > Functions those were local to livepatched function previously, may have > become global now or they might be out of range with current TOC base. > During module load, apply_relocate_add() fails for such global > functions, as it expect's a nop after a branch. Which does't exist for a > non-local function accessed via local entry point. > > For example, consider the following livepatch relocations (the example > is from livepatch module generated by kpatch tool): > > Relocation section '.klp.rela.vmlinux..text.meminfo_proc_show' at offset > 0x84530 contains 44 entries: > > Offset Info Type Symbol's Value Symbol's Name + Addend > ... ... R_PPC64_REL24 0x0 .klp.sym.vmlinux.si_swapinfo,0 + 0 > ... ... R_PPC64_REL24 0x0 .klp.sym.vmlinux.total_swapcache_pages,0 + 0 > ... ... R_PPC64_REL24 0x0 .klp.sym.vmlinux.show_val_kb,1 + 0 > [...] > > 1. .klp.sym.vmlinux.si_swapinfo and .klp.sym.vmlinux.total_swapcache_pages > are not available within the livepatch module TOC range. > > 2. .klp.sym.vmlinux.show_val_kb livepatch symbol was previously local > but now global w.r.t module call fs/proc/meminfo.c::meminfo_proc_show() > > While the livepatch module is loaded the livepatch symbols mentioned in > case 1 will fail with an error: > module_64: kpatch_meminfo: REL24 -1152921504751525976 out of range! > > and livepatch symbols mentioned in case 2 with fail with an error: > module_64: kpatch_meminfo: Expect noop after relocate, got 3d220000 > > Both the failures with REL24 livepatch symbols relocation, can be > resolved by constructing a new livepatch stub. The newly setup klp_stub > mimics the functionality of entry_64.S::livepatch_handler introduced by > commit 85baa095497f ("powerpc/livepatch: Add live patching support on > ppc64le"). > > Which introduces a "livepatch stack" growing upwards from the base of > the regular stack and is used to store/restore TOC/LR values, other than > the stub setup and branch. The additional instructions sequences to > handle klp_stub increases the stub size and current ppc64_stub_insn[] > is not sufficient to hold them. This patch also introduces new > ppc64le_klp_stub_entry[], along with the helpers to find/allocate > livepatch stub. > > Signed-off-by: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com> > Cc: Balbir Singh <bsingharora@gmail.com> > Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> > Cc: Josh Poimboeuf <jpoimboe@redhat.com> > Cc: Jessica Yu <jeyu@kernel.org> > Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com> > Cc: Aravinda Prasad <aravinda@linux.vnet.ibm.com> > Cc: linuxppc-dev@lists.ozlabs.org > Cc: live-patching@vger.kernel.org > --- > This patch applies on top of livepatch_handler fix posted at > https://lists.ozlabs.org/pipermail/linuxppc-dev/2017-September/163824.html > > v2: > - Changed klp_stub construction to re-use livepatch_handler and > additional patch code required for klp_stub, instead of duplicating it. > - Minor comments and commit body edits. > > arch/powerpc/include/asm/module.h | 4 + > arch/powerpc/kernel/module_64.c | 135 ++++++++++++++++++++++++- > arch/powerpc/kernel/trace/ftrace_64_mprofile.S | 31 ++++++ > 3 files changed, 167 insertions(+), 3 deletions(-) > > diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h > index 6c0132c7212f..de22c4c7aebc 100644 > --- a/arch/powerpc/include/asm/module.h > +++ b/arch/powerpc/include/asm/module.h > @@ -44,6 +44,10 @@ struct mod_arch_specific { > unsigned long toc; > unsigned long tramp; > #endif > +#ifdef CONFIG_LIVEPATCH > + /* Count of kernel livepatch relocations */ > + unsigned long klp_relocs; > +#endif > > #else /* powerpc64 */ > /* Indices of PLT sections within module. */ > diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c > index 0b0f89685b67..5be955e59162 100644 > --- a/arch/powerpc/kernel/module_64.c > +++ b/arch/powerpc/kernel/module_64.c > @@ -140,6 +140,24 @@ static u32 ppc64_stub_insns[] = { > 0x4e800420 /* bctr */ > }; > > +#ifdef CONFIG_LIVEPATCH > +extern u32 klp_stub_insn[], klp_stub_insn_end[]; > +extern u32 livepatch_handler[], livepatch_handler_end[]; > + > +struct ppc64le_klp_stub_entry { > + /* > + * Other than setting up the stub and livepatch stub also needs to > + * allocate extra instructions to allocate livepatch stack, > + * storing/restoring TOC/LR values on/from the livepatch stack. > + */ > + u32 jump[31]; > + /* Used by ftrace to identify stubs */ > + u32 magic; > + /* Data for the above code */ > + func_desc_t funcdata; > +}; > +#endif > + > #ifdef CONFIG_DYNAMIC_FTRACE > int module_trampoline_target(struct module *mod, unsigned long addr, > unsigned long *target) > @@ -239,10 +257,19 @@ static void relaswap(void *_x, void *_y, int size) > > /* Get size of potential trampolines required. */ > static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, > - const Elf64_Shdr *sechdrs) > + const Elf64_Shdr *sechdrs, > + struct module *me) > { > /* One extra reloc so it's always 0-funcaddr terminated */ > unsigned long relocs = 1; > + /* > + * size of livepatch stub is 28 instructions, whereas the > + * non-livepatch stub requires 7 instructions. Account for > + * different stub sizes and track the livepatch relocation > + * count in me->arch.klp_relocs. > + */ > + unsigned long sec_relocs = 0; > + unsigned long klp_relocs = 0; > unsigned i; > > /* Every relocated section... */ > @@ -262,9 +289,14 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, > sechdrs[i].sh_size / sizeof(Elf64_Rela), > sizeof(Elf64_Rela), relacmp, relaswap); > > - relocs += count_relocs((void *)sechdrs[i].sh_addr, > + sec_relocs = count_relocs((void *)sechdrs[i].sh_addr, > sechdrs[i].sh_size > / sizeof(Elf64_Rela)); > + relocs += sec_relocs; > +#ifdef CONFIG_LIVEPATCH > + if (sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) > + klp_relocs += sec_relocs; > +#endif > } > } > > @@ -273,6 +305,15 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, > relocs++; > #endif > > + relocs -= klp_relocs; > +#ifdef CONFIG_LIVEPATCH > + me->arch.klp_relocs = klp_relocs; > + > + pr_debug("Looks like a total of %lu stubs, (%lu) livepatch stubs, max\n", > + relocs, klp_relocs); > + return (relocs * sizeof(struct ppc64_stub_entry) + > + klp_relocs * sizeof(struct ppc64le_klp_stub_entry)); > +#endif > pr_debug("Looks like a total of %lu stubs, max\n", relocs); > return relocs * sizeof(struct ppc64_stub_entry); > } > @@ -369,7 +410,7 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr, > me->arch.toc_section = me->arch.stubs_section; > > /* Override the stubs size */ > - sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs); > + sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs, me); > return 0; > } > > @@ -415,6 +456,56 @@ static inline int create_stub(const Elf64_Shdr *sechdrs, > return 1; > } > > +#ifdef CONFIG_LIVEPATCH > +/* Patch livepatch stub to reference function and correct r2 value. */ > +static inline int create_klp_stub(const Elf64_Shdr *sechdrs, > + struct ppc64le_klp_stub_entry *entry, > + unsigned long addr, > + struct module *me) > +{ > + long reladdr; > + unsigned long klp_stub_idx, klp_stub_idx_end; > + > + klp_stub_idx = (klp_stub_insn - livepatch_handler); > + klp_stub_idx_end = (livepatch_handler_end - klp_stub_insn_end); > + > + /* Copy first half of livepatch_handler till klp_stub_insn */ > + memcpy(entry->jump, livepatch_handler, sizeof(u32) * klp_stub_idx); > + > + /* Stub uses address relative to r2. */ > + reladdr = (unsigned long)entry - my_r2(sechdrs, me); > + if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { > + pr_err("%s: Address %p of stub out of range of %p.\n", > + me->name, (void *)reladdr, (void *)my_r2); > + return 0; > + } > + pr_debug("Stub %p get data from reladdr %li\n", entry, reladdr); > + > + /* > + * Patch the code required to load the trampoline address into r11, > + * function global entry point into r12, ctr. > + */ > + entry->jump[klp_stub_idx++] = (PPC_INST_ADDIS | ___PPC_RT(11) | > + ___PPC_RA(2) | PPC_HA(reladdr)); > + > + entry->jump[klp_stub_idx++] = (PPC_INST_ADDI | ___PPC_RT(11) | > + ___PPC_RA(11) | PPC_LO(reladdr)); > + > + entry->jump[klp_stub_idx++] = (PPC_INST_LD | ___PPC_RT(12) | > + ___PPC_RA(11) | 128); ^^^ Better to use offsetof(). Apart from that, the stub handling itself looks good to me. Thanks, Naveen > + > + entry->jump[klp_stub_idx++] = PPC_INST_MTCTR | ___PPC_RT(12); > + > + /* Copy second half of livepatch_handler starting klp_stub_insn_end */ > + memcpy(entry->jump + klp_stub_idx, klp_stub_insn_end, > + sizeof(u32) * klp_stub_idx_end); > + > + entry->funcdata = func_desc(addr); > + entry->magic = STUB_MAGIC; > + return 1; > +} > +#endif > + > /* Create stub to jump to function described in this OPD/ptr: we need the > stub to set up the TOC ptr (r2) for the function. */ > static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs, > @@ -441,6 +532,38 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs, > return (unsigned long)&stubs[i]; > } > > +#ifdef CONFIG_LIVEPATCH > +static unsigned long klp_stub_for_addr(const Elf64_Shdr *sechdrs, > + unsigned long addr, > + struct module *me) > +{ > + struct ppc64le_klp_stub_entry *klp_stubs; > + unsigned int num_klp_stubs = me->arch.klp_relocs; > + unsigned int i, num_stubs; > + > + num_stubs = (sechdrs[me->arch.stubs_section].sh_size - > + (num_klp_stubs * sizeof(*klp_stubs))) / > + sizeof(struct ppc64_stub_entry); > + > + /* > + * Create livepatch stubs after the regular stubs. > + */ > + klp_stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr + > + (num_stubs * sizeof(struct ppc64_stub_entry)); > + for (i = 0; stub_func_addr(klp_stubs[i].funcdata); i++) { > + BUG_ON(i >= num_klp_stubs); > + > + if (stub_func_addr(klp_stubs[i].funcdata) == func_addr(addr)) > + return (unsigned long)&klp_stubs[i]; > + } > + > + if (!create_klp_stub(sechdrs, &klp_stubs[i], addr, me)) > + return 0; > + > + return (unsigned long)&klp_stubs[i]; > +} > +#endif > + > #ifdef CC_USING_MPROFILE_KERNEL > static bool is_early_mcount_callsite(u32 *instruction) > { > @@ -622,6 +745,12 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, > return -ENOEXEC; > > squash_toc_save_inst(strtab + sym->st_name, value); > +#ifdef CONFIG_LIVEPATCH > + } else if (sym->st_shndx == SHN_LIVEPATCH) { > + value = klp_stub_for_addr(sechdrs, value, me); > + if (!value) > + return -ENOENT; > +#endif > } else > value += local_entry_offset(sym); > > diff --git a/arch/powerpc/kernel/trace/ftrace_64_mprofile.S b/arch/powerpc/kernel/trace/ftrace_64_mprofile.S > index b4e2b7165f79..a4e97cb9da91 100644 > --- a/arch/powerpc/kernel/trace/ftrace_64_mprofile.S > +++ b/arch/powerpc/kernel/trace/ftrace_64_mprofile.S > @@ -183,7 +183,10 @@ _GLOBAL(ftrace_stub) > * - CTR holds the new NIP in C > * - r0, r11 & r12 are free > */ > + > + .global livepatch_handler > livepatch_handler: > + > CURRENT_THREAD_INFO(r12, r1) > > /* Allocate 3 x 8 bytes */ > @@ -201,8 +204,33 @@ livepatch_handler: > ori r12, r12, STACK_END_MAGIC@l > std r12, -8(r11) > > + /* > + * klp_stub_insn/klp_stub_insn_end marks the beginning/end of the > + * additional instructions, which gets patched by create_klp_stub() > + * for livepatch symbol relocation stub. The instructions are: > + * > + * Load TOC relative address into r11. module_64.c::klp_stub_for_addr() > + * identifies the available free stub slot and loads the address into > + * r11 with two instructions. > + * > + * addis r11, r2, stub_address@ha > + * addi r11, r11, stub_address@l > + * > + * Load global entry into r12 from entry->funcdata offset > + * ld r12, 128(r11) > + * > + * Put r12 into ctr and branch there > + * mtctr r12 > + */ > + > + .global klp_stub_insn > +klp_stub_insn: > + > /* Put ctr in r12 for global entry and branch there */ > mfctr r12 > + > + .global klp_stub_insn_end > +klp_stub_insn_end: > bctrl > > /* > @@ -234,6 +262,9 @@ livepatch_handler: > > /* Return to original caller of live patched function */ > blr > + > + .global livepatch_handler_end > +livepatch_handler_end: > #endif /* CONFIG_LIVEPATCH */ > > #endif /* CONFIG_DYNAMIC_FTRACE */ > -- > 2.11.0 >
On Wed, Oct 04, 2017 at 11:25:16AM -0400, Kamalesh Babulal wrote: > > Both the failures with REL24 livepatch symbols relocation, can be > resolved by constructing a new livepatch stub. The newly setup klp_stub > mimics the functionality of entry_64.S::livepatch_handler introduced by > commit 85baa095497f ("powerpc/livepatch: Add live patching support on > ppc64le"). So, do I get his right that this patch is based on your June 13 proposal "powerpc/modules: Introduce new stub code for SHN_LIVEPATCH symbols" ? I guess you lost many of us already at that point. What is the new, much bigger stub code needed for? Stub code should do only the very bare minimum, all common functionality is handled in the kernel main object. What exactly is the problem you're trying to solve, what is to be achieved? > + > + /* > + * Patch the code required to load the trampoline address into r11, > + * function global entry point into r12, ctr. > + */ > + entry->jump[klp_stub_idx++] = (PPC_INST_ADDIS | ___PPC_RT(11) | > + ___PPC_RA(2) | PPC_HA(reladdr)); > + > + entry->jump[klp_stub_idx++] = (PPC_INST_ADDI | ___PPC_RT(11) | > + ___PPC_RA(11) | PPC_LO(reladdr)); > + > + entry->jump[klp_stub_idx++] = (PPC_INST_LD | ___PPC_RT(12) | > + ___PPC_RA(11) | 128); ^^^ Also, I was a bit puzzled by this constant, BTW. Can you #define a meaning to this, perhaps? > @@ -201,8 +204,33 @@ livepatch_handler: > ori r12, r12, STACK_END_MAGIC@l > std r12, -8(r11) > > + /* > + * klp_stub_insn/klp_stub_insn_end marks the beginning/end of the > + * additional instructions, which gets patched by create_klp_stub() > + * for livepatch symbol relocation stub. The instructions are: > + * > + * Load TOC relative address into r11. module_64.c::klp_stub_for_addr() > + * identifies the available free stub slot and loads the address into > + * r11 with two instructions. > + * > + * addis r11, r2, stub_address@ha > + * addi r11, r11, stub_address@l > + * > + * Load global entry into r12 from entry->funcdata offset > + * ld r12, 128(r11) Is that the same 128 as above? Then it should definitely be a #define to avoid inconsistencies. Torsten
On Thursday 05 October 2017 06:13 PM, Torsten Duwe wrote: > On Wed, Oct 04, 2017 at 11:25:16AM -0400, Kamalesh Babulal wrote: >> >> Both the failures with REL24 livepatch symbols relocation, can be >> resolved by constructing a new livepatch stub. The newly setup klp_stub >> mimics the functionality of entry_64.S::livepatch_handler introduced by >> commit 85baa095497f ("powerpc/livepatch: Add live patching support on >> ppc64le"). > > So, do I get his right that this patch is based on your June 13 proposal > "powerpc/modules: Introduce new stub code for SHN_LIVEPATCH symbols" ? > I guess you lost many of us already at that point. What is the new, much > bigger stub code needed for? Stub code should do only the very bare minimum, > all common functionality is handled in the kernel main object. > > What exactly is the problem you're trying to solve, what is to be achieved? Thanks for the review. With apply_relocate_add() writing out relocations for livepatch symbols too. R_PPC_REL24: Doesn't handle SHN_LIVEPATCH symbols and ends up being treated as local symbol and calls local_entry_offset(). Which triggers an error: module_64: kpatch_meminfo: REL24 -1152921504897399800 out of range! Whereas SHN_LIVEPATCH symbols are essentially SHN_UNDEF, should be called via external stub. This issue can be fixed by handling both SHN_UNDEF and SHN_LIVEPATCH via same external stub. It isn't a complete fix, because it will fail with local calls becoming global. Consider the livepatch sequence[1]. Where function A calls B, B is the function which has been livepatched and the call to function B is redirected to patched version P. P calls the function C in M2, whereas C was local to the function B and have became SHN_UNDEF in function P. Local call becoming global. +--------+ +--------+ +--------+ +--------+ | | +--------+--------+--->| | +-->| | | A | | | B | | F | | | P | | | | | | | +--+ | | | +---+ | | | |<-+ | | | |<--+ +----+ C | | | | | | | | | | +->| | | | | | |<---+ | K / M1 | | | | | K / M2 | +-+ Kernel | +---+ Mod3 +--+ | +--------+ | | | +--------+ | +--------+ +--------+ | | | | | | | | +---+-+--------------+ | | | | | | | +--------------------------------------------+ | +------------------------------------------------+ Handling such call with regular stub, triggers another error: module_64: kpatch_meminfo: Expect noop after relocate, got 3d220000 Every branch to SHN_UNDEF is followed by a nop instruction, that gets overwritten by an instruction to restore TOC with r2 value that get stored onto the stack, before calling the function via global entry point. Given that C was local to function B, it does not store/restore TOC as they are not expected to be clobbered for functions called via local entry point. Current stub can be extended to re-store TOC and have a single stub for both SHN_UNDEF/SHN_LIVEPATCH symbols. Constructing a single stub is an overhead for non livepatch calla, as it adds extra instructions for TOC restore. Idea was to create a new stub for SHN_LIVEPATCH symbols. Which would also restore the TOC on the return to livepatched function, by introducing an intermediate stack between function P and function C. This was the earlier proposal made in June. It will work for most of the cases but will not work, when arguments to C as passes through stack. This issue has been already solved by introduction of livepatch_handler, which runs in _mcount context by creating a livepatch stack to store/restore TOC/LR. It avoids the need for an intermediate stack. Current approach, creates a hybrid stub. Which is a combination of regular stub (stub setup code) + livepatch_handler (stores/restores TOC/LR with livepatch stack). > >> + >> + /* >> + * Patch the code required to load the trampoline address into r11, >> + * function global entry point into r12, ctr. >> + */ >> + entry->jump[klp_stub_idx++] = (PPC_INST_ADDIS | ___PPC_RT(11) | >> + ___PPC_RA(2) | PPC_HA(reladdr)); >> + >> + entry->jump[klp_stub_idx++] = (PPC_INST_ADDI | ___PPC_RT(11) | >> + ___PPC_RA(11) | PPC_LO(reladdr)); >> + >> + entry->jump[klp_stub_idx++] = (PPC_INST_LD | ___PPC_RT(12) | >> + ___PPC_RA(11) | 128); > ^^^ > Also, I was a bit puzzled by this constant, BTW. > Can you #define a meaning to this, perhaps? Naveen too pointed it out. Will introduce a define for the offset. > >> @@ -201,8 +204,33 @@ livepatch_handler: >> ori r12, r12, STACK_END_MAGIC@l >> std r12, -8(r11) >> >> + /* >> + * klp_stub_insn/klp_stub_insn_end marks the beginning/end of the >> + * additional instructions, which gets patched by create_klp_stub() >> + * for livepatch symbol relocation stub. The instructions are: >> + * >> + * Load TOC relative address into r11. module_64.c::klp_stub_for_addr() >> + * identifies the available free stub slot and loads the address into >> + * r11 with two instructions. >> + * >> + * addis r11, r2, stub_address@ha >> + * addi r11, r11, stub_address@l >> + * >> + * Load global entry into r12 from entry->funcdata offset >> + * ld r12, 128(r11) > > Is that the same 128 as above? Then it should definitely be a #define to > avoid inconsistencies. Yes, it's the same offset as above in the comments. [1] ASCII diagram adopted from: http://mpe.github.io/posts/2016/05/23/kernel-live-patching-for-ppc64le/
On Thursday 05 October 2017 06:13 PM, Torsten Duwe wrote: > On Wed, Oct 04, 2017 at 11:25:16AM -0400, Kamalesh Babulal wrote: >> >> Both the failures with REL24 livepatch symbols relocation, can be >> resolved by constructing a new livepatch stub. The newly setup klp_stub >> mimics the functionality of entry_64.S::livepatch_handler introduced by >> commit 85baa095497f ("powerpc/livepatch: Add live patching support on >> ppc64le"). > > So, do I get his right that this patch is based on your June 13 proposal > "powerpc/modules: Introduce new stub code for SHN_LIVEPATCH symbols" ? > I guess you lost many of us already at that point. What is the new, much > bigger stub code needed for? Stub code should do only the very bare minimum, > all common functionality is handled in the kernel main object. > > What exactly is the problem you're trying to solve, what is to be achieved? Resending the reply, sorry about the word wrapping in the previous mail. Thanks for the review. With apply_relocate_add() writing out relocations for livepatch symbols too. R_PPC_REL24: Doesn't handle SHN_LIVEPATCH symbols and ends up being treated as local symbol and calls local_entry_offset(). Which triggers an error: module_64: kpatch_meminfo: REL24 -1152921504897399800 out of range! Whereas SHN_LIVEPATCH symbols are essentially SHN_UNDEF, should be called via external stub. This issue can be fixed by handling both SHN_UNDEF and SHN_LIVEPATCH via same external stub. It isn't a complete fix, because it will fail with local calls becoming global. Consider the livepatch sequence[1]. Where function A calls B, B is the function which has been livepatched and the call to function B is redirected to patched version P. P calls the function C in M2, whereas C was local to the function B and have became SHN_UNDEF in function P. Local call becoming global. +--------+ +--------+ +--------+ +--------+ | | +--------+--------+--->| | +-->| | | A | | | B | | F | | | P | | | | | | | +--+ | | | +---+ | | | |<-+ | | | |<--+ +----+ C | | | | | | | | | | +->| | | | | | |<---+ | K / M1 | | | | | K / M2 | +-+ Kernel | +---+ Mod3 +--+ | +--------+ | | | +--------+ | +--------+ +--------+ | | | | | | | | +---+-+--------------+ | | | | | | | +--------------------------------------------+ | +------------------------------------------------+ Handling such call with regular stub, triggers another error: module_64: kpatch_meminfo: Expect noop after relocate, got 3d220000 Every branch to SHN_UNDEF is followed by a nop instruction, that gets overwritten by an instruction to restore TOC with r2 value that get stored onto the stack, before calling the function via global entry point. Given that C was local to function B, it does not store/restore TOC as they are not expected to be clobbered for functions called via local entry point. Current stub can be extended to re-store TOC and have a single stub for both SHN_UNDEF/SHN_LIVEPATCH symbols. Constructing a single stub is an overhead for non livepatch calla, as it adds extra instructions for TOC restore. Idea was to create a new stub for SHN_LIVEPATCH symbols. Which would also restore the TOC on the return to livepatched function, by introducing an intermediate stack between function P and function C. This was the earlier proposal made in June. It will work for most of the cases but will not work, when arguments to C as passes through stack. This issue has been already solved by introduction of livepatch_handler, which runs in _mcount context by creating a livepatch stack to store/restore TOC/LR. It avoids the need for an intermediate stack. Current approach, creates a hybrid stub. Which is a combination of regular stub (stub setup code) + livepatch_handler (stores/restores TOC/LR with livepatch stack). > >> + >> + /* >> + * Patch the code required to load the trampoline address into r11, >> + * function global entry point into r12, ctr. >> + */ >> + entry->jump[klp_stub_idx++] = (PPC_INST_ADDIS | ___PPC_RT(11) | >> + ___PPC_RA(2) | PPC_HA(reladdr)); >> + >> + entry->jump[klp_stub_idx++] = (PPC_INST_ADDI | ___PPC_RT(11) | >> + ___PPC_RA(11) | PPC_LO(reladdr)); >> + >> + entry->jump[klp_stub_idx++] = (PPC_INST_LD | ___PPC_RT(12) | >> + ___PPC_RA(11) | 128); > ^^^ > Also, I was a bit puzzled by this constant, BTW. > Can you #define a meaning to this, perhaps? Naveen too pointed it out. Will introduce a define for the offset. > >> @@ -201,8 +204,33 @@ livepatch_handler: >> ori r12, r12, STACK_END_MAGIC@l >> std r12, -8(r11) >> >> + /* >> + * klp_stub_insn/klp_stub_insn_end marks the beginning/end of the >> + * additional instructions, which gets patched by create_klp_stub() >> + * for livepatch symbol relocation stub. The instructions are: >> + * >> + * Load TOC relative address into r11. module_64.c::klp_stub_for_addr() >> + * identifies the available free stub slot and loads the address into >> + * r11 with two instructions. >> + * >> + * addis r11, r2, stub_address@ha >> + * addi r11, r11, stub_address@l >> + * >> + * Load global entry into r12 from entry->funcdata offset >> + * ld r12, 128(r11) > > Is that the same 128 as above? Then it should definitely be a #define to > avoid inconsistencies. > Yes, it's the same offset as above in the comments. [1] ASCII diagram adopted from: http://mpe.github.io/posts/2016/05/23/kernel-live-patching-for-ppc64le/
On Friday 06 October 2017 11:13 AM, Kamalesh Babulal wrote: > On Thursday 05 October 2017 06:13 PM, Torsten Duwe wrote: >> On Wed, Oct 04, 2017 at 11:25:16AM -0400, Kamalesh Babulal wrote: >>> >>> Both the failures with REL24 livepatch symbols relocation, can be >>> resolved by constructing a new livepatch stub. The newly setup klp_stub >>> mimics the functionality of entry_64.S::livepatch_handler introduced by >>> commit 85baa095497f ("powerpc/livepatch: Add live patching support on >>> ppc64le"). >> >> So, do I get his right that this patch is based on your June 13 proposal >> "powerpc/modules: Introduce new stub code for SHN_LIVEPATCH symbols" ? >> I guess you lost many of us already at that point. What is the new, much >> bigger stub code needed for? Stub code should do only the very bare >> minimum, >> all common functionality is handled in the kernel main object. >> >> What exactly is the problem you're trying to solve, what is to be >> achieved? > > Thanks for the review. > > With apply_relocate_add() writing out relocations for livepatch symbols > too. R_PPC_REL24: Doesn't handle SHN_LIVEPATCH symbols and ends up being > treated as local symbol and calls local_entry_offset(). Which triggers > an error: > > module_64: kpatch_meminfo: REL24 -1152921504897399800 out of range! > > Whereas SHN_LIVEPATCH symbols are essentially SHN_UNDEF, should be > called via external stub. This issue can be fixed by handling both > SHN_UNDEF and SHN_LIVEPATCH via same external stub. It isn't a complete > fix, because it will fail with local calls becoming global. > > Consider the livepatch sequence[1]. Where function A calls B, B is the > function which has been livepatched and the call to function B is > redirected to patched version P. P calls the function C in M2, whereas C > was local to the function B and have became SHN_UNDEF in function P. > Local call becoming global. > > +--------+ +--------+ +--------+ +--------+ > | | +--------+--------+--->| | +-->| | > | A | | | B | | F | | | P | > | | | | | | +--+ | | > | +---+ | | | |<-+ | | > | |<--+ +----+ C | | | | | | > | | | | +->| | | | | | |<---+ > | K / M1 | | | | | K / M2 | +-+ Kernel | +---+ Mod3 +--+ | > +--------+ | | | +--------+ | +--------+ +--------+ | | > | | | | | | > +---+-+--------------+ | | > | | | | > | +--------------------------------------------+ | > +------------------------------------------------+ > > Handling such call with regular stub, triggers another error: > > module_64: kpatch_meminfo: Expect noop after relocate, got 3d220000 > > Every branch to SHN_UNDEF is followed by a nop instruction, that gets > overwritten by an instruction to restore TOC with r2 value that get > stored onto the stack, before calling the function via global entry point. > > Given that C was local to function B, it does not store/restore TOC as > they are not expected to be clobbered for functions called via local > entry point. > > Current stub can be extended to re-store TOC and have a single stub for > both SHN_UNDEF/SHN_LIVEPATCH symbols. Constructing a single stub is an > overhead for non livepatch calla, as it adds extra instructions for TOC > restore. > > Idea was to create a new stub for SHN_LIVEPATCH symbols. Which would > also restore the TOC on the return to livepatched function, by > introducing an intermediate stack between function P and function C. > This was the earlier proposal made in June. > > It will work for most of the cases but will not work, when arguments to > C as passes through stack. This issue has been already solved by > introduction of livepatch_handler, which runs in _mcount context by > creating a livepatch stack to store/restore TOC/LR. It avoids the need > for an intermediate stack. > > Current approach, creates a hybrid stub. Which is a combination of > regular stub (stub setup code) + livepatch_handler (stores/restores > TOC/LR with livepatch stack). > Torsten, Did you get a chance to read the problem statement and solution proposed. Looking forward for your comments. >> >>> + >>> + /* >>> + * Patch the code required to load the trampoline address into r11, >>> + * function global entry point into r12, ctr. >>> + */ >>> + entry->jump[klp_stub_idx++] = (PPC_INST_ADDIS | ___PPC_RT(11) | >>> + ___PPC_RA(2) | PPC_HA(reladdr)); >>> + >>> + entry->jump[klp_stub_idx++] = (PPC_INST_ADDI | ___PPC_RT(11) | >>> + ___PPC_RA(11) | PPC_LO(reladdr)); >>> + >>> + entry->jump[klp_stub_idx++] = (PPC_INST_LD | ___PPC_RT(12) | >>> + ___PPC_RA(11) | 128); >> ^^^ >> Also, I was a bit puzzled by this constant, BTW. >> Can you #define a meaning to this, perhaps? > > Naveen too pointed it out. Will introduce a define for the offset. > >> >>> @@ -201,8 +204,33 @@ livepatch_handler: >>> ori r12, r12, STACK_END_MAGIC@l >>> std r12, -8(r11) >>> >>> + /* >>> + * klp_stub_insn/klp_stub_insn_end marks the beginning/end of the >>> + * additional instructions, which gets patched by create_klp_stub() >>> + * for livepatch symbol relocation stub. The instructions are: >>> + * >>> + * Load TOC relative address into r11. >>> module_64.c::klp_stub_for_addr() >>> + * identifies the available free stub slot and loads the address >>> into >>> + * r11 with two instructions. >>> + * >>> + * addis r11, r2, stub_address@ha >>> + * addi r11, r11, stub_address@l >>> + * >>> + * Load global entry into r12 from entry->funcdata offset >>> + * ld r12, 128(r11) >> >> Is that the same 128 as above? Then it should definitely be a #define to >> avoid inconsistencies. > > Yes, it's the same offset as above in the comments. > > [1] ASCII diagram adopted from: > http://mpe.github.io/posts/2016/05/23/kernel-live-patching-for-ppc64le/ >
On Fri, Oct 06, 2017 at 11:27:42AM +0530, Kamalesh Babulal wrote: > > Consider the livepatch sequence[1]. Where function A calls B, B is the > function which has been livepatched and the call to function B is > redirected to patched version P. P calls the function C in M2, whereas > C was local to the function B and have became SHN_UNDEF in function P. > Local call becoming global. > > +--------+ +--------+ +--------+ +--------+ > | | +--------+--------+--->| | +-->| | > | A | | | B | | F | | | P | > | | | | | | +--+ | | > | +---+ | | | |<-+ | | > | |<--+ +----+ C | | | | | | > | | | | +->| | | | | | |<---+ > | K / M1 | | | | | K / M2 | +-+ Kernel | +---+ Mod3 +--+ | > +--------+ | | | +--------+ | +--------+ +--------+ | | > | | | | | | > +---+-+--------------+ | | > | | | | > | +--------------------------------------------+ | > +------------------------------------------------+ > > > Handling such call with regular stub, triggers another error: > > module_64: kpatch_meminfo: Expect noop after relocate, got 3d220000 > > Every branch to SHN_UNDEF is followed by a nop instruction, that gets > overwritten by an instruction to restore TOC with r2 value that get > stored onto the stack, before calling the function via global entry > point. > > Given that C was local to function B, it does not store/restore TOC as > they are not expected to be clobbered for functions called via local > entry point. Can you please provide example source code of Mod3 and C? If P calls C, this is a regular global call, the TOC is saved by the stub and restored after the call instruction. Why do you think this is not the case? Torsten
On Tuesday 17 October 2017 08:17 PM, Torsten Duwe wrote: > On Fri, Oct 06, 2017 at 11:27:42AM +0530, Kamalesh Babulal wrote: >> >> Consider the livepatch sequence[1]. Where function A calls B, B is the >> function which has been livepatched and the call to function B is >> redirected to patched version P. P calls the function C in M2, whereas >> C was local to the function B and have became SHN_UNDEF in function P. >> Local call becoming global. >> >> +--------+ +--------+ +--------+ +--------+ >> | | +--------+--------+--->| | +-->| | >> | A | | | B | | F | | | P | >> | | | | | | +--+ | | >> | +---+ | | | |<-+ | | >> | |<--+ +----+ C | | | | | | >> | | | | +->| | | | | | |<---+ >> | K / M1 | | | | | K / M2 | +-+ Kernel | +---+ Mod3 +--+ | >> +--------+ | | | +--------+ | +--------+ +--------+ | | >> | | | | | | >> +---+-+--------------+ | | >> | | | | >> | +--------------------------------------------+ | >> +------------------------------------------------+ >> >> >> Handling such call with regular stub, triggers another error: >> >> module_64: kpatch_meminfo: Expect noop after relocate, got 3d220000 >> >> Every branch to SHN_UNDEF is followed by a nop instruction, that gets >> overwritten by an instruction to restore TOC with r2 value that get >> stored onto the stack, before calling the function via global entry >> point. >> >> Given that C was local to function B, it does not store/restore TOC as >> they are not expected to be clobbered for functions called via local >> entry point. > > Can you please provide example source code of Mod3 and C? If P calls C, this > is a regular global call, the TOC is saved by the stub and restored after the > call instruction. Why do you think this is not the case? > Consider a trivial patch, supplied to kpatch tool for generating a livepatch module: --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -132,7 +132,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) seq_printf(m, "VmallocTotal: %8lu kB\n", (unsigned long)VMALLOC_TOTAL >> 10); show_val_kb(m, "VmallocUsed: ", 0ul); - show_val_kb(m, "VmallocChunk: ", 0ul); + show_val_kb(m, "VMALLOCCHUNK: ", 0ul); #ifdef CONFIG_MEMORY_FAILURE seq_printf(m, "HardwareCorrupted: %5lu kB\n", # readelf -s -W ./fs/proc/meminfo.o Symbol table '.symtab' contains 54 entries: Num: Value Size Type Bind Vis Ndx Name ... 23: 0x50 224 FUNC LOCAL DEFAULT [<localentry>: 8] 1 show_val_kb ... # objdump -dr ./fs/proc/meminfo.o 0000000000000140 <meminfo_proc_show>: 204: 01 00 00 48 bl 204 <meminfo_proc_show+0xc4> 204: R_PPC64_REL24 si_mem_available 208: 00 00 00 60 nop ... 220: 01 00 00 48 bl 220 <meminfo_proc_show+0xe0> 220: R_PPC64_REL24 show_val_kb 224: 88 00 a1 e8 ld r5,136(r1) 228: 00 00 82 3c addis r4,r2,0 show_val_kb() is called by meminfo_proc_show() frequently to print memory statistics, is also defined in meminfo.o. Which means both the functions share the same TOC base and is accessed via local entry point by calculating the offset with respect to current TOC. A nop instruction is only excepted after every branch to a global call. That gets overwritten by an instruction to restore TOC with r2 value of callee. Given function show_val_kb() is local to meminfo_proc_show(), any call to show_val_kb() doesn't requires setting up/restoring TOC as they are not expected to be clobbered for local function call (via local entry point). kpatch identifies the patched function to be meminfo_proc_show() and copies it into livepatch module, along with required symbols and livepatch hooks but doesn't copies show_val_kb(). The reason being, it can be called like any other global function and is marked with SHN_LIVEPATCH symbol section index. show_val_kb() which is local to meminfo_proc_show(), is global to patched version of meminfo_proc_show(). Symbol table '.symtab' contains 91 entries: Num: Value Size Type Bind Vis Ndx Name ... 82: 0x0 0 FUNC LOCAL DEFAULT OS [0xff20] .klp.sym.vmlinux.show_val_kb,1 ... apply_relocate_add() should be modified to handle show_val_kb() via global entry point (through stub) like SHN_UNDEF. Branch to a global function, is excepted to be followed by a nop instruction. Whereas patched version of meminfo_proc_show() code is not modified to add a nop after every branch to show_val_kb(). Nop instruction is required for setting up r2 with the TOC of livepatch module, which is clobbered by TOC base, required to access show_val_kb() and fails with error: module_64: kpatch_meminfo: Expect noop after relocate, got 3d220000 Approach is to setup klp_stub mimicking the functionality of entry_64.S::livepatch_handler to store/restore TOC/LR values, other than the stub setup and branching code.
On Wed, Oct 18, 2017 at 11:47:35AM +0530, Kamalesh Babulal wrote: > > Consider a trivial patch, supplied to kpatch tool for generating a > livepatch module: > > --- a/fs/proc/meminfo.c > +++ b/fs/proc/meminfo.c > @@ -132,7 +132,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) > seq_printf(m, "VmallocTotal: %8lu kB\n", > (unsigned long)VMALLOC_TOTAL >> 10); > show_val_kb(m, "VmallocUsed: ", 0ul); > - show_val_kb(m, "VmallocChunk: ", 0ul); > + show_val_kb(m, "VMALLOCCHUNK: ", 0ul); > Am I assuming correctly that "kpatch tool" simply recompiles all code the way it would get compiled in a regular kernel build? My understanding is that live patching modules need to be carefully prepared, which involves source code reorganisation and recompilation. In that process, you can easily declare show_val_kb() extern, and get the suitable instruction sequence for the call. You have CC'ed live-patching. A discussion about how to automate this very process is currently going on there. May I suggest you subscribe to that if you are interested. Torsten
On Fri, 2017-10-20 at 14:07 +0200, Torsten Duwe wrote: > On Wed, Oct 18, 2017 at 11:47:35AM +0530, Kamalesh Babulal wrote: > > > > Consider a trivial patch, supplied to kpatch tool for generating a > > livepatch module: > > > > --- a/fs/proc/meminfo.c > > +++ b/fs/proc/meminfo.c > > @@ -132,7 +132,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) > > seq_printf(m, "VmallocTotal: %8lu kB\n", > > (unsigned long)VMALLOC_TOTAL >> 10); > > show_val_kb(m, "VmallocUsed: ", 0ul); > > - show_val_kb(m, "VmallocChunk: ", 0ul); > > + show_val_kb(m, "VMALLOCCHUNK: ", 0ul); > > > > Am I assuming correctly that "kpatch tool" simply recompiles all code the > way it would get compiled in a regular kernel build? kpatch is open source and is available on github. This patch is specific to the way kpatch works > My understanding is > that live patching modules need to be carefully prepared, which involves > source code reorganisation and recompilation. In that process, you can > easily declare show_val_kb() extern, and get the suitable instruction sequence > for the call. Yes, we agree. For the current versions of kpatch, which involve a process of applying the patch and building the kernel without and with the patch and doing an elf diff (programatically), we cannot deviate from that process as it's architecture independent. This patch solves arch specific issues related to that process. > > You have CC'ed live-patching. A discussion about how to automate this very > process is currently going on there. May I suggest you subscribe to that if > you are interested. We are interested, can you point us to the archives. While we do follow that thread, this patch is independent of future changes and enables kpatch today and enables certain workflows > > Torsten > Thanks for your review Balbir Singh.
On Saturday 21 October 2017 06:29 AM, Balbir Singh wrote: > On Fri, 2017-10-20 at 14:07 +0200, Torsten Duwe wrote: >> On Wed, Oct 18, 2017 at 11:47:35AM +0530, Kamalesh Babulal wrote: >>> >>> Consider a trivial patch, supplied to kpatch tool for generating a >>> livepatch module: >>> >>> --- a/fs/proc/meminfo.c >>> +++ b/fs/proc/meminfo.c >>> @@ -132,7 +132,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) >>> seq_printf(m, "VmallocTotal: %8lu kB\n", >>> (unsigned long)VMALLOC_TOTAL >> 10); >>> show_val_kb(m, "VmallocUsed: ", 0ul); >>> - show_val_kb(m, "VmallocChunk: ", 0ul); >>> + show_val_kb(m, "VMALLOCCHUNK: ", 0ul); >>> >> >> Am I assuming correctly that "kpatch tool" simply recompiles all code the >> way it would get compiled in a regular kernel build? > > kpatch is open source and is available on github. This patch is specific > to the way kpatch works > >> My understanding is >> that live patching modules need to be carefully prepared, which involves >> source code reorganisation and recompilation. In that process, you can >> easily declare show_val_kb() extern, and get the suitable instruction sequence >> for the call. > > Yes, we agree. For the current versions of kpatch, which involve a process of > applying the patch and building the kernel without and with the patch and doing > an elf diff (programatically), we cannot deviate from that process as it's > architecture independent. This patch solves arch specific issues related > to that process. Yes, that's the essence of the kpatch tool on building livepatchable kernel module, by doing an elf diff on the kernel with and without the patch applied. show_val_kb() is a simple example, consider more complex patch(s), if they need to be prepared manually as suggested. It beats the whole purpose of a kpatch tool, which programmatically prepares a livepatch module with close to no manual preparation required. It's the architecture limitation, which is addressed in this patch. This patch is outcome of long discussion at kpatch https://github.com/dynup/kpatch/pull/650
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h index 6c0132c7212f..de22c4c7aebc 100644 --- a/arch/powerpc/include/asm/module.h +++ b/arch/powerpc/include/asm/module.h @@ -44,6 +44,10 @@ struct mod_arch_specific { unsigned long toc; unsigned long tramp; #endif +#ifdef CONFIG_LIVEPATCH + /* Count of kernel livepatch relocations */ + unsigned long klp_relocs; +#endif #else /* powerpc64 */ /* Indices of PLT sections within module. */ diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c index 0b0f89685b67..5be955e59162 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -140,6 +140,24 @@ static u32 ppc64_stub_insns[] = { 0x4e800420 /* bctr */ }; +#ifdef CONFIG_LIVEPATCH +extern u32 klp_stub_insn[], klp_stub_insn_end[]; +extern u32 livepatch_handler[], livepatch_handler_end[]; + +struct ppc64le_klp_stub_entry { + /* + * Other than setting up the stub and livepatch stub also needs to + * allocate extra instructions to allocate livepatch stack, + * storing/restoring TOC/LR values on/from the livepatch stack. + */ + u32 jump[31]; + /* Used by ftrace to identify stubs */ + u32 magic; + /* Data for the above code */ + func_desc_t funcdata; +}; +#endif + #ifdef CONFIG_DYNAMIC_FTRACE int module_trampoline_target(struct module *mod, unsigned long addr, unsigned long *target) @@ -239,10 +257,19 @@ static void relaswap(void *_x, void *_y, int size) /* Get size of potential trampolines required. */ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, - const Elf64_Shdr *sechdrs) + const Elf64_Shdr *sechdrs, + struct module *me) { /* One extra reloc so it's always 0-funcaddr terminated */ unsigned long relocs = 1; + /* + * size of livepatch stub is 28 instructions, whereas the + * non-livepatch stub requires 7 instructions. Account for + * different stub sizes and track the livepatch relocation + * count in me->arch.klp_relocs. + */ + unsigned long sec_relocs = 0; + unsigned long klp_relocs = 0; unsigned i; /* Every relocated section... */ @@ -262,9 +289,14 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, sechdrs[i].sh_size / sizeof(Elf64_Rela), sizeof(Elf64_Rela), relacmp, relaswap); - relocs += count_relocs((void *)sechdrs[i].sh_addr, + sec_relocs = count_relocs((void *)sechdrs[i].sh_addr, sechdrs[i].sh_size / sizeof(Elf64_Rela)); + relocs += sec_relocs; +#ifdef CONFIG_LIVEPATCH + if (sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) + klp_relocs += sec_relocs; +#endif } } @@ -273,6 +305,15 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, relocs++; #endif + relocs -= klp_relocs; +#ifdef CONFIG_LIVEPATCH + me->arch.klp_relocs = klp_relocs; + + pr_debug("Looks like a total of %lu stubs, (%lu) livepatch stubs, max\n", + relocs, klp_relocs); + return (relocs * sizeof(struct ppc64_stub_entry) + + klp_relocs * sizeof(struct ppc64le_klp_stub_entry)); +#endif pr_debug("Looks like a total of %lu stubs, max\n", relocs); return relocs * sizeof(struct ppc64_stub_entry); } @@ -369,7 +410,7 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr, me->arch.toc_section = me->arch.stubs_section; /* Override the stubs size */ - sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs); + sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs, me); return 0; } @@ -415,6 +456,56 @@ static inline int create_stub(const Elf64_Shdr *sechdrs, return 1; } +#ifdef CONFIG_LIVEPATCH +/* Patch livepatch stub to reference function and correct r2 value. */ +static inline int create_klp_stub(const Elf64_Shdr *sechdrs, + struct ppc64le_klp_stub_entry *entry, + unsigned long addr, + struct module *me) +{ + long reladdr; + unsigned long klp_stub_idx, klp_stub_idx_end; + + klp_stub_idx = (klp_stub_insn - livepatch_handler); + klp_stub_idx_end = (livepatch_handler_end - klp_stub_insn_end); + + /* Copy first half of livepatch_handler till klp_stub_insn */ + memcpy(entry->jump, livepatch_handler, sizeof(u32) * klp_stub_idx); + + /* Stub uses address relative to r2. */ + reladdr = (unsigned long)entry - my_r2(sechdrs, me); + if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { + pr_err("%s: Address %p of stub out of range of %p.\n", + me->name, (void *)reladdr, (void *)my_r2); + return 0; + } + pr_debug("Stub %p get data from reladdr %li\n", entry, reladdr); + + /* + * Patch the code required to load the trampoline address into r11, + * function global entry point into r12, ctr. + */ + entry->jump[klp_stub_idx++] = (PPC_INST_ADDIS | ___PPC_RT(11) | + ___PPC_RA(2) | PPC_HA(reladdr)); + + entry->jump[klp_stub_idx++] = (PPC_INST_ADDI | ___PPC_RT(11) | + ___PPC_RA(11) | PPC_LO(reladdr)); + + entry->jump[klp_stub_idx++] = (PPC_INST_LD | ___PPC_RT(12) | + ___PPC_RA(11) | 128); + + entry->jump[klp_stub_idx++] = PPC_INST_MTCTR | ___PPC_RT(12); + + /* Copy second half of livepatch_handler starting klp_stub_insn_end */ + memcpy(entry->jump + klp_stub_idx, klp_stub_insn_end, + sizeof(u32) * klp_stub_idx_end); + + entry->funcdata = func_desc(addr); + entry->magic = STUB_MAGIC; + return 1; +} +#endif + /* Create stub to jump to function described in this OPD/ptr: we need the stub to set up the TOC ptr (r2) for the function. */ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs, @@ -441,6 +532,38 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs, return (unsigned long)&stubs[i]; } +#ifdef CONFIG_LIVEPATCH +static unsigned long klp_stub_for_addr(const Elf64_Shdr *sechdrs, + unsigned long addr, + struct module *me) +{ + struct ppc64le_klp_stub_entry *klp_stubs; + unsigned int num_klp_stubs = me->arch.klp_relocs; + unsigned int i, num_stubs; + + num_stubs = (sechdrs[me->arch.stubs_section].sh_size - + (num_klp_stubs * sizeof(*klp_stubs))) / + sizeof(struct ppc64_stub_entry); + + /* + * Create livepatch stubs after the regular stubs. + */ + klp_stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr + + (num_stubs * sizeof(struct ppc64_stub_entry)); + for (i = 0; stub_func_addr(klp_stubs[i].funcdata); i++) { + BUG_ON(i >= num_klp_stubs); + + if (stub_func_addr(klp_stubs[i].funcdata) == func_addr(addr)) + return (unsigned long)&klp_stubs[i]; + } + + if (!create_klp_stub(sechdrs, &klp_stubs[i], addr, me)) + return 0; + + return (unsigned long)&klp_stubs[i]; +} +#endif + #ifdef CC_USING_MPROFILE_KERNEL static bool is_early_mcount_callsite(u32 *instruction) { @@ -622,6 +745,12 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, return -ENOEXEC; squash_toc_save_inst(strtab + sym->st_name, value); +#ifdef CONFIG_LIVEPATCH + } else if (sym->st_shndx == SHN_LIVEPATCH) { + value = klp_stub_for_addr(sechdrs, value, me); + if (!value) + return -ENOENT; +#endif } else value += local_entry_offset(sym); diff --git a/arch/powerpc/kernel/trace/ftrace_64_mprofile.S b/arch/powerpc/kernel/trace/ftrace_64_mprofile.S index b4e2b7165f79..a4e97cb9da91 100644 --- a/arch/powerpc/kernel/trace/ftrace_64_mprofile.S +++ b/arch/powerpc/kernel/trace/ftrace_64_mprofile.S @@ -183,7 +183,10 @@ _GLOBAL(ftrace_stub) * - CTR holds the new NIP in C * - r0, r11 & r12 are free */ + + .global livepatch_handler livepatch_handler: + CURRENT_THREAD_INFO(r12, r1) /* Allocate 3 x 8 bytes */ @@ -201,8 +204,33 @@ livepatch_handler: ori r12, r12, STACK_END_MAGIC@l std r12, -8(r11) + /* + * klp_stub_insn/klp_stub_insn_end marks the beginning/end of the + * additional instructions, which gets patched by create_klp_stub() + * for livepatch symbol relocation stub. The instructions are: + * + * Load TOC relative address into r11. module_64.c::klp_stub_for_addr() + * identifies the available free stub slot and loads the address into + * r11 with two instructions. + * + * addis r11, r2, stub_address@ha + * addi r11, r11, stub_address@l + * + * Load global entry into r12 from entry->funcdata offset + * ld r12, 128(r11) + * + * Put r12 into ctr and branch there + * mtctr r12 + */ + + .global klp_stub_insn +klp_stub_insn: + /* Put ctr in r12 for global entry and branch there */ mfctr r12 + + .global klp_stub_insn_end +klp_stub_insn_end: bctrl /* @@ -234,6 +262,9 @@ livepatch_handler: /* Return to original caller of live patched function */ blr + + .global livepatch_handler_end +livepatch_handler_end: #endif /* CONFIG_LIVEPATCH */ #endif /* CONFIG_DYNAMIC_FTRACE */
With commit 425595a7fc20 ("livepatch: reuse module loader code to write relocations") livepatch uses module loader to write relocations of livepatch symbols, instead of managing them by arch-dependent klp_write_module_reloc() function. livepatch module managed relocation entries are written to sections marked with SHF_RELA_LIVEPATCH flag and livepatch symbols within the section are marked with SHN_LIVEPATCH symbol section index. When the livepatching module is loaded, the livepatch symbols are resolved before calling apply_relocate_add() to apply the relocations. R_PPC64_REL24 relocation type resolves to a function address, those may be local to the livepatch module or available in kernel/other modules. For every such non-local function, apply_relocate_add() constructs a stub (a.k.a trampoline) to branch to a function. Stub code is responsible to save toc onto the stack, before calling the function via the global entry point. A NOP instruction is expected after every non local function branch, i.e. after the REL24 relocation. Which in-turn is replaced by toc restore instruction by apply_relocate_add(). Functions those were local to livepatched function previously, may have become global now or they might be out of range with current TOC base. During module load, apply_relocate_add() fails for such global functions, as it expect's a nop after a branch. Which does't exist for a non-local function accessed via local entry point. For example, consider the following livepatch relocations (the example is from livepatch module generated by kpatch tool): Relocation section '.klp.rela.vmlinux..text.meminfo_proc_show' at offset 0x84530 contains 44 entries: Offset Info Type Symbol's Value Symbol's Name + Addend ... ... R_PPC64_REL24 0x0 .klp.sym.vmlinux.si_swapinfo,0 + 0 ... ... R_PPC64_REL24 0x0 .klp.sym.vmlinux.total_swapcache_pages,0 + 0 ... ... R_PPC64_REL24 0x0 .klp.sym.vmlinux.show_val_kb,1 + 0 [...] 1. .klp.sym.vmlinux.si_swapinfo and .klp.sym.vmlinux.total_swapcache_pages are not available within the livepatch module TOC range. 2. .klp.sym.vmlinux.show_val_kb livepatch symbol was previously local but now global w.r.t module call fs/proc/meminfo.c::meminfo_proc_show() While the livepatch module is loaded the livepatch symbols mentioned in case 1 will fail with an error: module_64: kpatch_meminfo: REL24 -1152921504751525976 out of range! and livepatch symbols mentioned in case 2 with fail with an error: module_64: kpatch_meminfo: Expect noop after relocate, got 3d220000 Both the failures with REL24 livepatch symbols relocation, can be resolved by constructing a new livepatch stub. The newly setup klp_stub mimics the functionality of entry_64.S::livepatch_handler introduced by commit 85baa095497f ("powerpc/livepatch: Add live patching support on ppc64le"). Which introduces a "livepatch stack" growing upwards from the base of the regular stack and is used to store/restore TOC/LR values, other than the stub setup and branch. The additional instructions sequences to handle klp_stub increases the stub size and current ppc64_stub_insn[] is not sufficient to hold them. This patch also introduces new ppc64le_klp_stub_entry[], along with the helpers to find/allocate livepatch stub. Signed-off-by: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com> Cc: Balbir Singh <bsingharora@gmail.com> Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Jessica Yu <jeyu@kernel.org> Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com> Cc: Aravinda Prasad <aravinda@linux.vnet.ibm.com> Cc: linuxppc-dev@lists.ozlabs.org Cc: live-patching@vger.kernel.org --- This patch applies on top of livepatch_handler fix posted at https://lists.ozlabs.org/pipermail/linuxppc-dev/2017-September/163824.html v2: - Changed klp_stub construction to re-use livepatch_handler and additional patch code required for klp_stub, instead of duplicating it. - Minor comments and commit body edits. arch/powerpc/include/asm/module.h | 4 + arch/powerpc/kernel/module_64.c | 135 ++++++++++++++++++++++++- arch/powerpc/kernel/trace/ftrace_64_mprofile.S | 31 ++++++ 3 files changed, 167 insertions(+), 3 deletions(-)