Received: by 2002:a05:6358:d09b:b0:dc:cd0c:909e with SMTP id jc27csp811371rwb; Fri, 18 Nov 2022 08:32:02 -0800 (PST) X-Google-Smtp-Source: AA0mqf7qU/nz96kbbOahNqYCl+gMwpJti5037zMSoBL25y+1OgYDQaszyCbtZ1Rl8Dd/SRfdlDcF X-Received: by 2002:a17:902:b692:b0:188:fc5f:84f2 with SMTP id c18-20020a170902b69200b00188fc5f84f2mr190727pls.137.1668789121740; Fri, 18 Nov 2022 08:32:01 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1668789121; cv=none; d=google.com; s=arc-20160816; b=N/s2HucGUYv0nK+0L25wSWEJro1JsXNBr/3wDPZDiVFP97DlHJoR+zdm7ytCan2lsv NMN8mGgLSK3vTJRYLxytJUBJI2mHAiGQUoucq4O5+dLgYyvrNi54ipIQPb1cfMzfgQcp mHRVxOEDVjmigJwPOvu+ODg2rD/gBD0p/+mVWRkQ74Es1WzjDAAUi12g65EQ8FyxQNqS rDndg9rRD8XrJgNAqYVjzkCOWuwytpEXvz+AzY2BAmDBto9k76BQTxlt83CXkG7J5pnt VSEkLahRlcIZGJLBK6uhRec+DCZmZpsVYPp0yHnUrXUEV8REBmSzhlxeUtX9RfEIihLO OdTw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=qr94mBts7Tg7djDvAk8xY0jbfoze+lyUFDJ0niaddco=; b=YGvCv/cBgsISzzGaxRamFVMlfswM5pyzkVX6Wq4iJ1M8DYagw9Mu7M3pQGgDXoMOsN 2h0ermOp0/53KC2f4F8h9xXPI9/UEdyP8hYcMin3E1AQgMHYSlHXo2EWXH8noNFhHfAu clDXrwht8kRcbCaxfIckjGqWhkf+bAp6Mxb96xy/PYQagTaahpl+XrXKLJMBHwo1fd6V 6i6GQO3wOgzU9gCllzwYuvYNfSk2c7yb3hsfw4M7w9IjIda1Ymgcnl9M5zwwopvdVXG4 LK9aVC+xKxa3RutymyOIQok5SvS/pZxs23wWMoE+3R7P0j4MVa5ctuJbFUIaP59qEHWP sv0w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@suse.com header.s=susede1 header.b=sDm8w6n6; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=suse.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id h22-20020a056a00219600b005734ae5e1b4si543090pfi.362.2022.11.18.08.31.49; Fri, 18 Nov 2022 08:32:01 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@suse.com header.s=susede1 header.b=sDm8w6n6; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=suse.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241367AbiKRQYL (ORCPT + 92 others); Fri, 18 Nov 2022 11:24:11 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45556 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240540AbiKRQYI (ORCPT ); Fri, 18 Nov 2022 11:24:08 -0500 Received: from smtp-out2.suse.de (smtp-out2.suse.de [IPv6:2001:67c:2178:6::1d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 441F478191; Fri, 18 Nov 2022 08:24:06 -0800 (PST) Received: from relay2.suse.de (relay2.suse.de [149.44.160.134]) by smtp-out2.suse.de (Postfix) with ESMTP id E63131FD42; Fri, 18 Nov 2022 16:24:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1668788644; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=qr94mBts7Tg7djDvAk8xY0jbfoze+lyUFDJ0niaddco=; b=sDm8w6n6FIXPkYgQQi+mur1gToC1gsnY6I7eXu1vTz+HfSNpRGAGCcBL8bqGb0I75oSX89 R5NuXjrwHvY+n0u8pH+3j08iTJlbMG5cPc6viYx14SdhghLIeewt2Hys5IexxBuAMRkawM nVK7Uxa+T0Onbjgz1Cqp6+9xqutZ8VY= Received: from suse.cz (unknown [10.100.201.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by relay2.suse.de (Postfix) with ESMTPS id 7B3D32C141; Fri, 18 Nov 2022 16:24:04 +0000 (UTC) Date: Fri, 18 Nov 2022 17:24:04 +0100 From: Petr Mladek To: Song Liu Cc: live-patching@vger.kernel.org, linux-kernel@vger.kernel.org, jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz, x86@kernel.org, joe.lawrence@redhat.com, linuxppc-dev@lists.ozlabs.org, Josh Poimboeuf Subject: Re: [PATCH v6] livepatch: Clear relocation targets on a module removal Message-ID: References: <20220901171252.2148348-1-song@kernel.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20220901171252.2148348-1-song@kernel.org> X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED,SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu 2022-09-01 10:12:52, Song Liu wrote: > From: Miroslav Benes > > Josh reported a bug: > > When the object to be patched is a module, and that module is > rmmod'ed and reloaded, it fails to load with: > > module: x86/modules: Skipping invalid relocation target, existing value is nonzero for type 2, loc 00000000ba0302e9, val ffffffffa03e293c > livepatch: failed to initialize patch 'livepatch_nfsd' for module 'nfsd' (-8) > livepatch: patch 'livepatch_nfsd' failed for module 'nfsd', refusing to load module 'nfsd' > > The livepatch module has a relocation which references a symbol > in the _previous_ loading of nfsd. When apply_relocate_add() > tries to replace the old relocation with a new one, it sees that > the previous one is nonzero and it errors out. > > We thus decided to reverse the relocation patching (clear all relocation > targets on x86_64). The solution is not > universal and is too much arch-specific, but it may prove to be simpler > in the end. > > arch/powerpc/kernel/module_32.c | 10 ++++ > arch/powerpc/kernel/module_64.c | 49 +++++++++++++++ > arch/s390/kernel/module.c | 8 +++ > arch/x86/kernel/module.c | 102 +++++++++++++++++++++++--------- > include/linux/moduleloader.h | 7 +++ > kernel/livepatch/core.c | 41 ++++++++++++- First, thanks a lot for working on this. I can't check or test the powerpc and s390 code easily. I am going to comment only x86 and generic code. It looks good but it needs some changes to improve maintainability. > 6 files changed, 189 insertions(+), 28 deletions(-) > > diff --git a/arch/powerpc/kernel/module_32.c b/arch/powerpc/kernel/module_32.c > index ea6536171778..e3c312770453 100644 > --- a/arch/powerpc/kernel/module_32.c > +++ b/arch/powerpc/kernel/module_32.c > @@ -285,6 +285,16 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, > return 0; > } > > +#ifdef CONFIG_LIVEPATCH > +void clear_relocate_add(Elf32_Shdr *sechdrs, > + const char *strtab, > + unsigned int symindex, > + unsigned int relsec, > + struct module *me) > +{ > +} > +#endif > + > #ifdef CONFIG_DYNAMIC_FTRACE > notrace int module_trampoline_target(struct module *mod, unsigned long addr, > unsigned long *target) > diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c > index 7e45dc98df8a..514951f97391 100644 > --- a/arch/powerpc/kernel/module_64.c > +++ b/arch/powerpc/kernel/module_64.c > @@ -739,6 +739,55 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, > return 0; > } > > +#ifdef CONFIG_LIVEPATCH > +void clear_relocate_add(Elf64_Shdr *sechdrs, > + const char *strtab, > + unsigned int symindex, > + unsigned int relsec, > + struct module *me) > +{ > + unsigned int i; > + Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr; > + Elf64_Sym *sym; > + unsigned long *location; > + const char *symname; > + u32 *instruction; > + > + pr_debug("Clearing ADD relocate section %u to %u\n", relsec, > + sechdrs[relsec].sh_info); > + > + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { > + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr > + + rela[i].r_offset; > + sym = (Elf64_Sym *)sechdrs[symindex].sh_addr > + + ELF64_R_SYM(rela[i].r_info); > + symname = me->core_kallsyms.strtab > + + sym->st_name; > + > + if (ELF64_R_TYPE(rela[i].r_info) != R_PPC_REL24) > + continue; > + /* > + * reverse the operations in apply_relocate_add() for case > + * R_PPC_REL24. > + */ > + if (sym->st_shndx != SHN_UNDEF && > + sym->st_shndx != SHN_LIVEPATCH) > + continue; > + > + instruction = (u32 *)location; > + if (is_mprofile_ftrace_call(symname)) > + continue; > + > + if (!instr_is_relative_link_branch(ppc_inst(*instruction))) > + continue; > + > + instruction += 1; > + patch_instruction(instruction, ppc_inst(PPC_RAW_NOP())); > + } > + > +} This looks like a lot of duplicated code. Isn't it? > +#endif > + > #ifdef CONFIG_DYNAMIC_FTRACE > int module_trampoline_target(struct module *mod, unsigned long addr, > unsigned long *target) > --- a/arch/x86/kernel/module.c > +++ b/arch/x86/kernel/module.c > @@ -128,18 +128,20 @@ int apply_relocate(Elf32_Shdr *sechdrs, > return 0; > } > #else /*X86_64*/ > -static int __apply_relocate_add(Elf64_Shdr *sechdrs, > +static int __apply_clear_relocate_add(Elf64_Shdr *sechdrs, Nit: Honestly, the combination of 4 verbs: "apply", "clear, "relocate", and "add" is really crazy. It is far from obvious what the function does. The name was not ideal even before. Let's not make it worse and use on 3 verbs again. What about __update_relocate_add or __write_relocate_add()? Note that the "__" prefix is still needed, see below. > const char *strtab, > unsigned int symindex, > unsigned int relsec, > struct module *me, > - void *(*write)(void *dest, const void *src, size_t len)) > + void *(*write)(void *dest, const void *src, size_t len), > + bool clear) > { > unsigned int i; > Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; > Elf64_Sym *sym; > void *loc; > u64 val; > + u64 zero = 0ULL; > > DEBUGP("Applying relocate section %u to %u\n", > relsec, sechdrs[relsec].sh_info); > @@ -163,40 +165,60 @@ static int __apply_relocate_add(Elf64_Shdr *sechdrs, > case R_X86_64_NONE: > break; > case R_X86_64_64: > - if (*(u64 *)loc != 0) > - goto invalid_relocation; > - write(loc, &val, 8); > + if (!clear) { Nit: I would prefer to use positive check when both if/else branches are used. I would call the parameter "apply". > + if (*(u64 *)loc != 0) > + goto invalid_relocation; > + write(loc, &val, 8); > + } else { > + write(loc, &zero, 8); > + } > break; > case R_X86_64_32: > - if (*(u32 *)loc != 0) > - goto invalid_relocation; > - write(loc, &val, 4); > - if (val != *(u32 *)loc) > - goto overflow; > + if (!clear) { > + if (*(u32 *)loc != 0) > + goto invalid_relocation; > + write(loc, &val, 4); > + if (val != *(u32 *)loc) > + goto overflow; > + } else { > + write(loc, &zero, 4); > + } > break; > case R_X86_64_32S: > - if (*(s32 *)loc != 0) > - goto invalid_relocation; > - write(loc, &val, 4); > - if ((s64)val != *(s32 *)loc) > - goto overflow; > + if (!clear) { > + if (*(s32 *)loc != 0) > + goto invalid_relocation; > + write(loc, &val, 4); > + if ((s64)val != *(s32 *)loc) > + goto overflow; > + } else { > + write(loc, &zero, 4); > + } > break; > case R_X86_64_PC32: > case R_X86_64_PLT32: > - if (*(u32 *)loc != 0) > - goto invalid_relocation; > - val -= (u64)loc; > - write(loc, &val, 4); > + if (!clear) { > + if (*(u32 *)loc != 0) > + goto invalid_relocation; > + val -= (u64)loc; > + write(loc, &val, 4); > #if 0 > - if ((s64)val != *(s32 *)loc) > - goto overflow; > + if ((s64)val != *(s32 *)loc) > + goto overflow; > #endif > + } else { > + write(loc, &zero, 4); > + } > break; > case R_X86_64_PC64: > - if (*(u64 *)loc != 0) > - goto invalid_relocation; > - val -= (u64)loc; > - write(loc, &val, 8); > + if (!clear) { > + if (*(u64 *)loc != 0) > + goto invalid_relocation; > + val -= (u64)loc; > + write(loc, &val, 8); > + } else { > + write(loc, &zero, 8); > + } > break; > default: > pr_err("%s: Unknown rela relocation: %llu\n", > @@ -245,6 +267,32 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, > return ret; > } > > +#ifdef CONFIG_LIVEPATCH > + > +void clear_relocate_add(Elf64_Shdr *sechdrs, > + const char *strtab, > + unsigned int symindex, > + unsigned int relsec, > + struct module *me) > +{ > + bool early = me->state == MODULE_STATE_UNFORMED; > + void *(*write)(void *, const void *, size_t) = memcpy; > + > + if (!early) { > + write = text_poke; > + mutex_lock(&text_mutex); > + } > + > + __apply_clear_relocate_add(sechdrs, strtab, symindex, relsec, me, > + write, true /* clear */); > + > + if (!early) { > + text_poke_sync(); > + mutex_unlock(&text_mutex); > + } > +} This duplicates a lot of code. Please, rename apply_relocate_add() the same way as __apply_clear_relocate_add() and add the "apply" parameter. Then add the wrappers for this: int write_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me, bool apply) { int ret; bool early = me->state == MODULE_STATE_UNFORMED; void *(*write)(void *, const void *, size_t) = memcpy; if (!early) { write = text_poke; mutex_lock(&text_mutex); } ret = __write_relocate_add(sechdrs, strtab, symindex, relsec, me, write, apply); if (!early) { text_poke_sync(); mutex_unlock(&text_mutex); } return ret; } int apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { return write_relocate_add(sechdrs, strtab, symindex, relsec, me, true); } #ifdef CONFIG_LIVEPATCH void apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { write_relocate_add(sechdrs, strtab, symindex, relsec, me, false); } #endif > +#endif > + > #endif > > int module_finalize(const Elf_Ehdr *hdr, > --- a/kernel/livepatch/core.c > +++ b/kernel/livepatch/core.c > @@ -316,6 +316,45 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, > return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod); > } > > +static void klp_clear_object_relocations(struct module *pmod, > + struct klp_object *obj) > +{ > + int i, cnt; > + const char *objname, *secname; > + char sec_objname[MODULE_NAME_LEN]; > + Elf_Shdr *sec; > + > + objname = klp_is_module(obj) ? obj->name : "vmlinux"; > + > + /* For each klp relocation section */ > + for (i = 1; i < pmod->klp_info->hdr.e_shnum; i++) { > + sec = pmod->klp_info->sechdrs + i; > + secname = pmod->klp_info->secstrings + sec->sh_name; > + if (!(sec->sh_flags & SHF_RELA_LIVEPATCH)) > + continue; > + > + /* > + * Format: .klp.rela.sec_objname.section_name > + * See comment in klp_resolve_symbols() for an explanation > + * of the selected field width value. > + */ > + secname = pmod->klp_info->secstrings + sec->sh_name; > + cnt = sscanf(secname, ".klp.rela.%55[^.]", sec_objname); > + if (cnt != 1) { > + pr_err("section %s has an incorrectly formatted name\n", > + secname); > + continue; > + } > + > + if (strcmp(objname, sec_objname)) > + continue; > + > + clear_relocate_add(pmod->klp_info->sechdrs, > + pmod->core_kallsyms.strtab, > + pmod->klp_info->symndx, i, pmod); > + } > +} Huh, this duplicates a lot of tricky code. It is even worse because this squashed code from two functions klp_apply_section_relocs() and klp_apply_object_relocs() into a single function. As a result, the code duplication is not even obvious. Also the suffix "_reloacations() does not match the suffix of the related funciton: + klp_apply_object_relocs() (existing) + klp_clear_object_relocations() (new) This all would complicate maintenance of the code. Please, implement a common: int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, const char *shstrtab, const char *strtab, unsigned int symndx, unsigned int secndx, const char *objname, bool apply); and int klp_write_object_relocs(struct klp_patch *patch, struct klp_object *obj, bool apply); and add the respective wrappers: int klp_apply_section_relocs(); /* also needed in module/main.c */ int klp_apply_object_relocs(); void klp_clear_object_relocs(); Best Regards, Petr