From patchwork Wed Apr 10 15:50:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894301 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D4C3F13B5 for ; Wed, 10 Apr 2019 15:52:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA24228B58 for ; Wed, 10 Apr 2019 15:52:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B810228B5A; Wed, 10 Apr 2019 15:52:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 31C3028B62 for ; Wed, 10 Apr 2019 15:52:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727229AbfDJPvy (ORCPT ); Wed, 10 Apr 2019 11:51:54 -0400 Received: from mx1.redhat.com ([209.132.183.28]:45081 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387476AbfDJPvH (ORCPT ); Wed, 10 Apr 2019 11:51:07 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 13E6E6DDD6; Wed, 10 Apr 2019 15:51:06 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8045D1017B2E; Wed, 10 Apr 2019 15:51:04 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 1/9] livepatch: Create and include UAPI headers Date: Wed, 10 Apr 2019 11:50:50 -0400 Message-Id: <20190410155058.9437-2-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Wed, 10 Apr 2019 15:51:06 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Josh Poimboeuf Define klp prefixes in include/uapi/linux/livepatch.h, and use them for replacing hard-coded values in kernel/livepatch/core.c. Update MAINTAINERS. Note: Add defines to uapi as these are also to be used by a newly introduced klp-convert script. Signed-off-by: Josh Poimboeuf Signed-off-by: Joao Moreira Signed-off-by: Joe Lawrence --- MAINTAINERS | 1 + include/linux/livepatch.h | 1 + include/uapi/linux/livepatch.h | 17 +++++++++++++++++ kernel/livepatch/core.c | 4 ++-- 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 include/uapi/linux/livepatch.h diff --git a/MAINTAINERS b/MAINTAINERS index 2359e12e4c41..65b4e8f481f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9017,6 +9017,7 @@ R: Joe Lawrence S: Maintained F: kernel/livepatch/ F: include/linux/livepatch.h +F: include/uapi/linux/livepatch.h F: arch/x86/include/asm/livepatch.h F: arch/x86/kernel/livepatch.c F: Documentation/livepatch/ diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 53551f470722..16b48e8b29a2 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -25,6 +25,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_LIVEPATCH) diff --git a/include/uapi/linux/livepatch.h b/include/uapi/linux/livepatch.h new file mode 100644 index 000000000000..bb86243de805 --- /dev/null +++ b/include/uapi/linux/livepatch.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * livepatch.h - Kernel Live Patching Core + * + * Copyright (C) 2016 Josh Poimboeuf + */ + +#ifndef _UAPI_LIVEPATCH_H +#define _UAPI_LIVEPATCH_H + +#include + +#define KLP_RELA_PREFIX ".klp.rela." +#define KLP_SYM_PREFIX ".klp.sym." + +#endif /* _UAPI_LIVEPATCH_H */ diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index eb0ee10a1981..3d9ed895b252 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -235,7 +235,7 @@ static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod) /* Format: .klp.sym.objname.symname,sympos */ cnt = sscanf(strtab + sym->st_name, - ".klp.sym.%55[^.].%127[^,],%lu", + KLP_SYM_PREFIX "%55[^.].%127[^,],%lu", objname, symname, &sympos); if (cnt != 3) { pr_err("symbol %s has an incorrectly formatted name\n", @@ -281,7 +281,7 @@ static int klp_write_object_relocations(struct module *pmod, * See comment in klp_resolve_symbols() for an explanation * of the selected field width value. */ - cnt = sscanf(secname, ".klp.rela.%55[^.]", sec_objname); + cnt = sscanf(secname, KLP_RELA_PREFIX "%55[^.]", sec_objname); if (cnt != 1) { pr_err("section %s has an incorrectly formatted name\n", secname); From patchwork Wed Apr 10 15:50:51 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894285 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F0ECD13B5 for ; Wed, 10 Apr 2019 15:51:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D1D8028BAB for ; Wed, 10 Apr 2019 15:51:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CFAC928BC3; Wed, 10 Apr 2019 15:51:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DC88228BD8 for ; Wed, 10 Apr 2019 15:51:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387484AbfDJPvK (ORCPT ); Wed, 10 Apr 2019 11:51:10 -0400 Received: from mx1.redhat.com ([209.132.183.28]:17094 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387480AbfDJPvH (ORCPT ); Wed, 10 Apr 2019 11:51:07 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6D4B58B139; Wed, 10 Apr 2019 15:51:07 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 07F791017B45; Wed, 10 Apr 2019 15:51:05 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 2/9] kbuild: Support for Symbols.list creation Date: Wed, 10 Apr 2019 11:50:51 -0400 Message-Id: <20190410155058.9437-3-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Wed, 10 Apr 2019 15:51:07 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Joao Moreira For automatic resolution of livepatch relocations, a file called Symbols.list is used. This file maps symbols within every compiled kernel object allowing the identification of symbols whose name is unique, thus relocation can be automatically inferred, or providing information that helps developers when code annotation is required for solving the matter. Add support for creating Symbols.list in the main Makefile. First, ensure that built-in is compiled when CONFIG_LIVEPATCH is enabled (as required to achieve a complete Symbols.list file). Define the command to build Symbols.list (cmd_klp_map) and hook it in the modules rule. As it is undesirable to have symbols from livepatch objects inside Symbols.list, make livepatches discernible by modifying scripts/Makefile.build to create a .livepatch file for each livepatch in $(MODVERDIR). This file then used by cmd_klp_map to identify and bypass livepatches. For identifying livepatches during the build process, a flag variable LIVEPATCH_$(basetarget).o is considered in scripts/Makefile.build. This way, set this flag for the livepatch sample Makefile in samples/livepatch/Makefile. Finally, Add a clean rule to ensure that Symbols.list is removed during clean. Notes: To achieve a correct Symbols.list file, all kernel objects must be considered, thus, its construction require these objects to be priorly built. On the other hand, invoking scripts/Makefile.modpost without having a complete Symbols.list in place would occasionally lead to in-tree livepatches being post-processed incorrectly. To prevent this from becoming a circular dependency, the construction of Symbols.list uses non-post-processed kernel objects and such does not cause harm as the symbols normally referenced from within livepatches are visible at this stage. Also due to these requirements, the spot in-between modules compilation and the invocation of scripts/Makefile.modpost was picked for hooking cmd_klp_map. The approach based on .livepatch files was proposed as an alternative to using MODULE_INFO statements. This approach was originally proposed by Miroslav Benes as a workaround for identifying livepathes without depending on modinfo during the modpost stage. It was moved to this patch as the approach also shown to be useful while building Symbols.list. Signed-off-by: Joao Moreira Signed-off-by: Joe Lawrence --- .gitignore | 1 + Makefile | 30 ++++++++++++++++++++++++++---- samples/livepatch/Makefile | 1 + scripts/Makefile.build | 7 +++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a20ac26aa2f5..5cd5758f5ffe 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ *.xz Module.symvers modules.builtin +Symbols.list # # Top-level generic files diff --git a/Makefile b/Makefile index 15c8251d4d5e..2c07bdd87b2f 100644 --- a/Makefile +++ b/Makefile @@ -574,10 +574,13 @@ KBUILD_BUILTIN := 1 # If we have only "make modules", don't compile built-in objects. # When we're building modules with modversions, we need to consider # the built-in objects during the descend as well, in order to -# make sure the checksums are up to date before we record them. +# make sure the checksums are up to date before we record them. The +# same applies for building livepatches, as built-in objects may hold +# symbols which are referenced from livepatches and are required by +# klp-convert post-processing tool for resolving these cases. ifeq ($(MAKECMDGOALS),modules) - KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) + KBUILD_BUILTIN := $(if $(or $(CONFIG_MODVERSIONS), $(CONFIG_LIVEPATCH)),1) endif # If we have "make modules", compile modules @@ -1261,9 +1264,25 @@ all: modules # duplicate lines in modules.order files. Those are removed # using awk while concatenating to the final file. +quiet_cmd_klp_map = KLP Symbols.list +SLIST = $(objtree)/Symbols.list + +define cmd_klp_map + $(shell echo "klp-convert-symbol-data.0.1" > $(SLIST)) \ + $(shell echo "*vmlinux" >> $(SLIST)) \ + $(shell nm -f posix $(objtree)/vmlinux | cut -d\ -f1 >> $(SLIST)) \ + $(foreach m, $(wildcard $(MODVERDIR)/*.mod), \ + $(eval mod = $(patsubst %.ko,%.o,$(shell head -n1 $(m)))) \ + $(if $(wildcard $(MODVERDIR)/$(shell basename -s .o $(mod)).livepatch),,\ + $(eval fmod = $(subst $(quote),_,$(subst -,_,$(mod)))) \ + $(shell echo "*$(shell basename -s .o $(fmod))" >> $(SLIST)) \ + $(shell nm -f posix $(mod) | cut -d\ -f1 >> $(SLIST)))) +endef + PHONY += modules modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin $(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order + $(if $(CONFIG_LIVEPATCH), $(call cmd,klp_map)) @$(kecho) ' Building modules, stage 2.'; $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost @@ -1350,7 +1369,7 @@ clean: rm-dirs := $(CLEAN_DIRS) clean: rm-files := $(CLEAN_FILES) clean-dirs := $(addprefix _clean_, . $(vmlinux-alldirs) Documentation samples) -PHONY += $(clean-dirs) clean archclean vmlinuxclean +PHONY += $(clean-dirs) clean archclean vmlinuxclean klpclean $(clean-dirs): $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) @@ -1358,7 +1377,10 @@ vmlinuxclean: $(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean $(Q)$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) clean) -clean: archclean vmlinuxclean +klpclean: + $(Q) rm -f $(objtree)/Symbols.list + +clean: archclean vmlinuxclean klpclean # mrproper - Delete all generated files, including .config # diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile index 2472ce39a18d..8b9b42a258ad 100644 --- a/samples/livepatch/Makefile +++ b/samples/livepatch/Makefile @@ -1,3 +1,4 @@ +LIVEPATCH_livepatch-sample := y obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 76ca30cc4791..ca76bd2080f0 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -246,6 +246,11 @@ cmd_gen_ksymdeps = \ $(CONFIG_SHELL) $(srctree)/scripts/gen_ksymdeps.sh $@ >> $(dot-target).cmd endif +ifdef CONFIG_LIVEPATCH +cmd_livepatch = $(if $(LIVEPATCH_$(basetarget)), \ + $(shell touch $(MODVERDIR)/$(basetarget).livepatch)) +endif + define rule_cc_o_c $(call cmd,checksrc) $(call cmd_and_fixdep,cc_o_c) @@ -280,6 +285,7 @@ $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE $(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE $(call cmd,force_checksrc) $(call if_changed_rule,cc_o_c) + $(call cmd_livepatch) @{ echo $(@:.o=.ko); echo $@; \ $(cmd_undef_syms); } > $(MODVERDIR)/$(@F:.o=.mod) @@ -456,6 +462,7 @@ cmd_link_multi-m = $(LD) $(ld_flags) -r -o $@ $(filter %.o,$^) $(cmd_secanalysis $(multi-used-m): FORCE $(call if_changed,link_multi-m) + $(call cmd,livepatch) @{ echo $(@:.o=.ko); echo $(filter %.o,$^); \ $(cmd_undef_syms); } > $(MODVERDIR)/$(@F:.o=.mod) $(call multi_depend, $(multi-used-m), .o, -objs -y -m) From patchwork Wed Apr 10 15:50:52 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894287 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C941D13B5 for ; Wed, 10 Apr 2019 15:51:14 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A765C289CA for ; Wed, 10 Apr 2019 15:51:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A48EF28B17; Wed, 10 Apr 2019 15:51:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 25581289CA for ; Wed, 10 Apr 2019 15:51:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387488AbfDJPvK (ORCPT ); Wed, 10 Apr 2019 11:51:10 -0400 Received: from mx1.redhat.com ([209.132.183.28]:44529 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729235AbfDJPvJ (ORCPT ); Wed, 10 Apr 2019 11:51:09 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id F24183167E40; Wed, 10 Apr 2019 15:51:08 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 88725110A181; Wed, 10 Apr 2019 15:51:07 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 3/9] livepatch: Add klp-convert tool Date: Wed, 10 Apr 2019 11:50:52 -0400 Message-Id: <20190410155058.9437-4-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.41]); Wed, 10 Apr 2019 15:51:09 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Josh Poimboeuf Livepatches may use symbols which are not contained in its own scope, and, because of that, may end up compiled with relocations that will only be resolved during module load. Yet, when the referenced symbols are not exported, solving this relocation requires information on the object that holds the symbol (either vmlinux or modules) and its position inside the object, as an object may contain multiple symbols with the same name. Providing such information must be done accordingly to what is specified in Documentation/livepatch/module-elf-format.txt. Currently, there is no trivial way to embed the required information as requested in the final livepatch elf object. klp-convert solves this problem in two different forms: (i) by relying on Symbols.list, which is built during kernel compilation, to automatically infer the relocation targeted symbol, and, when such inference is not possible (ii) by using annotations in the elf object to convert the relocation accordingly to the specification, enabling it to be handled by the livepatch loader. Given the above, create scripts/livepatch to hold tools developed for livepatches and add source files for klp-convert there. The core file of klp-convert is scripts/livepatch/klp-convert.c, which implements the heuristics used to solve the relocations and the conversion of unresolved symbols into the expected format, as defined in [1]. klp-convert receives as arguments the Symbols.list file, an input livepatch module to be converted and the output name for the converted livepatch. When it starts running, klp-convert parses Symbols.list and builds two internal lists of symbols, one containing the exported and another containing the non-exported symbols. Then, by parsing the rela sections in the elf object, klp-convert identifies which symbols must be converted, which are those unresolved and that do not have a corresponding exported symbol, and attempts to convert them accordingly to the specification. By using Symbols.list, klp-convert identifies which symbols have names that only appear in a single kernel object, thus being capable of resolving these cases without the intervention of the developer. When various homonymous symbols exist through kernel objects, it is not possible to infer the right one, thus klp-convert falls back into using developer annotations. If these were not provided, then the tool will print a list with all acceptable targets for the symbol being processed. Annotations in the context of klp-convert are accessible as struct klp_module_reloc entries in sections named .klp.module_relocs.. These entries are pairs of symbol references and positions which are to be resolved against definitions in . Define the structure klp_module_reloc in include/linux/uapi/livepatch.h allowing developers to annotate the livepatch source code with it. klp-convert relies on libelf and on a list implementation. Add files scripts/livepatch/elf.c and scripts/livepatch/elf.h, which are a libelf interfacing layer and scripts/livepatch/list.h, which is a list implementation. Update Makefiles to correctly support the compilation of the new tool, update MAINTAINERS file and add a .gitignore file. [1] - Documentation/livepatch/module-elf-format.txt Signed-off-by: Josh Poimboeuf Signed-off-by: Konstantin Khlebnikov Signed-off-by: Joao Moreira Signed-off-by: Joe Lawrence --- MAINTAINERS | 1 + include/uapi/linux/livepatch.h | 5 + scripts/Makefile | 1 + scripts/livepatch/.gitignore | 1 + scripts/livepatch/Makefile | 7 + scripts/livepatch/elf.c | 753 ++++++++++++++++++++++++++++++++ scripts/livepatch/elf.h | 72 +++ scripts/livepatch/klp-convert.c | 692 +++++++++++++++++++++++++++++ scripts/livepatch/klp-convert.h | 39 ++ scripts/livepatch/list.h | 391 +++++++++++++++++ 10 files changed, 1962 insertions(+) create mode 100644 scripts/livepatch/.gitignore create mode 100644 scripts/livepatch/Makefile create mode 100644 scripts/livepatch/elf.c create mode 100644 scripts/livepatch/elf.h create mode 100644 scripts/livepatch/klp-convert.c create mode 100644 scripts/livepatch/klp-convert.h create mode 100644 scripts/livepatch/list.h diff --git a/MAINTAINERS b/MAINTAINERS index 65b4e8f481f0..5f66ad2e54f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9023,6 +9023,7 @@ F: arch/x86/kernel/livepatch.c F: Documentation/livepatch/ F: Documentation/ABI/testing/sysfs-kernel-livepatch F: samples/livepatch/ +F: scripts/livepatch/ F: tools/testing/selftests/livepatch/ L: live-patching@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching.git diff --git a/include/uapi/linux/livepatch.h b/include/uapi/linux/livepatch.h index bb86243de805..a50ff68bec12 100644 --- a/include/uapi/linux/livepatch.h +++ b/include/uapi/linux/livepatch.h @@ -14,4 +14,9 @@ #define KLP_RELA_PREFIX ".klp.rela." #define KLP_SYM_PREFIX ".klp.sym." +struct klp_module_reloc { + void *sym; + unsigned int sympos; +} __attribute__((packed)); + #endif /* _UAPI_LIVEPATCH_H */ diff --git a/scripts/Makefile b/scripts/Makefile index 9d442ee050bd..bf9ce74b70b0 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -39,6 +39,7 @@ build_unifdef: $(obj)/unifdef subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins subdir-$(CONFIG_MODVERSIONS) += genksyms subdir-$(CONFIG_SECURITY_SELINUX) += selinux +subdir-$(CONFIG_LIVEPATCH) += livepatch # Let clean descend into subdirs subdir- += basic dtc gdb kconfig mod package diff --git a/scripts/livepatch/.gitignore b/scripts/livepatch/.gitignore new file mode 100644 index 000000000000..dc22fe4b6a5b --- /dev/null +++ b/scripts/livepatch/.gitignore @@ -0,0 +1 @@ +klp-convert diff --git a/scripts/livepatch/Makefile b/scripts/livepatch/Makefile new file mode 100644 index 000000000000..2842ecdba3fd --- /dev/null +++ b/scripts/livepatch/Makefile @@ -0,0 +1,7 @@ +hostprogs-y := klp-convert +always := $(hostprogs-y) + +klp-convert-objs := klp-convert.o elf.o + +HOST_EXTRACFLAGS := -g -I$(INSTALL_HDR_PATH)/include -Wall +HOSTLDLIBS_klp-convert := -lelf diff --git a/scripts/livepatch/elf.c b/scripts/livepatch/elf.c new file mode 100644 index 000000000000..11746bccecf2 --- /dev/null +++ b/scripts/livepatch/elf.c @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * elf.c - ELF access library + * + * Adapted from kpatch (https://github.com/dynup/kpatch): + * Copyright (C) 2013-2016 Josh Poimboeuf + * Copyright (C) 2014 Seth Jennings + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "elf.h" + +#define WARN(format, ...) \ + fprintf(stderr, "%s: " format "\n", elf->name, ##__VA_ARGS__) + +/* + * Fallback for systems without this "read, mmaping if possible" cmd. + */ +#ifndef ELF_C_READ_MMAP +#define ELF_C_READ_MMAP ELF_C_READ +#endif + +bool is_rela_section(struct section *sec) +{ + return (sec->sh.sh_type == SHT_RELA); +} + +struct section *find_section_by_name(struct elf *elf, const char *name) +{ + struct section *sec; + + list_for_each_entry(sec, &elf->sections, list) + if (!strcmp(sec->name, name)) + return sec; + + return NULL; +} + +static struct section *find_section_by_index(struct elf *elf, + unsigned int idx) +{ + struct section *sec; + + list_for_each_entry(sec, &elf->sections, list) + if (sec->idx == idx) + return sec; + + return NULL; +} + +static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) +{ + struct symbol *sym; + + list_for_each_entry(sym, &elf->symbols, list) + if (sym->idx == idx) + return sym; + + return NULL; +} + +static int read_sections(struct elf *elf) +{ + Elf_Scn *s = NULL; + struct section *sec; + size_t shstrndx, sections_nr; + int i; + + if (elf_getshdrnum(elf->elf, §ions_nr)) { + perror("elf_getshdrnum"); + return -1; + } + + if (elf_getshdrstrndx(elf->elf, &shstrndx)) { + perror("elf_getshdrstrndx"); + return -1; + } + + for (i = 0; i < sections_nr; i++) { + sec = malloc(sizeof(*sec)); + if (!sec) { + perror("malloc"); + return -1; + } + memset(sec, 0, sizeof(*sec)); + + INIT_LIST_HEAD(&sec->relas); + + list_add_tail(&sec->list, &elf->sections); + + s = elf_getscn(elf->elf, i); + if (!s) { + perror("elf_getscn"); + return -1; + } + + sec->idx = elf_ndxscn(s); + + if (!gelf_getshdr(s, &sec->sh)) { + perror("gelf_getshdr"); + return -1; + } + + sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); + if (!sec->name) { + perror("elf_strptr"); + return -1; + } + + sec->elf_data = elf_getdata(s, NULL); + if (!sec->elf_data) { + perror("elf_getdata"); + return -1; + } + + if (sec->elf_data->d_off != 0 || + sec->elf_data->d_size != sec->sh.sh_size) { + WARN("unexpected data attributes for %s", sec->name); + return -1; + } + + sec->data = sec->elf_data->d_buf; + sec->size = sec->elf_data->d_size; + } + + /* sanity check, one more call to elf_nextscn() should return NULL */ + if (elf_nextscn(elf->elf, s)) { + WARN("section entry mismatch"); + return -1; + } + + return 0; +} + +static int read_symbols(struct elf *elf) +{ + struct section *symtab; + struct symbol *sym; + int symbols_nr, i; + + symtab = find_section_by_name(elf, ".symtab"); + if (!symtab) { + WARN("missing symbol table"); + return -1; + } + + symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; + + for (i = 0; i < symbols_nr; i++) { + sym = malloc(sizeof(*sym)); + if (!sym) { + perror("malloc"); + return -1; + } + memset(sym, 0, sizeof(*sym)); + + sym->idx = i; + + if (!gelf_getsym(symtab->elf_data, i, &sym->sym)) { + perror("gelf_getsym"); + goto err; + } + + sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, + sym->sym.st_name); + if (!sym->name) { + perror("elf_strptr"); + goto err; + } + + sym->type = GELF_ST_TYPE(sym->sym.st_info); + sym->bind = GELF_ST_BIND(sym->sym.st_info); + + if (sym->sym.st_shndx > SHN_UNDEF && + sym->sym.st_shndx < SHN_LORESERVE) { + sym->sec = find_section_by_index(elf, + sym->sym.st_shndx); + if (!sym->sec) { + WARN("couldn't find section for symbol %s", + sym->name); + goto err; + } + if (sym->type == STT_SECTION) { + sym->name = sym->sec->name; + sym->sec->sym = sym; + } + } + + sym->offset = sym->sym.st_value; + sym->size = sym->sym.st_size; + + list_add_tail(&sym->list, &elf->symbols); + } + + return 0; + +err: + free(sym); + return -1; +} + +static int read_relas(struct elf *elf) +{ + struct section *sec; + struct rela *rela; + int i; + unsigned int symndx; + + list_for_each_entry(sec, &elf->sections, list) { + if (sec->sh.sh_type != SHT_RELA) + continue; + + sec->base = find_section_by_name(elf, sec->name + 5); + if (!sec->base) { + WARN("can't find base section for rela section %s", + sec->name); + return -1; + } + + sec->base->rela = sec; + + for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { + rela = malloc(sizeof(*rela)); + if (!rela) { + perror("malloc"); + return -1; + } + memset(rela, 0, sizeof(*rela)); + + if (!gelf_getrela(sec->elf_data, i, &rela->rela)) { + perror("gelf_getrela"); + return -1; + } + + rela->type = GELF_R_TYPE(rela->rela.r_info); + rela->addend = rela->rela.r_addend; + rela->offset = rela->rela.r_offset; + symndx = GELF_R_SYM(rela->rela.r_info); + rela->sym = find_symbol_by_index(elf, symndx); + if (!rela->sym) { + WARN("can't find rela entry symbol %d for %s", + symndx, sec->name); + return -1; + } + + list_add_tail(&rela->list, &sec->relas); + } + } + + return 0; +} + +struct section *create_rela_section(struct elf *elf, const char *name, + struct section *base) +{ + struct section *sec; + + sec = malloc(sizeof(*sec)); + if (!sec) { + WARN("malloc failed"); + return NULL; + } + memset(sec, 0, sizeof(*sec)); + INIT_LIST_HEAD(&sec->relas); + + sec->base = base; + sec->name = strdup(name); + if (!sec->name) { + WARN("strdup failed"); + return NULL; + } + sec->sh.sh_name = -1; + sec->sh.sh_type = SHT_RELA; + sec->sh.sh_entsize = sizeof(GElf_Rela); + sec->sh.sh_addralign = 8; + sec->sh.sh_flags = SHF_ALLOC; + + sec->elf_data = malloc(sizeof(*sec->elf_data)); + if (!sec->elf_data) { + WARN("malloc failed"); + return NULL; + } + memset(sec->elf_data, 0, sizeof(*sec->elf_data)); + sec->elf_data->d_type = ELF_T_RELA; + + list_add_tail(&sec->list, &elf->sections); + + return sec; +} + +static int update_shstrtab(struct elf *elf) +{ + struct section *shstrtab, *sec; + size_t orig_size, new_size = 0, offset, len; + char *buf; + + shstrtab = find_section_by_name(elf, ".shstrtab"); + if (!shstrtab) { + WARN("can't find .shstrtab"); + return -1; + } + + orig_size = new_size = shstrtab->size; + + list_for_each_entry(sec, &elf->sections, list) { + if (sec->sh.sh_name != -1) + continue; + new_size += strlen(sec->name) + 1; + } + + if (new_size == orig_size) + return 0; + + buf = malloc(new_size); + if (!buf) { + WARN("malloc failed"); + return -1; + } + memcpy(buf, (void *)shstrtab->data, orig_size); + + offset = orig_size; + list_for_each_entry(sec, &elf->sections, list) { + if (sec->sh.sh_name != -1) + continue; + sec->sh.sh_name = offset; + len = strlen(sec->name) + 1; + memcpy(buf + offset, sec->name, len); + offset += len; + } + + shstrtab->elf_data->d_buf = shstrtab->data = buf; + shstrtab->elf_data->d_size = shstrtab->size = new_size; + shstrtab->sh.sh_size = new_size; + + return 1; +} + +static void free_shstrtab(struct elf *elf) +{ + struct section *shstrtab; + + shstrtab = find_section_by_name(elf, ".shstrtab"); + if (!shstrtab) + return; + + free(shstrtab->elf_data->d_buf); +} + +static int update_strtab(struct elf *elf) +{ + struct section *strtab; + struct symbol *sym; + size_t orig_size, new_size = 0, offset, len; + char *buf; + + strtab = find_section_by_name(elf, ".strtab"); + if (!strtab) { + WARN("can't find .strtab"); + return -1; + } + + orig_size = new_size = strtab->size; + + list_for_each_entry(sym, &elf->symbols, list) { + if (sym->sym.st_name != -1) + continue; + new_size += strlen(sym->name) + 1; + } + + if (new_size == orig_size) + return 0; + + buf = malloc(new_size); + if (!buf) { + WARN("malloc failed"); + return -1; + } + memcpy(buf, (void *)strtab->data, orig_size); + + offset = orig_size; + list_for_each_entry(sym, &elf->symbols, list) { + if (sym->sym.st_name != -1) + continue; + sym->sym.st_name = offset; + len = strlen(sym->name) + 1; + memcpy(buf + offset, sym->name, len); + offset += len; + } + + strtab->elf_data->d_buf = strtab->data = buf; + strtab->elf_data->d_size = strtab->size = new_size; + strtab->sh.sh_size = new_size; + + return 1; +} + +static void free_strtab(struct elf *elf) +{ + struct section *strtab; + + strtab = find_section_by_name(elf, ".strtab"); + if (!strtab) + return; + + if (strtab->elf_data) + free(strtab->elf_data->d_buf); +} + +static int update_symtab(struct elf *elf) +{ + struct section *symtab, *sec; + struct symbol *sym; + char *buf; + size_t size; + int offset = 0, nr_locals = 0, idx, nr_syms; + + idx = 0; + list_for_each_entry(sec, &elf->sections, list) + sec->idx = idx++; + + idx = 0; + list_for_each_entry(sym, &elf->symbols, list) { + sym->idx = idx++; + if (sym->sec) + sym->sym.st_shndx = sym->sec->idx; + } + nr_syms = idx; + + symtab = find_section_by_name(elf, ".symtab"); + if (!symtab) { + WARN("can't find symtab"); + return -1; + } + + symtab->sh.sh_link = find_section_by_name(elf, ".strtab")->idx; + + /* create new symtab buffer */ + size = nr_syms * symtab->sh.sh_entsize; + buf = malloc(size); + if (!buf) { + WARN("malloc failed"); + return -1; + } + memset(buf, 0, size); + + offset = 0; + list_for_each_entry(sym, &elf->symbols, list) { + memcpy(buf + offset, &sym->sym, symtab->sh.sh_entsize); + offset += symtab->sh.sh_entsize; + + if (sym->bind == STB_LOCAL) + nr_locals++; + } + + symtab->elf_data->d_buf = symtab->data = buf; + symtab->elf_data->d_size = symtab->size = size; + symtab->sh.sh_size = size; + + /* update symtab section header */ + symtab->sh.sh_info = nr_locals; + + return 1; +} + +static void free_symtab(struct elf *elf) +{ + struct section *symtab; + + symtab = find_section_by_name(elf, ".symtab"); + if (!symtab) + return; + + free(symtab->elf_data->d_buf); +} + +static int update_relas(struct elf *elf) +{ + struct section *sec, *symtab; + struct rela *rela; + int nr_relas, idx, size; + GElf_Rela *relas; + + symtab = find_section_by_name(elf, ".symtab"); + + list_for_each_entry(sec, &elf->sections, list) { + if (!is_rela_section(sec)) + continue; + + sec->sh.sh_link = symtab->idx; + if (sec->base) + sec->sh.sh_info = sec->base->idx; + + nr_relas = 0; + list_for_each_entry(rela, &sec->relas, list) + nr_relas++; + + size = nr_relas * sizeof(*relas); + relas = malloc(size); + if (!relas) { + WARN("malloc failed"); + return -1; + } + + sec->elf_data->d_buf = sec->data = relas; + sec->elf_data->d_size = sec->size = size; + sec->sh.sh_size = size; + + idx = 0; + list_for_each_entry(rela, &sec->relas, list) { + relas[idx].r_offset = rela->offset; + relas[idx].r_addend = rela->addend; + relas[idx].r_info = GELF_R_INFO(rela->sym->idx, + rela->type); + idx++; + } + } + + return 1; +} + +static void free_relas(struct elf *elf) +{ + struct section *sec, *symtab; + + symtab = find_section_by_name(elf, ".symtab"); + if (!symtab) + return; + + list_for_each_entry(sec, &elf->sections, list) { + if (!is_rela_section(sec)) + continue; + + free(sec->elf_data->d_buf); + } +} + +static int write_file(struct elf *elf, const char *file) +{ + int fd; + Elf *e; + GElf_Ehdr eh, ehout; + Elf_Scn *scn; + Elf_Data *data; + GElf_Shdr sh; + struct section *sec; + + fd = creat(file, 0664); + if (fd == -1) { + WARN("couldn't create %s", file); + return -1; + } + + e = elf_begin(fd, ELF_C_WRITE, NULL); + if (!e) { + WARN("elf_begin failed"); + return -1; + } + + if (!gelf_newehdr(e, gelf_getclass(elf->elf))) { + WARN("gelf_newehdr failed"); + return -1; + } + + if (!gelf_getehdr(e, &ehout)) { + WARN("gelf_getehdr failed"); + return -1; + } + + if (!gelf_getehdr(elf->elf, &eh)) { + WARN("gelf_getehdr failed"); + return -1; + } + + memset(&ehout, 0, sizeof(ehout)); + ehout.e_ident[EI_DATA] = eh.e_ident[EI_DATA]; + ehout.e_machine = eh.e_machine; + ehout.e_type = eh.e_type; + ehout.e_version = EV_CURRENT; + ehout.e_shstrndx = find_section_by_name(elf, ".shstrtab")->idx; + + list_for_each_entry(sec, &elf->sections, list) { + if (!sec->idx) + continue; + scn = elf_newscn(e); + if (!scn) { + WARN("elf_newscn failed"); + return -1; + } + + data = elf_newdata(scn); + if (!data) { + WARN("elf_newdata failed"); + return -1; + } + + if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) { + WARN("elf_flagdata failed"); + return -1; + } + + data->d_type = sec->elf_data->d_type; + data->d_buf = sec->elf_data->d_buf; + data->d_size = sec->elf_data->d_size; + + if (!gelf_getshdr(scn, &sh)) { + WARN("gelf_getshdr failed"); + return -1; + } + + sh = sec->sh; + + if (!gelf_update_shdr(scn, &sh)) { + WARN("gelf_update_shdr failed"); + return -1; + } + } + + if (!gelf_update_ehdr(e, &ehout)) { + WARN("gelf_update_ehdr failed"); + return -1; + } + + if (elf_update(e, ELF_C_WRITE) < 0) { + fprintf(stderr, "%s\n", elf_errmsg(-1)); + WARN("elf_update failed"); + return -1; + } + + elf_end(e); + + return 0; +} + +int elf_write_file(struct elf *elf, const char *file) +{ + int ret_shstrtab; + int ret_strtab; + int ret_symtab; + int ret_relas; + int ret; + + ret_shstrtab = update_shstrtab(elf); + if (ret_shstrtab < 0) + return ret_shstrtab; + + ret_strtab = update_strtab(elf); + if (ret_strtab < 0) + return ret_strtab; + + ret_symtab = update_symtab(elf); + if (ret_symtab < 0) + return ret_symtab; + + ret_relas = update_relas(elf); + if (ret_relas < 0) + return ret_relas; + + ret = write_file(elf, file); + if (ret) + return ret; + + if (ret_relas > 0) + free_relas(elf); + if (ret_symtab > 0) + free_symtab(elf); + if (ret_strtab > 0) + free_strtab(elf); + if (ret_shstrtab > 0) + free_shstrtab(elf); + + return 0; +} + +struct elf *elf_open(const char *name) +{ + struct elf *elf; + + elf_version(EV_CURRENT); + + elf = malloc(sizeof(*elf)); + if (!elf) { + perror("malloc"); + return NULL; + } + memset(elf, 0, sizeof(*elf)); + + INIT_LIST_HEAD(&elf->sections); + INIT_LIST_HEAD(&elf->symbols); + + elf->fd = open(name, O_RDONLY); + if (elf->fd == -1) { + perror("open"); + goto err; + } + + elf->elf = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL); + if (!elf->elf) { + perror("elf_begin"); + goto err; + } + + if (!gelf_getehdr(elf->elf, &elf->ehdr)) { + perror("gelf_getehdr"); + goto err; + } + + if (read_sections(elf)) + goto err; + + if (read_symbols(elf)) + goto err; + + if (read_relas(elf)) + goto err; + + return elf; + +err: + elf_close(elf); + return NULL; +} + +void elf_close(struct elf *elf) +{ + struct section *sec, *tmpsec; + struct symbol *sym, *tmpsym; + struct rela *rela, *tmprela; + + list_for_each_entry_safe(sym, tmpsym, &elf->symbols, list) { + list_del(&sym->list); + free(sym); + } + list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { + list_for_each_entry_safe(rela, tmprela, &sec->relas, list) { + list_del(&rela->list); + free(rela); + } + list_del(&sec->list); + free(sec); + } + if (elf->fd > 0) + close(elf->fd); + if (elf->elf) + elf_end(elf->elf); + free(elf); +} diff --git a/scripts/livepatch/elf.h b/scripts/livepatch/elf.h new file mode 100644 index 000000000000..3b26dc28524f --- /dev/null +++ b/scripts/livepatch/elf.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2015-2016 Josh Poimboeuf + */ + +#ifndef _KLP_POST_ELF_H +#define _KLP_POST_ELF_H + +#include +#include +#include +#include "list.h" + +#ifdef LIBELF_USE_DEPRECATED +# define elf_getshdrnum elf_getshnum +# define elf_getshdrstrndx elf_getshstrndx +#endif + +struct section { + struct list_head list; + GElf_Shdr sh; + struct section *base, *rela; + struct list_head relas; + struct symbol *sym; + Elf_Data *elf_data; + char *name; + int idx; + void *data; + unsigned int size; +}; + +struct symbol { + struct list_head list; + GElf_Sym sym; + struct section *sec; + char *name; + unsigned int idx; + unsigned char bind, type; + unsigned long offset; + unsigned int size; +}; + +struct rela { + struct list_head list; + GElf_Rela rela; + struct symbol *sym; + unsigned int type; + unsigned long offset; + int addend; +}; + +struct elf { + Elf *elf; + GElf_Ehdr ehdr; + int fd; + char *name; + struct list_head sections; + struct list_head symbols; +}; + + +struct elf *elf_open(const char *name); +bool is_rela_section(struct section *sec); +struct section *find_section_by_name(struct elf *elf, const char *name); +struct section *create_rela_section(struct elf *elf, const char *name, + struct section *base); + +void elf_close(struct elf *elf); +int elf_write_file(struct elf *elf, const char *file); + + +#endif /* _KLP_POST_ELF_H */ diff --git a/scripts/livepatch/klp-convert.c b/scripts/livepatch/klp-convert.c new file mode 100644 index 000000000000..62bd26941081 --- /dev/null +++ b/scripts/livepatch/klp-convert.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Josh Poimboeuf + * Copyright (C) 2017 Joao Moreira + */ + +#include +#include +#include +#include "elf.h" +#include "list.h" +#include "klp-convert.h" + +/* + * Symbols parsed from Symbols.list are kept in two lists: + * - symbols: keeps non-exported symbols + * - exp_symbols: keeps exported symbols (__ksymtab_prefixed) + */ +static LIST_HEAD(symbols); +static LIST_HEAD(exp_symbols); + +/* In-livepatch user-provided symbol positions are kept in list usr_symbols */ +static LIST_HEAD(usr_symbols); + +static void free_syms_lists(void) +{ + struct symbol_entry *entry, *aux; + struct sympos *sp, *sp_aux; + + list_for_each_entry_safe(entry, aux, &symbols, list) { + free(entry->object_name); + free(entry->symbol_name); + list_del(&entry->list); + free(entry); + } + + list_for_each_entry_safe(entry, aux, &exp_symbols, list) { + free(entry->object_name); + free(entry->symbol_name); + list_del(&entry->list); + free(entry); + } + + list_for_each_entry_safe(sp, sp_aux, &usr_symbols, list) { + free(sp->object_name); + free(sp->symbol_name); + list_del(&sp->list); + free(sp); + } +} + +/* Parses file and fill symbols and exp_symbols list */ +static bool load_syms_lists(const char *symbols_list) +{ + FILE *fsyms; + struct symbol_entry *entry; + size_t len = 0; + ssize_t n; + char *obj = NULL, *sym = NULL; + + fsyms = fopen(symbols_list, "r"); + if (!fsyms) { + WARN("Unable to open Symbol list: %s", symbols_list); + return false; + } + + /* read file format version */ + n = getline(&sym, &len, fsyms); + if (n <= 0) { + WARN("Unable to read Symbol list: %s", symbols_list); + return false; + } + + if (strncmp(sym, "klp-convert-symbol-data.0.1", 27) != 0) { + WARN("Symbol list is in unknown format."); + return false; + } + + len = 0; + free(sym); + sym = NULL; + + /* read file */ + n = getline(&sym, &len, fsyms); + while (n > 0) { + if (sym[n-1] == '\n') + sym[n-1] = '\0'; + + /* Objects in Symbols.list are flagged with '*' */ + if (sym[0] == '*') { + if (obj) + free(obj); + obj = strdup(sym+1); + if (!obj) { + WARN("Unable to allocate object name\n"); + return false; + } + free(sym); + } else { + entry = calloc(1, sizeof(struct symbol_entry)); + if (!entry) { + WARN("Unable to allocate Symbol entry\n"); + return false; + } + + entry->object_name = strdup(obj); + if (!entry->object_name) { + WARN("Unable to allocate entry object name\n"); + return false; + } + + entry->symbol_name = sym; + if (strncmp(entry->symbol_name, "__ksymtab_", 10) == 0) + list_add(&entry->list, &exp_symbols); + else + list_add(&entry->list, &symbols); + } + len = 0; + sym = NULL; + n = getline(&sym, &len, fsyms); + } + free(sym); + free(obj); + fclose(fsyms); + return true; +} + +/* Searches for sympos of specific symbol in usr_symbols list */ +static bool get_usr_sympos(struct symbol *s, struct sympos *sp) +{ + struct sympos *aux; + + list_for_each_entry(aux, &usr_symbols, list) { + if (strcmp(aux->symbol_name, s->name) == 0) { + sp->symbol_name = aux->symbol_name; + sp->object_name = aux->object_name; + sp->pos = aux->pos; + return true; + } + } + return false; +} + +/* Removes symbols used for sympos annotation from livepatch elf object */ +static void clear_sympos_symbols(struct section *sec, struct elf *klp_elf) +{ + struct symbol *sym, *aux; + + list_for_each_entry_safe(sym, aux, &klp_elf->symbols, list) { + if (sym->sec == sec) { + + struct section *sec; + struct rela *rela, *tmprela; + + list_for_each_entry(sec, &klp_elf->sections, list) { + list_for_each_entry_safe(rela, tmprela, &sec->relas, list) { + if (rela->sym == sym) { + list_del(&rela->list); + free(rela); + } + } + } + + list_del(&sym->list); + free(sym); + } + } +} + +/* Removes annotation from livepatch elf object */ +static void clear_sympos_annontations(struct elf *klp_elf) +{ + struct section *sec, *aux; + + list_for_each_entry_safe(sec, aux, &klp_elf->sections, list) { + if (strncmp(sec->name, ".klp.module_relocs.", 19) == 0) { + clear_sympos_symbols(sec, klp_elf); + list_del(&sec->list); + free(sec); + continue; + } + if (strncmp(sec->name, ".rela.klp.module_relocs.", 24) == 0) { + + struct rela *rela, *tmprela; + + list_for_each_entry_safe(rela, tmprela, &sec->relas, list) { + list_del(&rela->list); + free(rela); + } + list_del(&sec->list); + free(sec); + continue; + } + } +} + +/* Checks if two or more elements in usr_symbols have the same name */ +static bool sympos_sanity_check(void) +{ + bool sane = true; + struct sympos *sp, *aux; + + list_for_each_entry(sp, &usr_symbols, list) { + aux = list_next_entry(sp, list); + list_for_each_entry_from(aux, &usr_symbols, list) { + if (strcmp(sp->symbol_name, aux->symbol_name) == 0) { + WARN("Conflicting KLP_SYMPOS definition: %s.%s,%d vs. %s.%s,%d.", + sp->object_name, sp->symbol_name, sp->pos, + aux->object_name, aux->symbol_name, aux->pos); + sane = false; + } + } + } + return sane; +} + +/* Parses the livepatch elf object and fills usr_symbols */ +static bool load_usr_symbols(struct elf *klp_elf) +{ + char objname[MODULE_NAME_LEN]; + struct sympos *sp; + struct section *sec, *aux, *relasec; + struct rela *rela; + struct klp_module_reloc *reloc; + int i, nr_entries; + + list_for_each_entry_safe(sec, aux, &klp_elf->sections, list) { + if (sscanf(sec->name, ".klp.module_relocs.%55s", objname) != 1) + continue; + + relasec = sec->rela; + reloc = sec->data; + i = 0; + nr_entries = sec->size / sizeof(*reloc); + list_for_each_entry(rela, &relasec->relas, list) { + if (i >= nr_entries) { + WARN("section %s length beyond nr_entries\n", + relasec->name); + return false; + } + sp = calloc(1, sizeof(struct sympos)); + if (!sp) { + WARN("Unable to allocate sympos memory\n"); + return false; + } + sp->object_name = strdup(objname); + if (!sp->object_name) { + WARN("Unable to allocate object name\n"); + return false; + } + sp->symbol_name = strdup(rela->sym->name); + if (!sp->symbol_name) { + WARN("Unable to allocate symbol name\n"); + return false; + } + sp->pos = reloc[i].sympos; + list_add(&sp->list, &usr_symbols); + i++; + } + if (i != nr_entries) { + WARN("nr_entries mismatch (%d != %d) for %s\n", + i, nr_entries, relasec->name); + return false; + } + } + clear_sympos_annontations(klp_elf); + return sympos_sanity_check(); +} + +/* prints list of valid sympos for symbol with provided name */ +static void print_valid_module_relocs(char *name) +{ + struct symbol_entry *e; + char *cur_obj = ""; + int counter = 0; + bool first = true; + + /* Symbols from the same object are locally gathered in the list */ + fprintf(stderr, "Valid KLP_SYMPOS for symbol %s:\n", name); + fprintf(stderr, "-------------------------------------------------\n"); + list_for_each_entry(e, &symbols, list) { + if (strcmp(e->object_name, cur_obj) != 0) { + cur_obj = e->object_name; + counter = 0; + } + if (strcmp(e->symbol_name, name) == 0) { + if (counter == 0) { + if (!first) + fprintf(stderr, "}\n"); + + fprintf(stderr, "KLP_MODULE_RELOC(%s){\n", + cur_obj); + first = false; + } + fprintf(stderr, "\tKLP_SYMPOS(%s,%d)\n", name, counter); + counter++; + } + } + fprintf(stderr, "-------------------------------------------------\n"); +} + +/* + * Searches for symbol in symbols list and returns its sympos if it is unique, + * otherwise prints a list with all considered valid sympos + */ +static struct symbol_entry *find_sym_entry_by_name(char *name) +{ + struct symbol_entry *found = NULL; + struct symbol_entry *e; + + list_for_each_entry(e, &symbols, list) { + if (strcmp(e->symbol_name, name) == 0) { + + /* + * If there exist multiple symbols with the same + * name then user-provided sympos is required + */ + if (found) { + WARN("Define KLP_SYMPOS for the symbol: %s", + e->symbol_name); + + print_valid_module_relocs(name); + return NULL; + } + found = e; + } + } + if (found) + return found; + + return NULL; +} + +/* Checks if sympos is valid, otherwise prints valid sympos list */ +static bool valid_sympos(struct sympos *sp) +{ + struct symbol_entry *e; + + if (sp->pos == 0) { + + /* + * sympos of 0 is reserved for uniquely named obj:sym, + * verify that this is the case + */ + int counter = 0; + + list_for_each_entry(e, &symbols, list) { + if ((strcmp(e->symbol_name, sp->symbol_name) == 0) && + (strcmp(e->object_name, sp->object_name) == 0)) { + counter++; + } + } + if (counter == 1) + return true; + + WARN("Provided KLP_SYMPOS of 0, but found %d symbols matching: %s.%s,%d", + counter, sp->object_name, sp->symbol_name, + sp->pos); + + } else { + + /* + * sympos > 0 indicates a specific commonly-named obj:sym, + * indexing starts with 1 + */ + int index = 1; + + list_for_each_entry(e, &symbols, list) { + if ((strcmp(e->symbol_name, sp->symbol_name) == 0) && + (strcmp(e->object_name, sp->object_name) == 0)) { + if (index == sp->pos) + return true; + index++; + } + } + + WARN("Provided KLP_SYMPOS does not match a symbol: %s.%s,%d", + sp->object_name, sp->symbol_name, sp->pos); + } + + print_valid_module_relocs(sp->symbol_name); + + return false; +} + +/* Returns the right sympos respective to a symbol to be relocated */ +static bool find_missing_position(struct symbol *s, struct sympos *sp) +{ + struct symbol_entry *entry; + + if (get_usr_sympos(s, sp)) { + if (valid_sympos(sp)) + return true; + return false; + } + + /* if no user-provided sympos, search symbol in symbols list */ + entry = find_sym_entry_by_name(s->name); + if (entry) { + sp->symbol_name = entry->symbol_name; + sp->object_name = entry->object_name; + sp->pos = 0; + return true; + } + return false; +} + +/* + * Finds or creates a klp rela section based on another given section (@oldsec) + * and sympos (@*sp), then returns it + */ +static struct section *get_or_create_klp_rela_section(struct section *oldsec, + struct sympos *sp, struct elf *klp_elf) +{ + char *name; + struct section *sec; + unsigned int length; + + length = strlen(KLP_RELA_PREFIX) + strlen(sp->object_name) + + strlen(oldsec->base->name) + 2; + + name = calloc(1, length); + if (!name) { + WARN("Memory allocation failed (%s%s.%s)\n", KLP_RELA_PREFIX, + sp->object_name, oldsec->base->name); + return NULL; + } + + if (snprintf(name, length, KLP_RELA_PREFIX "%s.%s", sp->object_name, + oldsec->base->name) >= length) { + WARN("Length error (%s)", name); + free(name); + return NULL; + } + + sec = find_section_by_name(klp_elf, name); + if (!sec) + sec = create_rela_section(klp_elf, name, oldsec->base); + + if (sec) + sec->sh.sh_flags |= SHF_RELA_LIVEPATCH; + + free(name); + return sec; +} + +/* Converts rela symbol names */ +static bool convert_klp_symbol(struct symbol *s, struct sympos *sp) +{ + char *name; + char pos[4]; /* assume that pos will never be > 999 */ + unsigned int length; + + if (snprintf(pos, sizeof(pos), "%d", sp->pos) > sizeof(pos)) { + WARN("Insuficient buffer for expanding sympos (%s.%s,%d)\n", + sp->object_name, sp->symbol_name, sp->pos); + return false; + } + + length = strlen(KLP_SYM_PREFIX) + strlen(sp->object_name) + + strlen(sp->symbol_name) + sizeof(pos) + 3; + + name = calloc(1, length); + if (!name) { + WARN("Memory allocation failed (%s%s.%s,%s)\n", KLP_SYM_PREFIX, + sp->object_name, sp->symbol_name, pos); + return false; + } + + if (snprintf(name, length, KLP_SYM_PREFIX "%s.%s,%s", sp->object_name, + sp->symbol_name, pos) >= length) { + + WARN("Length error (%s%s.%s,%s)", KLP_SYM_PREFIX, + sp->object_name, sp->symbol_name, pos); + + return false; + } + + /* + * Despite the memory waste, we don't mind freeing the original symbol + * name memory chunk. Keeping it there is harmless and, since removing + * bytes from the string section is non-trivial, it is unworthy. + */ + s->name = name; + s->sec = NULL; + s->sym.st_name = -1; + s->sym.st_shndx = SHN_LIVEPATCH; + + return true; +} + +/* + * Convert rela that cannot be resolved by the clasic module loader + * to the special klp rela one. + */ +static bool convert_rela(struct section *oldsec, struct rela *r, + struct sympos *sp, struct elf *klp_elf) +{ + struct section *sec; + struct rela *r1, *r2; + + sec = get_or_create_klp_rela_section(oldsec, sp, klp_elf); + if (!sec) { + WARN("Can't create or access klp.rela section (%s.%s)\n", + sp->object_name, sp->symbol_name); + return false; + } + + if (!convert_klp_symbol(r->sym, sp)) { + WARN("Unable to convert symbol name (%s.%s)\n", sec->name, + r->sym->name); + return false; + } + + /* Move the converted rela to klp rela section */ + list_for_each_entry_safe(r1, r2, &oldsec->relas, list) { + if (r1->sym->name == r->sym->name) { + list_del(&r1->list); + list_add(&r1->list, &sec->relas); + } + } + return true; +} + +/* Checks if given symbol name matches a symbol in exp_symbols */ +static bool is_exported(char *sname) +{ + struct symbol_entry *e; + + /* + * exp_symbols itens are prefixed with __ksymtab_ - comparisons must + * skip prefix and check if both are properly null-terminated + */ + list_for_each_entry(e, &exp_symbols, list) { + if (strcmp(e->symbol_name + 10, sname) == 0) + return true; + } + return false; +} + +/* Checks if a symbol was previously klp-converted based on its name */ +static bool is_converted(char *sname) +{ + int len = strlen(KLP_SYM_PREFIX); + + if (strncmp(sname, KLP_SYM_PREFIX, len) == 0) + return true; + return false; +} + +/* + * Checks if symbol must be converted (conditions): + * not resolved, not already converted or isn't an exported symbol + */ +static bool must_convert(struct symbol *sym) +{ + /* already resolved? */ + if (sym->sec) + return false; + + /* skip symbol with index 0 */ + if (!sym->idx) + return false; + + /* we should not touch .TOC. on ppc64le */ + if (strcmp(sym->name, ".TOC.") == 0) + return false; + + return (!(is_converted(sym->name) || is_exported(sym->name))); +} + +/* Checks if a section is a klp rela section */ +static bool is_klp_rela_section(char *sname) +{ + int len = strlen(KLP_RELA_PREFIX); + + if (strncmp(sname, KLP_RELA_PREFIX, len) == 0) + return true; + return false; +} + +/* + * Frees the new names and rela sections as created by convert_rela() + */ +static void free_converted_resources(struct elf *klp_elf) +{ + struct symbol *sym; + struct section *sec; + + list_for_each_entry(sym, &klp_elf->symbols, list) { + if (sym->name && is_converted(sym->name)) { + free(sym->name); + } + } + + list_for_each_entry(sec, &klp_elf->sections, list) { + if (is_klp_rela_section(sec->name)) { + free(sec->elf_data); + free(sec->name); + } + } +} + +int main(int argc, const char **argv) +{ + const char *klp_in_module, *klp_out_module, *symbols_list; + struct rela *rela, *tmprela; + struct section *sec, *aux; + struct sympos sp; + struct elf *klp_elf; + + if (argc != 4) { + WARN("Usage: %s ", argv[0]); + return -1; + } + + symbols_list = argv[1]; + klp_in_module = argv[2]; + klp_out_module = argv[3]; + + klp_elf = elf_open(klp_in_module); + if (!klp_elf) { + WARN("Unable to read elf file %s\n", klp_in_module); + return -1; + } + + if (!load_syms_lists(symbols_list)) + return -1; + + if (!load_usr_symbols(klp_elf)) { + WARN("Unable to load user-provided sympos"); + return -1; + } + + list_for_each_entry_safe(sec, aux, &klp_elf->sections, list) { + if (!is_rela_section(sec)) + continue; + + list_for_each_entry_safe(rela, tmprela, &sec->relas, list) { + if (!must_convert(rela->sym)) + continue; + + if (!find_missing_position(rela->sym, &sp)) { + WARN("Unable to find missing symbol: %s", + rela->sym->name); + return -1; + } + if (!convert_rela(sec, rela, &sp, klp_elf)) { + WARN("Unable to convert relocation: %s", + rela->sym->name); + return -1; + } + } + } + + free_syms_lists(); + if (elf_write_file(klp_elf, klp_out_module)) + return -1; + + free_converted_resources(klp_elf); + elf_close(klp_elf); + + return 0; +} + +/* Functions kept commented since they might be useful for future debugging */ + +/* Dumps sympos list (useful for debugging purposes) + * static void dump_sympos(void) + * { + * struct sympos *sp; + * + * fprintf(stderr, "BEGIN OF SYMPOS DUMP\n"); + * list_for_each_entry(sp, &usr_symbols, list) { + * fprintf(stderr, "%s %s %d\n", sp->symbol_name, sp->object_name, + * sp->pos); + * } + * fprintf(stderr, "END OF SYMPOS DUMP\n"); + * } + * + * + * / Dump symbols list for debugging purposes / + * static void dump_symbols(void) + * { + * struct symbol_entry *entry; + * + * fprintf(stderr, "BEGIN OF SYMBOLS DUMP\n"); + * list_for_each_entry(entry, &symbols, list) + * printf("%s %s\n", entry->object_name, entry->symbol_name); + * fprintf(stderr, "END OF SYMBOLS DUMP\n"); + * } + */ diff --git a/scripts/livepatch/klp-convert.h b/scripts/livepatch/klp-convert.h new file mode 100644 index 000000000000..fb92b1ea4b52 --- /dev/null +++ b/scripts/livepatch/klp-convert.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2016 Josh Poimboeuf + * Copyright (C) 2017 Joao Moreira + * + */ + +#define SHN_LIVEPATCH 0xff20 +#define SHF_RELA_LIVEPATCH 0x00100000 +#define MODULE_NAME_LEN (64 - sizeof(GElf_Addr)) +#define WARN(format, ...) \ + fprintf(stderr, "klp-convert: " format "\n", ##__VA_ARGS__) + +/* + * klp-convert uses macros defined in the linux sources package. To prevent the + * dependency when building locally, they are defined below. Also notice that + * these should match the definitions from the targeted kernel. + */ + +#define KLP_RELA_PREFIX ".klp.rela." +#define KLP_SYM_PREFIX ".klp.sym." + +struct symbol_entry { + struct list_head list; + char *symbol_name; + char *object_name; +}; + +struct sympos { + struct list_head list; + char *symbol_name; + char *object_name; + int pos; +}; + +struct klp_module_reloc { + void *sym; + unsigned int sympos; +} __attribute__((packed)); diff --git a/scripts/livepatch/list.h b/scripts/livepatch/list.h new file mode 100644 index 000000000000..4d429120fabf --- /dev/null +++ b/scripts/livepatch/list.h @@ -0,0 +1,391 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +#define WRITE_ONCE(a, b) (a = b) +#define READ_ONCE(a) a + +#undef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) * __mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); }) + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + WRITE_ONCE(list->next, list); + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + WRITE_ONCE(prev->next, new); +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + WRITE_ONCE(prev->next, next); +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return READ_ONCE(head->next) == head; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +/** + * list_first_entry_or_null - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note that if the list is empty, it returns NULL. + */ +#define list_first_entry_or_null(ptr, type, member) \ + (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_prev_entry - get the prev element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal + of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +/** + * list_prepare_entry - prepare a pos entry for use in + list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_head within the struct. + * + * Prepares a pos entry for use as a start point in + list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_prev_entry(pos, member); \ + &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current + point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against + removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_continue - continue list iteration safe against + * removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_next_entry(pos, member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_from - iterate over list from current point safe + * against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list safe against + * removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member), \ + n = list_prev_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_prev_entry(n, member)) + +/** + * list_safe_reset_next - reset a stale list_for_each_entry_safe loop + * @pos: the loop cursor used in the list_for_each_entry_safe loop + * @n: temporary storage used in list_for_each_entry_safe + * @member: the name of the list_head within the struct. + * + * list_safe_reset_next is not safe to use in general if the list may be + * modified concurrently (eg. the lock is dropped in the loop body). An + * exception to this is if the cursor element (pos) is pinned in the list, + * and list_safe_reset_next is called after re-taking the lock and before + * completing the current iteration of the loop body. + */ +#define list_safe_reset_next(pos, n, member) \ + (n = list_next_entry(pos, member)) + +#endif From patchwork Wed Apr 10 15:50:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894299 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 49CA714DB for ; Wed, 10 Apr 2019 15:51:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3097D28A7D for ; Wed, 10 Apr 2019 15:51:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2E96E28B06; Wed, 10 Apr 2019 15:51:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AB33628A7D for ; Wed, 10 Apr 2019 15:51:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387494AbfDJPvL (ORCPT ); Wed, 10 Apr 2019 11:51:11 -0400 Received: from mx1.redhat.com ([209.132.183.28]:37556 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387491AbfDJPvK (ORCPT ); Wed, 10 Apr 2019 11:51:10 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 74EC430B32F0; Wed, 10 Apr 2019 15:51:10 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1F33B110A180; Wed, 10 Apr 2019 15:51:09 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 4/9] livepatch: Add klp-convert annotation helpers Date: Wed, 10 Apr 2019 11:50:53 -0400 Message-Id: <20190410155058.9437-5-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.43]); Wed, 10 Apr 2019 15:51:10 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Josh Poimboeuf Define macros KLP_MODULE_RELOC and KLP_SYMPOS in include/linux/livepatch.h to improve user-friendliness of the livepatch annotation process. Signed-off-by: Josh Poimboeuf Signed-off-by: Joao Moreira Signed-off-by: Joe Lawrence --- include/linux/livepatch.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 16b48e8b29a2..947cfc2d1980 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -236,6 +236,18 @@ void *klp_shadow_get_or_alloc(void *obj, unsigned long id, void klp_shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor); void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor); +/* Used to annotate symbol relocations in livepatches */ +#define KLP_MODULE_RELOC(obj) \ + struct klp_module_reloc \ + __attribute__((__section__(".klp.module_relocs." #obj))) \ + __attribute__((aligned (4))) + +#define KLP_SYMPOS(symbol, pos) \ + { \ + .sym = &symbol, \ + .sympos = pos, \ + }, + #else /* !CONFIG_LIVEPATCH */ static inline int klp_module_coming(struct module *mod) { return 0; } From patchwork Wed Apr 10 15:50:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894297 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 51FC11805 for ; Wed, 10 Apr 2019 15:51:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3928A28AFF for ; Wed, 10 Apr 2019 15:51:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2A23F28AD3; Wed, 10 Apr 2019 15:51:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id ABEA228B90 for ; Wed, 10 Apr 2019 15:51:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387521AbfDJPvN (ORCPT ); Wed, 10 Apr 2019 11:51:13 -0400 Received: from mx1.redhat.com ([209.132.183.28]:60662 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387507AbfDJPvN (ORCPT ); Wed, 10 Apr 2019 11:51:13 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EE533A5E1B; Wed, 10 Apr 2019 15:51:11 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 96BF81062225; Wed, 10 Apr 2019 15:51:10 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 5/9] modpost: Integrate klp-convert Date: Wed, 10 Apr 2019 11:50:54 -0400 Message-Id: <20190410155058.9437-6-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Wed, 10 Apr 2019 15:51:12 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Josh Poimboeuf Create cmd_klp_convert and hook it into scripts/Makefile.modpost. cmd_klp_convert invokes klp-convert with the right arguments for the conversion of unresolved symbols inside a livepatch. Signed-off-by: Josh Poimboeuf Signed-off-by: Konstantin Khlebnikov Signed-off-by: Miroslav Benes Signed-off-by: Joao Moreira Signed-off-by: Joe Lawrence Signed-off-by: Joe Lawrence --- scripts/Kbuild.include | 4 +++- scripts/Makefile.modpost | 16 +++++++++++++++- scripts/mod/modpost.c | 6 +++++- scripts/mod/modpost.h | 1 + 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index 7484b9d8272f..f8b7d12e44c9 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -236,6 +236,8 @@ endif # (needed for the shell) make-cmd = $(call escsq,$(subst $(pound),$$(pound),$(subst $$,$$$$,$(cmd_$(1))))) +save-cmd = printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd + # Find any prerequisites that is newer than target or that does not exist. # PHONY targets skipped in both cases. any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^) @@ -243,7 +245,7 @@ any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^) # Execute command if command has changed or prerequisite(s) are updated. if_changed = $(if $(strip $(any-prereq) $(arg-check)), \ $(cmd); \ - printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:) + $(save-cmd), @:) # Execute the command and also postprocess generated .d dependencies file. if_changed_dep = $(if $(strip $(any-prereq) $(arg-check)),$(cmd_and_fixdep),@:) diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 6b7f354f189a..1e8bb7442689 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -124,8 +124,22 @@ quiet_cmd_ld_ko_o = LD [M] $@ -o $@ $(real-prereqs) ; \ $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) +SLIST = $(objtree)/Symbols.list +KLP_CONVERT = scripts/livepatch/klp-convert +quiet_cmd_klp_convert = KLP $@ + cmd_klp_convert = mv $@ $(@:.ko=.klp.o); \ + $(KLP_CONVERT) $(SLIST) $(@:.ko=.klp.o) $@ + +define rule_ld_ko_o + $(call echo-cmd,ld_ko_o) $(cmd_ld_ko_o) ; \ + $(call save-cmd,ld_ko_o) ; \ + $(if $(CONFIG_LIVEPATCH), \ + $(if $(wildcard $(MODVERDIR)/$(basetarget).livepatch), \ + $(call echo-cmd,klp_convert) $(cmd_klp_convert) )) +endef + $(modules): %.ko :%.o %.mod.o FORCE - +$(call if_changed,ld_ko_o) + +$(call if_changed_rule,ld_ko_o) targets += $(modules) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index f277e116e0eb..374b22c76ec5 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1974,6 +1974,10 @@ static void read_symbols(const char *modname) license = get_next_modinfo(&info, "license", license); } + /* Livepatch modules have unresolved symbols resolved by klp-convert */ + if (get_modinfo(&info, "livepatch")) + mod->livepatch = 1; + for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { symname = remove_dot(info.strtab + sym->st_name); @@ -2101,7 +2105,7 @@ static int check_exports(struct module *mod) const char *basename; exp = find_symbol(s->name); if (!exp || exp->module == mod) { - if (have_vmlinux && !s->weak) { + if (have_vmlinux && !s->weak && !mod->livepatch) { if (warn_unresolved) { warn("\"%s\" [%s.ko] undefined!\n", s->name, mod->name); diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index 8453d6ac2f77..2acfaae064ec 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -118,6 +118,7 @@ struct module { int skip; int has_init; int has_cleanup; + int livepatch; struct buffer dev_table_buf; char srcversion[25]; int is_dot_o; From patchwork Wed Apr 10 15:50:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894295 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E2C2213B5 for ; Wed, 10 Apr 2019 15:51:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C80BD28B8A for ; Wed, 10 Apr 2019 15:51:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B9C3228B38; Wed, 10 Apr 2019 15:51:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A4FD228AAF for ; Wed, 10 Apr 2019 15:51:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387544AbfDJPvh (ORCPT ); Wed, 10 Apr 2019 11:51:37 -0400 Received: from mx1.redhat.com ([209.132.183.28]:33167 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387523AbfDJPvP (ORCPT ); Wed, 10 Apr 2019 11:51:15 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7C9928765E; Wed, 10 Apr 2019 15:51:13 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1C6DF1077D2D; Wed, 10 Apr 2019 15:51:12 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 6/9] modpost: Add modinfo flag to livepatch modules Date: Wed, 10 Apr 2019 11:50:55 -0400 Message-Id: <20190410155058.9437-7-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Wed, 10 Apr 2019 15:51:13 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Miroslav Benes Currently, livepatch infrastructure in the kernel relies on MODULE_INFO(livepatch, "Y") statement in a livepatch module. Then the kernel module loader knows a module is indeed livepatch module and can behave accordingly. klp-convert, on the other hand relies on LIVEPATCH_* statement in the module's Makefile for exactly the same reason. Remove dependency on modinfo and generate MODULE_INFO flag automatically in modpost when LIVEPATCH_* is defined in the module's Makefile. Generate a list of all built livepatch modules based on the .livepatch file and store it in (MODVERDIR)/livepatchmods. Give this list as an argument for modpost which will use it to identify livepatch modules. As MODULE_INFO is no longer needed, remove it. Signed-off-by: Miroslav Benes Signed-off-by: Joao Moreira Signed-off-by: Joe Lawrence --- lib/livepatch/Makefile | 5 ++ lib/livepatch/test_klp_atomic_replace.c | 1 - lib/livepatch/test_klp_callbacks_demo.c | 1 - lib/livepatch/test_klp_callbacks_demo2.c | 1 - lib/livepatch/test_klp_livepatch.c | 1 - samples/livepatch/Makefile | 4 ++ samples/livepatch/livepatch-sample.c | 1 - scripts/Makefile.modpost | 8 ++- scripts/mod/modpost.c | 84 ++++++++++++++++++++++-- 9 files changed, 94 insertions(+), 12 deletions(-) diff --git a/lib/livepatch/Makefile b/lib/livepatch/Makefile index 26900ddaef82..513d200b7942 100644 --- a/lib/livepatch/Makefile +++ b/lib/livepatch/Makefile @@ -2,6 +2,11 @@ # # Makefile for livepatch test code. +LIVEPATCH_test_klp_atomic_replace := y +LIVEPATCH_test_klp_callbacks_demo := y +LIVEPATCH_test_klp_callbacks_demo2 := y +LIVEPATCH_test_klp_livepatch := y + obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \ test_klp_callbacks_demo.o \ test_klp_callbacks_demo2.o \ diff --git a/lib/livepatch/test_klp_atomic_replace.c b/lib/livepatch/test_klp_atomic_replace.c index 5af7093ca00c..3bf08a1b7b12 100644 --- a/lib/livepatch/test_klp_atomic_replace.c +++ b/lib/livepatch/test_klp_atomic_replace.c @@ -52,6 +52,5 @@ static void test_klp_atomic_replace_exit(void) module_init(test_klp_atomic_replace_init); module_exit(test_klp_atomic_replace_exit); MODULE_LICENSE("GPL"); -MODULE_INFO(livepatch, "Y"); MODULE_AUTHOR("Joe Lawrence "); MODULE_DESCRIPTION("Livepatch test: atomic replace"); diff --git a/lib/livepatch/test_klp_callbacks_demo.c b/lib/livepatch/test_klp_callbacks_demo.c index 3fd8fe1cd1cc..76e2f51a6771 100644 --- a/lib/livepatch/test_klp_callbacks_demo.c +++ b/lib/livepatch/test_klp_callbacks_demo.c @@ -116,6 +116,5 @@ static void test_klp_callbacks_demo_exit(void) module_init(test_klp_callbacks_demo_init); module_exit(test_klp_callbacks_demo_exit); MODULE_LICENSE("GPL"); -MODULE_INFO(livepatch, "Y"); MODULE_AUTHOR("Joe Lawrence "); MODULE_DESCRIPTION("Livepatch test: livepatch demo"); diff --git a/lib/livepatch/test_klp_callbacks_demo2.c b/lib/livepatch/test_klp_callbacks_demo2.c index 5417573e80af..76db98fc3071 100644 --- a/lib/livepatch/test_klp_callbacks_demo2.c +++ b/lib/livepatch/test_klp_callbacks_demo2.c @@ -88,6 +88,5 @@ static void test_klp_callbacks_demo2_exit(void) module_init(test_klp_callbacks_demo2_init); module_exit(test_klp_callbacks_demo2_exit); MODULE_LICENSE("GPL"); -MODULE_INFO(livepatch, "Y"); MODULE_AUTHOR("Joe Lawrence "); MODULE_DESCRIPTION("Livepatch test: livepatch demo2"); diff --git a/lib/livepatch/test_klp_livepatch.c b/lib/livepatch/test_klp_livepatch.c index aff08199de71..d94d0ae232db 100644 --- a/lib/livepatch/test_klp_livepatch.c +++ b/lib/livepatch/test_klp_livepatch.c @@ -46,6 +46,5 @@ static void test_klp_livepatch_exit(void) module_init(test_klp_livepatch_init); module_exit(test_klp_livepatch_exit); MODULE_LICENSE("GPL"); -MODULE_INFO(livepatch, "Y"); MODULE_AUTHOR("Seth Jennings "); MODULE_DESCRIPTION("Livepatch test: livepatch module"); diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile index 8b9b42a258ad..5fb3280bbdc4 100644 --- a/samples/livepatch/Makefile +++ b/samples/livepatch/Makefile @@ -1,4 +1,8 @@ LIVEPATCH_livepatch-sample := y +LIVEPATCH_livepatch-shadow-fix1 := y +LIVEPATCH_livepatch-shadow-fix2 := y +LIVEPATCH_livepatch-callbacks-demo := y + obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o diff --git a/samples/livepatch/livepatch-sample.c b/samples/livepatch/livepatch-sample.c index 01c9cf003ca2..8900a175046b 100644 --- a/samples/livepatch/livepatch-sample.c +++ b/samples/livepatch/livepatch-sample.c @@ -79,4 +79,3 @@ static void livepatch_exit(void) module_init(livepatch_init); module_exit(livepatch_exit); MODULE_LICENSE("GPL"); -MODULE_INFO(livepatch, "Y"); diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 1e8bb7442689..9fe4c5760aca 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -65,6 +65,11 @@ MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs -r grep -h '\.ko$$' | sort __modules := $(shell $(MODLISTCMD)) modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o))) +# find all .livepatch files listed in $(MODVERDIR)/ +ifdef CONFIG_LIVEPATCH +$(shell find $(MODVERDIR) -name '*.livepatch' | xargs -r -I{} basename {} .livepatch > $(MODVERDIR)/livepatchmods) +endif + # Stop after building .o files if NOFINAL is set. Makes compile tests quicker _modpost: $(if $(KBUILD_MODPOST_NOFINAL), $(modules:.ko:.o),$(modules)) @@ -78,7 +83,8 @@ modpost = scripts/mod/modpost \ $(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \ $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ $(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \ - $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) + $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ + $(if $(CONFIG_LIVEPATCH),-l $(MODVERDIR)/livepatchmods) MODPOST_OPT=$(subst -i,-n,$(filter -i,$(MAKEFLAGS))) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 374b22c76ec5..b3ab17d9d834 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1974,10 +1974,6 @@ static void read_symbols(const char *modname) license = get_next_modinfo(&info, "license", license); } - /* Livepatch modules have unresolved symbols resolved by klp-convert */ - if (get_modinfo(&info, "livepatch")) - mod->livepatch = 1; - for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { symname = remove_dot(info.strtab + sym->st_name); @@ -2416,6 +2412,76 @@ static void write_dump(const char *fname) free(buf.p); } +struct livepatch_mod_list { + struct livepatch_mod_list *next; + char *livepatch_mod; +}; + +static struct livepatch_mod_list *load_livepatch_mods(const char *fname) +{ + struct livepatch_mod_list *list_iter, *list_start = NULL; + unsigned long size, pos = 0; + void *file = grab_file(fname, &size); + char *line; + + if (!file) + return NULL; + + while ((line = get_next_line(&pos, file, size))) { + list_iter = NOFAIL(malloc(sizeof(*list_iter))); + list_iter->next = list_start; + list_iter->livepatch_mod = NOFAIL(strdup(line)); + list_start = list_iter; + } + release_file(file, size); + + return list_start; +} + +static void free_livepatch_mods(struct livepatch_mod_list *list_start) +{ + struct livepatch_mod_list *list_iter; + + while (list_start) { + list_iter = list_start->next; + free(list_start->livepatch_mod); + free(list_start); + list_start = list_iter; + } +} + +static int is_livepatch_mod(const char *modname, + struct livepatch_mod_list *list) +{ + const char *myname; + + if (!list) + return 0; + + myname = strrchr(modname, '/'); + if (myname) + myname++; + else + myname = modname; + + while (list) { + if (!strcmp(myname, list->livepatch_mod)) + return 1; + list = list->next; + } + return 0; +} + +static void add_livepatch_flag(struct buffer *b, struct module *mod, + struct livepatch_mod_list *list) +{ + if (is_livepatch_mod(mod->name, list)) { + buf_printf(b, "\nMODULE_INFO(livepatch, \"Y\");\n"); + mod->livepatch = 1; + } +} + + struct ext_sym_list { struct ext_sym_list *next; const char *file; @@ -2431,8 +2497,9 @@ int main(int argc, char **argv) int err; struct ext_sym_list *extsym_iter; struct ext_sym_list *extsym_start = NULL; + struct livepatch_mod_list *livepatch_mods = NULL; - while ((opt = getopt(argc, argv, "i:I:e:mnsT:o:awE")) != -1) { + while ((opt = getopt(argc, argv, "i:I:e:l:mnsT:o:awE")) != -1) { switch (opt) { case 'i': kernel_read = optarg; @@ -2449,6 +2516,9 @@ int main(int argc, char **argv) extsym_iter->file = optarg; extsym_start = extsym_iter; break; + case 'l': + livepatch_mods = load_livepatch_mods(optarg); + break; case 'm': modversions = 1; break; @@ -2506,15 +2576,16 @@ int main(int argc, char **argv) buf.pos = 0; err |= check_modname_len(mod); - err |= check_exports(mod); add_header(&buf, mod); add_intree_flag(&buf, !external_module); add_retpoline(&buf); add_staging_flag(&buf, mod->name); + add_livepatch_flag(&buf, mod, livepatch_mods); err |= add_versions(&buf, mod); add_depends(&buf, mod); add_moddevtable(&buf, mod); add_srcversion(&buf, mod); + err |= check_exports(mod); sprintf(fname, "%s.mod.c", mod->name); write_if_changed(&buf, fname); @@ -2524,6 +2595,7 @@ int main(int argc, char **argv) if (sec_mismatch_count && sec_mismatch_fatal) fatal("modpost: Section mismatches detected.\n" "Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n"); + free_livepatch_mods(livepatch_mods); free(buf.p); return err; From patchwork Wed Apr 10 15:50:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894289 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B20F21805 for ; Wed, 10 Apr 2019 15:51:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 97D5A28AFF for ; Wed, 10 Apr 2019 15:51:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9618028BD5; Wed, 10 Apr 2019 15:51:19 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E92AF28BC8 for ; Wed, 10 Apr 2019 15:51:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387541AbfDJPvR (ORCPT ); Wed, 10 Apr 2019 11:51:17 -0400 Received: from mx1.redhat.com ([209.132.183.28]:30576 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387528AbfDJPvP (ORCPT ); Wed, 10 Apr 2019 11:51:15 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EBFFD316891D; Wed, 10 Apr 2019 15:51:14 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 95D841062225; Wed, 10 Apr 2019 15:51:13 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 7/9] livepatch: Add sample livepatch module Date: Wed, 10 Apr 2019 11:50:56 -0400 Message-Id: <20190410155058.9437-8-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.41]); Wed, 10 Apr 2019 15:51:15 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Josh Poimboeuf Add a new livepatch sample in samples/livepatch/ to make use of symbols that must be post-processed to enable load-time relocation resolution. As the new sample is to be used as an example, it is annotated with KLP_MODULE_RELOC and with KLP_SYMPOS macros. The livepatch sample updates the function cmdline_proc_show to print the string referenced by the symbol saved_command_line appended by the string "livepatch=1". Update livepatch-sample.c to remove livepatch MODULE_INFO statement. Signed-off-by: Josh Poimboeuf Signed-off-by: Joao Moreira Signed-off-by: Joe Lawrence --- samples/livepatch/Makefile | 2 + .../livepatch/livepatch-annotated-sample.c | 102 ++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 samples/livepatch/livepatch-annotated-sample.c diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile index 5fb3280bbdc4..dea530840725 100644 --- a/samples/livepatch/Makefile +++ b/samples/livepatch/Makefile @@ -2,6 +2,7 @@ LIVEPATCH_livepatch-sample := y LIVEPATCH_livepatch-shadow-fix1 := y LIVEPATCH_livepatch-shadow-fix2 := y LIVEPATCH_livepatch-callbacks-demo := y +LIVEPATCH_livepatch-annotated-sample := y obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o @@ -10,3 +11,4 @@ obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-mod.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-busymod.o +obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-annotated-sample.o diff --git a/samples/livepatch/livepatch-annotated-sample.c b/samples/livepatch/livepatch-annotated-sample.c new file mode 100644 index 000000000000..556ce7e0bdab --- /dev/null +++ b/samples/livepatch/livepatch-annotated-sample.c @@ -0,0 +1,102 @@ +/* + * livepatch-annotated-sample.c - Kernel Live Patching Sample Module + * + * Copyright (C) 2014 Seth Jennings + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +/* + * This (dumb) live patch overrides the function that prints the + * kernel boot cmdline when /proc/cmdline is read. + * + * This livepatch uses the symbol saved_command_line whose relocation + * must be resolved during load time. To enable that, this module + * must be post-processed by a tool called klp-convert, which embeds + * information to be used by the loader to solve the relocation. + * + * The module is annotated with KLP_MODULE_RELOC/KLP_SYMPOS macros. + * These annotations are used by klp-convert to infer that the symbol + * saved_command_line is in the object vmlinux. + * + * As saved_command_line has no other homonimous symbol across + * kernel objects, this annotation is not a requirement, and can be + * suppressed with no harm to klp-convert. Yet, it is kept here as an + * example on how to annotate livepatch modules that contain symbols + * whose names are used in more than one kernel object. + * + * Example: + * + * $ cat /proc/cmdline + * + * + * $ insmod livepatch-sample.ko + * $ cat /proc/cmdline + * livepatch=1 + * + * $ echo 0 > /sys/kernel/livepatch/livepatch_sample/enabled + * $ cat /proc/cmdline + * + */ + +extern char *saved_command_line; + +#include +static int livepatch_cmdline_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s livepatch=1\n", saved_command_line); + return 0; +} + +KLP_MODULE_RELOC(vmlinux) vmlinux_relocs[] = { + KLP_SYMPOS(saved_command_line, 0) +}; + +static struct klp_func funcs[] = { + { + .old_name = "cmdline_proc_show", + .new_func = livepatch_cmdline_proc_show, + }, { } +}; + +static struct klp_object objs[] = { + { + /* name being NULL means vmlinux */ + .funcs = funcs, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int livepatch_init(void) +{ + return klp_enable_patch(&patch); +} + +static void livepatch_exit(void) +{ +} + +module_init(livepatch_init); +module_exit(livepatch_exit); +MODULE_LICENSE("GPL"); From patchwork Wed Apr 10 15:50:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894291 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E406314DB for ; Wed, 10 Apr 2019 15:51:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C3D5225223 for ; Wed, 10 Apr 2019 15:51:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C189328A74; Wed, 10 Apr 2019 15:51:19 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 27FCA28BBB for ; Wed, 10 Apr 2019 15:51:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387547AbfDJPvS (ORCPT ); Wed, 10 Apr 2019 11:51:18 -0400 Received: from mx1.redhat.com ([209.132.183.28]:8874 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387507AbfDJPvR (ORCPT ); Wed, 10 Apr 2019 11:51:17 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 715083091740; Wed, 10 Apr 2019 15:51:16 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 19A6A10694ED; Wed, 10 Apr 2019 15:51:15 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 8/9] documentation: Update on livepatch elf format Date: Wed, 10 Apr 2019 11:50:57 -0400 Message-Id: <20190410155058.9437-9-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.41]); Wed, 10 Apr 2019 15:51:16 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Joao Moreira Add a section to Documentation/livepatch/module-elf-format.txt describing how klp-convert works for fixing relocations. Signed-off-by: Joao Moreira Signed-off-by: Joe Lawrence --- Documentation/livepatch/livepatch.txt | 3 ++ Documentation/livepatch/module-elf-format.txt | 50 ++++++++++++++++--- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/Documentation/livepatch/livepatch.txt b/Documentation/livepatch/livepatch.txt index 4627b41ff02e..873c11aee038 100644 --- a/Documentation/livepatch/livepatch.txt +++ b/Documentation/livepatch/livepatch.txt @@ -274,6 +274,9 @@ into three levels: absolute position in the database, but rather the order it has been found only for a particular object ( vmlinux or a kernel module ). Note that kallsyms allows for searching symbols according to the object name. + Uniquely named symbols may use a symbol position of 0. Non-unique + symbols need to specify their object / kallsyms position, starting + at position 1. + struct klp_object defines an array of patched functions (struct klp_func) in the same object. Where the object is either vmlinux diff --git a/Documentation/livepatch/module-elf-format.txt b/Documentation/livepatch/module-elf-format.txt index f21a5289a09c..7bef8432352a 100644 --- a/Documentation/livepatch/module-elf-format.txt +++ b/Documentation/livepatch/module-elf-format.txt @@ -2,7 +2,8 @@ Livepatch module Elf format =========================== -This document outlines the Elf format requirements that livepatch modules must follow. +This document outlines the Elf format requirements that livepatch modules must +follow. ----------------- Table of Contents @@ -25,8 +26,9 @@ Table of Contents 3.3.2 Required name format 3.3.3 Example livepatch symbol names 3.3.4 Example `readelf --symbols` output -4. Architecture-specific sections -5. Symbol table and Elf section access +4. Automatic conversion of unresolved relocations +5. Architecture-specific sections +6. Symbol table and Elf section access ---------------------------- 0. Background and motivation @@ -270,7 +272,8 @@ Livepatch symbol names must conform to the following format: [D] The position of the symbol in the object (as according to kallsyms) This is used to differentiate duplicate symbols within the same object. The symbol position is expressed numerically (0, 1, 2...). - The symbol position of a unique symbol is 0. + The symbol position of a unique symbol is 0. The symbol position of + the first non-unique symbol is 1, the second is 2, etc. 3.3.3 Example livepatch symbol names: ------------------------------------- @@ -293,8 +296,43 @@ Symbol table '.symtab' contains 127 entries: [*] Note that the 'Ndx' (Section index) for these symbols is SHN_LIVEPATCH (0xff20). "OS" means OS-specific. +-------------------------------------------------- +4. Automatic conversion of unresolved relocations +-------------------------------------------------- +Sometimes livepatches may operate on symbols which are not self-contained nor +exported. When this happens, these symbols remain unresolved in the elf object +and will trigger an error during the livepatch instantiation. + +Whenever possible, the kernel building infrastructure solves this problem +automatically. First, a symbol database containing information on all compiled +objects is built. Second, this database - a file named Symbols.list, placed in +the kernel source root directory - is used to identify targets for unresolved +relocations, converting them in the livepatch elf accordingly to the +specifications above-described. While the first stage is fully handled by the +building system, the second is done by a tool called klp-convert, which can be +found in "scripts/livepatch". + +When an unresolved relocation has as target a symbol whose name is also used by +different symbols throughout the kernel, the relocation cannot be resolved +automatically. In these cases, the livepatch developer must add annotations to +the livepatch, making it possible for the system to identify which is the +correct target amongst multiple homonymous symbols. Such annotations must be +done through a data structure as follows: + +struct KLP_MODULE_RELOC(object) data_structure_name[] = { + KLP_SYMPOS(symbol, pos) +}; + +In the above example, object refers to the object file which contains the +symbol, being vmlinux or a module; symbol refers to the symbol name that will +be relocated and pos is its position in the object. + +When a data structure like this is added to the livepatch, the resulting elf +will hold symbols that will be identified by klp-convert and used to solve name +ambiguities. + --------------------------------- -4. Architecture-specific sections +5. Architecture-specific sections --------------------------------- Architectures may override arch_klp_init_object_loaded() to perform additional arch-specific tasks when a target module loads, such as applying @@ -305,7 +343,7 @@ be easily identified when iterating through a patch module's Elf sections (See arch/x86/kernel/livepatch.c for a complete example). -------------------------------------- -5. Symbol table and Elf section access +6. Symbol table and Elf section access -------------------------------------- A livepatch module's symbol table is accessible through module->symtab. From patchwork Wed Apr 10 15:50:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Lawrence X-Patchwork-Id: 10894293 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 533C313B5 for ; Wed, 10 Apr 2019 15:51:36 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 393C528B59 for ; Wed, 10 Apr 2019 15:51:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3777628B8B; Wed, 10 Apr 2019 15:51:36 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id ECBD028B1B for ; Wed, 10 Apr 2019 15:51:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729635AbfDJPv3 (ORCPT ); Wed, 10 Apr 2019 11:51:29 -0400 Received: from mx1.redhat.com ([209.132.183.28]:58176 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387544AbfDJPvS (ORCPT ); Wed, 10 Apr 2019 11:51:18 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EC8A430611EE; Wed, 10 Apr 2019 15:51:17 +0000 (UTC) Received: from jlaw-desktop.bos.redhat.com (dhcp-17-208.bos.redhat.com [10.18.17.208]) by smtp.corp.redhat.com (Postfix) with ESMTP id 918FA1077D2D; Wed, 10 Apr 2019 15:51:16 +0000 (UTC) From: Joe Lawrence To: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, linux-kbuild@vger.kernel.org Cc: Jessica Yu , Jiri Kosina , Joao Moreira , Joe Lawrence , Josh Poimboeuf , Konstantin Khlebnikov , Masahiro Yamada , Michael Matz , Miroslav Benes , Nicolai Stange , Petr Mladek Subject: [PATCH v3 9/9] livepatch/selftests: add klp-convert Date: Wed, 10 Apr 2019 11:50:58 -0400 Message-Id: <20190410155058.9437-10-joe.lawrence@redhat.com> In-Reply-To: <20190410155058.9437-1-joe.lawrence@redhat.com> References: <20190410155058.9437-1-joe.lawrence@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.48]); Wed, 10 Apr 2019 15:51:18 +0000 (UTC) Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add a simple klp-convert livepatch selftest that exercises various symbol homonym sympos scenarios. Signed-off-by: Joe Lawrence --- lib/livepatch/Makefile | 10 ++ lib/livepatch/test_klp_convert1.c | 106 ++++++++++++++++++ lib/livepatch/test_klp_convert2.c | 103 +++++++++++++++++ lib/livepatch/test_klp_convert_mod_a.c | 25 +++++ lib/livepatch/test_klp_convert_mod_b.c | 13 +++ .../selftests/livepatch/test-livepatch.sh | 64 +++++++++++ 6 files changed, 321 insertions(+) create mode 100644 lib/livepatch/test_klp_convert1.c create mode 100644 lib/livepatch/test_klp_convert2.c create mode 100644 lib/livepatch/test_klp_convert_mod_a.c create mode 100644 lib/livepatch/test_klp_convert_mod_b.c diff --git a/lib/livepatch/Makefile b/lib/livepatch/Makefile index 513d200b7942..b19253c72d72 100644 --- a/lib/livepatch/Makefile +++ b/lib/livepatch/Makefile @@ -5,6 +5,8 @@ LIVEPATCH_test_klp_atomic_replace := y LIVEPATCH_test_klp_callbacks_demo := y LIVEPATCH_test_klp_callbacks_demo2 := y +LIVEPATCH_test_klp_convert1 := y +LIVEPATCH_test_klp_convert2 := y LIVEPATCH_test_klp_livepatch := y obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \ @@ -12,9 +14,17 @@ obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \ test_klp_callbacks_demo2.o \ test_klp_callbacks_busy.o \ test_klp_callbacks_mod.o \ + test_klp_convert1.o \ + test_klp_convert2.o \ + test_klp_convert_mod.o \ test_klp_livepatch.o \ test_klp_shadow_vars.o +test_klp_convert_mod-y := \ + test_klp_convert_mod_a.o \ + test_klp_convert_mod_b.o + # Target modules to be livepatched require CC_FLAGS_FTRACE CFLAGS_test_klp_callbacks_busy.o += $(CC_FLAGS_FTRACE) CFLAGS_test_klp_callbacks_mod.o += $(CC_FLAGS_FTRACE) +CFLAGS_test_klp_convert_mod.o += $(CC_FLAGS_FTRACE) diff --git a/lib/livepatch/test_klp_convert1.c b/lib/livepatch/test_klp_convert1.c new file mode 100644 index 000000000000..594ec3a7cf70 --- /dev/null +++ b/lib/livepatch/test_klp_convert1.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2019 Joe Lawrence + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +/* klp-convert symbols - vmlinux */ +extern char *saved_command_line; +/* klp-convert symbols - test_klp_convert_mod.ko */ +extern char driver_name[]; +extern char homonym_string[]; +extern const char *get_homonym_string(void); +extern const char *get_driver_name(void); + +void print_saved_command_line(void) +{ + pr_info("saved_command_line, 0: %s\n", saved_command_line); +} + +void print_driver_name(void) +{ + pr_info("driver_name, 0: %s\n", driver_name); + pr_info("get_driver_name(), 0: %s\n", get_driver_name()); +} + +void print_homonym_string(void) +{ + pr_info("homonym_string, 1: %s\n", homonym_string); + pr_info("get_homonym_string(), 1: %s\n", get_homonym_string()); +} + +/* + * saved_command_line is a unique symbol, so the sympos annotation is + * optional. Provide to test that sympos=0 works correctly. + */ +KLP_MODULE_RELOC(vmlinux) vmlinux_relocs[] = { + KLP_SYMPOS(saved_command_line, 0) +}; + +/* + * driver_name symbols can be found in vmlinux (multiple) and also + * test_klp_convert_mod, therefore the annotation is required to + * clarify that we want the one from test_klp_convert_mod. + * + * test_klp_convert_mod contains multiple homonym_string and + * get_homonym_string symbols, test resolving the first set here and + * the others in test_klp_convert2.c + * + * get_driver_name is a uniquely named symbol, test that sympos=0 + * work correctly. + */ +KLP_MODULE_RELOC(test_klp_convert_mod) test_klp_convert_mod_relocs_a[] = { + KLP_SYMPOS(driver_name, 0) + KLP_SYMPOS(homonym_string, 1) + KLP_SYMPOS(get_homonym_string, 1) + KLP_SYMPOS(get_driver_name, 0) +}; + +static struct klp_func funcs[] = { + { + }, { } +}; + +static struct klp_object objs[] = { + { + /* name being NULL means vmlinux */ + .funcs = funcs, + }, + { + .name = "test_klp_convert_mod", + .funcs = funcs, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int test_klp_convert_init(void) +{ + int ret; + + ret = klp_enable_patch(&patch); + if (ret) + return ret; + + print_saved_command_line(); + print_driver_name(); + print_homonym_string(); + + return 0; +} + +static void test_klp_convert_exit(void) +{ +} + +module_init(test_klp_convert_init); +module_exit(test_klp_convert_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joe Lawrence "); +MODULE_DESCRIPTION("Livepatch test: klp-convert1"); diff --git a/lib/livepatch/test_klp_convert2.c b/lib/livepatch/test_klp_convert2.c new file mode 100644 index 000000000000..587dcecd546d --- /dev/null +++ b/lib/livepatch/test_klp_convert2.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2019 Joe Lawrence + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +/* klp-convert symbols - vmlinux */ +extern char *saved_command_line; +/* klp-convert symbols - test_klp_convert_mod.ko */ +extern char driver_name[]; +extern char homonym_string[]; +extern const char *get_homonym_string(void); +extern const char *get_driver_name(void); + +void print_saved_command_line(void) +{ + pr_info("saved_command_line (auto): %s\n", saved_command_line); +} + +void print_driver_name(void) +{ + pr_info("driver_name, 0: %s\n", driver_name); + pr_info("get_driver_name(), (auto): %s\n", get_driver_name()); +} + +void print_homonym_string(void) +{ + pr_info("homonym_string, 2: %s\n", homonym_string); + pr_info("get_homonym_string(), 2: %s\n", get_homonym_string()); +} + +/* + * saved_command_line is a uniquely named symbol, so the sympos + * annotation is optional. Skip it and test that klp-convert can + * resolve the symbol on its own. + */ + +/* + * driver_name symbols can be found in vmlinux (multiple) and also + * test_klp_convert_mod, therefore the annotation is required to + * clarify that we want the one from test_klp_convert_mod. + * + * test_klp_convert_mod contains multiple homonym_string symbols, + * test_klp_convert1.c resolved to the first one, resolve to the + * second one here. + * + * get_driver_name is a uniquely named symbol, test klp-convert can + * resolve it automatically. + */ +KLP_MODULE_RELOC(test_klp_convert_mod) test_klp_convert_mod_relocs_a[] = { + KLP_SYMPOS(driver_name, 0) + KLP_SYMPOS(homonym_string, 2) + KLP_SYMPOS(get_homonym_string, 2) +}; + +static struct klp_func funcs[] = { + { + }, { } +}; + +static struct klp_object objs[] = { + { + /* name being NULL means vmlinux */ + .funcs = funcs, + }, + { + .name = "test_klp_convert_mod", + .funcs = funcs, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int test_klp_convert_init(void) +{ + int ret; + + ret = klp_enable_patch(&patch); + if (ret) + return ret; + + print_saved_command_line(); + print_driver_name(); + print_homonym_string(); + + return 0; +} + +static void test_klp_convert_exit(void) +{ +} + +module_init(test_klp_convert_init); +module_exit(test_klp_convert_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joe Lawrence "); +MODULE_DESCRIPTION("Livepatch test: klp-convert2"); diff --git a/lib/livepatch/test_klp_convert_mod_a.c b/lib/livepatch/test_klp_convert_mod_a.c new file mode 100644 index 000000000000..b2db9942fbd2 --- /dev/null +++ b/lib/livepatch/test_klp_convert_mod_a.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2019 Joe Lawrence + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +/* Unique symbols that don't need sympos annotation */ +static const char driver_name[] = KBUILD_MODNAME; +__used static const char *get_driver_name(void) +{ + return driver_name; +} + +/* Common symbol names that need sympos */ +static const char homonym_string[] = "homonym string A"; +__used static const char *get_homonym_string(void) +{ + return homonym_string; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joe Lawrence "); +MODULE_DESCRIPTION("Livepatch test: klp-convert module"); diff --git a/lib/livepatch/test_klp_convert_mod_b.c b/lib/livepatch/test_klp_convert_mod_b.c new file mode 100644 index 000000000000..a3d529ff5f1f --- /dev/null +++ b/lib/livepatch/test_klp_convert_mod_b.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2019 Joe Lawrence + +/* + * A second compilation unit to provide another set of similarly named + * symbols, forcing a livepatch to use sympos annotations. + */ + +static char homonym_string[] = "homonym string B"; +__used static char *get_homonym_string(void) +{ + return homonym_string; +} diff --git a/tools/testing/selftests/livepatch/test-livepatch.sh b/tools/testing/selftests/livepatch/test-livepatch.sh index f05268aea859..9c3745e8ba1d 100755 --- a/tools/testing/selftests/livepatch/test-livepatch.sh +++ b/tools/testing/selftests/livepatch/test-livepatch.sh @@ -6,6 +6,9 @@ MOD_LIVEPATCH=test_klp_livepatch MOD_REPLACE=test_klp_atomic_replace +MOD_KLP_CONVERT_MOD=test_klp_convert_mod +MOD_KLP_CONVERT1=test_klp_convert1 +MOD_KLP_CONVERT2=test_klp_convert2 set_dynamic_debug @@ -165,4 +168,65 @@ livepatch: '$MOD_REPLACE': unpatching complete % rmmod $MOD_REPLACE" +# TEST: klp-convert symbols +# - load a livepatch that modifies the output from /proc/cmdline +# including a reference to vmlinux-local symbol that klp-convert +# will process +# - verify correct behavior +# - unload the livepatch and make sure the patch was removed + +echo -n "TEST: klp-convert symbols ... " +dmesg -C + +saved_cmdline=$(cat /proc/cmdline) + +load_mod $MOD_KLP_CONVERT_MOD + +load_lp $MOD_KLP_CONVERT1 +disable_lp $MOD_KLP_CONVERT1 +unload_lp $MOD_KLP_CONVERT1 + +load_lp $MOD_KLP_CONVERT2 +disable_lp $MOD_KLP_CONVERT2 +unload_lp $MOD_KLP_CONVERT2 + +unload_mod $MOD_KLP_CONVERT_MOD + +check_result "% modprobe $MOD_KLP_CONVERT_MOD +% modprobe $MOD_KLP_CONVERT1 +livepatch: enabling patch '$MOD_KLP_CONVERT1' +livepatch: '$MOD_KLP_CONVERT1': initializing patching transition +livepatch: '$MOD_KLP_CONVERT1': starting patching transition +$MOD_KLP_CONVERT1: saved_command_line, 0: $saved_cmdline +$MOD_KLP_CONVERT1: driver_name, 0: $MOD_KLP_CONVERT_MOD +$MOD_KLP_CONVERT1: get_driver_name(), 0: $MOD_KLP_CONVERT_MOD +$MOD_KLP_CONVERT1: homonym_string, 1: homonym string A +$MOD_KLP_CONVERT1: get_homonym_string(), 1: homonym string A +livepatch: '$MOD_KLP_CONVERT1': completing patching transition +livepatch: '$MOD_KLP_CONVERT1': patching complete +% echo 0 > /sys/kernel/livepatch/$MOD_KLP_CONVERT1/enabled +livepatch: '$MOD_KLP_CONVERT1': initializing unpatching transition +livepatch: '$MOD_KLP_CONVERT1': starting unpatching transition +livepatch: '$MOD_KLP_CONVERT1': completing unpatching transition +livepatch: '$MOD_KLP_CONVERT1': unpatching complete +% rmmod $MOD_KLP_CONVERT1 +% modprobe $MOD_KLP_CONVERT2 +livepatch: enabling patch '$MOD_KLP_CONVERT2' +livepatch: '$MOD_KLP_CONVERT2': initializing patching transition +livepatch: '$MOD_KLP_CONVERT2': starting patching transition +$MOD_KLP_CONVERT2: saved_command_line (auto): $saved_cmdline +$MOD_KLP_CONVERT2: driver_name, 0: $MOD_KLP_CONVERT_MOD +$MOD_KLP_CONVERT2: get_driver_name(), (auto): $MOD_KLP_CONVERT_MOD +$MOD_KLP_CONVERT2: homonym_string, 2: homonym string B +$MOD_KLP_CONVERT2: get_homonym_string(), 2: homonym string B +livepatch: '$MOD_KLP_CONVERT2': completing patching transition +livepatch: '$MOD_KLP_CONVERT2': patching complete +% echo 0 > /sys/kernel/livepatch/$MOD_KLP_CONVERT2/enabled +livepatch: '$MOD_KLP_CONVERT2': initializing unpatching transition +livepatch: '$MOD_KLP_CONVERT2': starting unpatching transition +livepatch: '$MOD_KLP_CONVERT2': completing unpatching transition +livepatch: '$MOD_KLP_CONVERT2': unpatching complete +% rmmod $MOD_KLP_CONVERT2 +% rmmod $MOD_KLP_CONVERT_MOD" + exit 0