From 3aadc79791e208b9d4c7eaaa1517f2732f4f138c Mon Sep 17 00:00:00 2001 From: yinbinbin Date: Wed, 2 Nov 2022 19:52:11 +0800 Subject: [PATCH] init kpatch build from https://github.com/dynup/kpatch Signed-off-by: yinbinbin --- COPYING | 339 ++ Makefile | 95 + Makefile.inc | 21 + contrib/Makefile | 17 + contrib/kpatch.conf | 29 + contrib/kpatch.service | 13 + contrib/kpatch.spec | 320 ++ doc/INSTALL.md | 316 ++ doc/patch-author-guide.md | 907 ++++ doc/s390-upstream-prerequisites.md | 33 + ...-better-follow-cubic-curve-converted.patch | 105 + ...c-better-follow-cubic-curve-original.patch | 58 + kmod/Makefile | 28 + kmod/core/Makefile | 24 + kmod/core/core.c | 1334 ++++++ kmod/core/kpatch.h | 102 + kmod/core/shadow.c | 175 + kmod/patch/Makefile | 26 + kmod/patch/kpatch-macros.h | 144 + kmod/patch/kpatch-patch-hook.c | 424 ++ kmod/patch/kpatch-patch.h | 63 + kmod/patch/kpatch-syscall.h | 180 + kmod/patch/kpatch.h | 1 + kmod/patch/kpatch.lds.S | 50 + kmod/patch/livepatch-patch-hook.c | 606 +++ kmod/patch/patch-hook.c | 25 + kpatch-build/.kpatch-cc.swp | Bin 0 -> 12288 bytes kpatch-build/Makefile | 52 + kpatch-build/create-diff-object | Bin 0 -> 203576 bytes kpatch-build/create-diff-object.c | 4041 +++++++++++++++++ kpatch-build/create-diff-object.d | 16 + kpatch-build/create-diff-object.o | Bin 0 -> 164904 bytes kpatch-build/create-klp-module | Bin 0 -> 105952 bytes kpatch-build/create-klp-module.c | 513 +++ kpatch-build/create-klp-module.d | 12 + kpatch-build/create-klp-module.o | Bin 0 -> 35704 bytes kpatch-build/create-kpatch-module | Bin 0 -> 99648 bytes kpatch-build/create-kpatch-module.c | 265 ++ kpatch-build/create-kpatch-module.d | 14 + kpatch-build/create-kpatch-module.o | Bin 0 -> 26888 bytes kpatch-build/gcc-plugins/gcc-common.h | 969 ++++ .../gcc-plugins/gcc-generate-rtl-pass.h | 176 + kpatch-build/gcc-plugins/ppc64le-plugin.c | 97 + kpatch-build/insn/asm/inat.h | 221 + kpatch-build/insn/asm/inat_types.h | 29 + kpatch-build/insn/asm/insn.h | 199 + kpatch-build/insn/inat-tables.c | 1146 +++++ kpatch-build/insn/inat.c | 97 + kpatch-build/insn/inat.d | 10 + kpatch-build/insn/inat.o | Bin 0 -> 28528 bytes kpatch-build/insn/insn.c | 582 +++ kpatch-build/insn/insn.d | 8 + kpatch-build/insn/insn.o | Bin 0 -> 24504 bytes kpatch-build/kpatch-build | 1401 ++++++ kpatch-build/kpatch-cc | 91 + kpatch-build/kpatch-elf.c | 1041 +++++ kpatch-build/kpatch-elf.d | 16 + kpatch-build/kpatch-elf.h | 187 + kpatch-build/kpatch-elf.o | Bin 0 -> 66488 bytes kpatch-build/kpatch-intermediate.h | 48 + kpatch-build/kpatch.h | 11 + kpatch-build/list.h | 221 + kpatch-build/log.h | 27 + kpatch-build/lookup.c | 561 +++ kpatch-build/lookup.d | 11 + kpatch-build/lookup.h | 23 + kpatch-build/lookup.o | Bin 0 -> 31560 bytes kpatch/Makefile | 12 + kpatch/kpatch | 649 +++ man/Makefile | 22 + man/kpatch-build.1 | 83 + man/kpatch-build.1.gz | Bin 0 -> 998 bytes man/kpatch.1 | 56 + man/kpatch.1.gz | Bin 0 -> 698 bytes test/difftree.sh | 93 + test/integration/.gitignore | 2 + test/integration/Makefile | 51 + test/integration/centos-7 | 1 + test/integration/centos-8 | 1 + test/integration/common/multiple.template | 80 + test/integration/fedora-27/README | 1 + .../fedora-27/data-new-LOADED.test | 3 + test/integration/fedora-27/data-new.patch | 20 + .../fedora-27/gcc-static-local-var-6.patch | 23 + .../fedora-27/macro-callbacks.patch | 163 + ...-cmdline-rebuild-SLOW-LOADED.test.disabled | 3 + ...eminfo-cmdline-rebuild-SLOW.patch.disabled | 40 + .../fedora-27/module-call-external.patch | 33 + .../fedora-27/module-shadow.patch.disabled | 24 + test/integration/fedora-27/multiple.test | 7 + test/integration/fedora-27/new-function.patch | 25 + test/integration/fedora-27/new-globals.patch | 34 + test/integration/fedora-27/remote-setup | 45 + .../fedora-27/shadow-newpid-LOADED.test | 3 + .../fedora-27/shadow-newpid.patch.disabled | 78 + .../fedora-27/warn-detect-FAIL.patch | 9 + test/integration/kpatch-test | 398 ++ test/integration/lib.sh | 316 ++ .../linux-5.10.11/data-new-LOADED.test | 3 + test/integration/linux-5.10.11/data-new.patch | 20 + .../gcc-static-local-var-6.patch | 22 + .../linux-5.10.11/macro-callbacks.patch | 163 + .../linux-5.10.11/module-call-external.patch | 36 + test/integration/linux-5.10.11/multiple.test | 7 + .../linux-5.10.11/new-function.patch | 27 + .../linux-5.10.11/new-globals.patch | 34 + .../linux-5.10.11/syscall-LOADED.test | 3 + test/integration/linux-5.10.11/syscall.patch | 21 + .../linux-5.10.11/warn-detect-FAIL.patch | 9 + .../linux-5.18.0/data-new-LOADED.test | 3 + test/integration/linux-5.18.0/data-new.patch | 20 + .../linux-5.18.0/gcc-static-local-var-6.patch | 22 + .../linux-5.18.0/macro-callbacks.patch | 155 + .../linux-5.18.0/module-LOADED.test | 13 + test/integration/linux-5.18.0/module.patch | 79 + test/integration/linux-5.18.0/multiple.test | 7 + .../linux-5.18.0/new-function.patch | 25 + .../linux-5.18.0/new-globals.patch | 34 + .../linux-5.18.0/shadow-newpid-LOADED.test | 3 + .../linux-5.18.0/shadow-newpid.patch | 75 + .../linux-5.18.0/special-static.patch | 22 + .../symvers-disagreement-FAIL.patch | 46 + .../linux-5.18.0/syscall-LOADED.test | 3 + test/integration/linux-5.18.0/syscall.patch | 20 + .../linux-5.18.0/warn-detect-FAIL.patch | 9 + test/integration/rebase-patches | 56 + .../rhel-7.4/bug-table-section.patch | 12 + .../rhel-7.4/cmdline-string-LOADED.test | 3 + .../integration/rhel-7.4/cmdline-string.patch | 12 + .../integration/rhel-7.4/data-new-LOADED.test | 3 + test/integration/rhel-7.4/data-new.patch | 28 + .../rhel-7.4/data-read-mostly.patch.disabled | 11 + test/integration/rhel-7.4/fixup-section.patch | 12 + test/integration/rhel-7.4/gcc-constprop.patch | 13 + test/integration/rhel-7.4/gcc-isra.patch | 11 + test/integration/rhel-7.4/gcc-mangled-3.patch | 13 + .../rhel-7.4/gcc-static-local-var-2.patch | 13 + .../rhel-7.4/gcc-static-local-var-3.patch | 19 + .../rhel-7.4/gcc-static-local-var-4.patch | 20 + .../rhel-7.4/gcc-static-local-var-4.test | 8 + .../rhel-7.4/gcc-static-local-var-5.patch | 45 + .../rhel-7.4/gcc-static-local-var-6.patch | 23 + .../rhel-7.4/gcc-static-local-var.patch | 25 + .../rhel-7.4/macro-callbacks.patch | 160 + test/integration/rhel-7.4/macro-printk.patch | 147 + .../rhel-7.4/meminfo-init-FAIL.patch | 11 + .../rhel-7.4/meminfo-init2-FAIL.patch | 19 + .../rhel-7.4/meminfo-string-LOADED.test | 3 + .../integration/rhel-7.4/meminfo-string.patch | 12 + .../rhel-7.4/module-call-external.patch | 33 + .../rhel-7.4/module-kvm-fixup.patch | 12 + test/integration/rhel-7.4/module-shadow.patch | 24 + test/integration/rhel-7.4/multiple.test | 7 + test/integration/rhel-7.4/new-function.patch | 25 + test/integration/rhel-7.4/new-globals.patch | 34 + .../rhel-7.4/parainstructions-section.patch | 11 + .../rhel-7.4/replace-section-references.patch | 12 + .../rhel-7.4/shadow-newpid-LOADED.test | 3 + test/integration/rhel-7.4/shadow-newpid.patch | 69 + .../rhel-7.4/smp-locks-section.patch | 14 + .../rhel-7.4/special-static-2.patch | 24 + .../integration/rhel-7.4/special-static.patch | 22 + .../rhel-7.4/tracepoints-section.patch | 13 + .../rhel-7.4/warn-detect-FAIL.patch | 8 + .../rhel-7.5/bug-table-section.patch | 12 + .../rhel-7.5/cmdline-string-LOADED.test | 3 + .../integration/rhel-7.5/cmdline-string.patch | 12 + .../integration/rhel-7.5/data-new-LOADED.test | 3 + test/integration/rhel-7.5/data-new.patch | 28 + .../rhel-7.5/data-read-mostly.patch.disabled | 11 + test/integration/rhel-7.5/fixup-section.patch | 12 + test/integration/rhel-7.5/gcc-constprop.patch | 13 + test/integration/rhel-7.5/gcc-isra.patch | 11 + test/integration/rhel-7.5/gcc-mangled-3.patch | 13 + .../rhel-7.5/gcc-static-local-var-2.patch | 13 + .../rhel-7.5/gcc-static-local-var-3.patch | 19 + .../rhel-7.5/gcc-static-local-var-4.patch | 20 + .../rhel-7.5/gcc-static-local-var-4.test | 8 + .../rhel-7.5/gcc-static-local-var-5.patch | 45 + .../rhel-7.5/gcc-static-local-var-6.patch | 23 + .../rhel-7.5/gcc-static-local-var.patch | 25 + .../rhel-7.5/macro-callbacks.patch | 160 + test/integration/rhel-7.5/macro-printk.patch | 147 + .../rhel-7.5/meminfo-init-FAIL.patch | 11 + .../rhel-7.5/meminfo-init2-FAIL.patch | 19 + .../rhel-7.5/meminfo-string-LOADED.test | 3 + .../integration/rhel-7.5/meminfo-string.patch | 12 + .../rhel-7.5/module-call-external.patch | 33 + .../rhel-7.5/module-kvm-fixup.patch | 12 + .../rhel-7.5/module-shadow.patch.disabled | 25 + test/integration/rhel-7.5/multiple.test | 7 + test/integration/rhel-7.5/new-function.patch | 25 + test/integration/rhel-7.5/new-globals.patch | 34 + .../rhel-7.5/parainstructions-section.patch | 11 + .../rhel-7.5/replace-section-references.patch | 12 + .../rhel-7.5/shadow-newpid-LOADED.test | 3 + test/integration/rhel-7.5/shadow-newpid.patch | 72 + .../rhel-7.5/smp-locks-section.patch | 14 + .../rhel-7.5/special-static-2.patch | 24 + .../integration/rhel-7.5/special-static.patch | 22 + .../rhel-7.5/tracepoints-section.patch | 13 + .../rhel-7.5/warn-detect-FAIL.patch | 8 + .../rhel-7.6/bug-table-section.patch | 13 + .../rhel-7.6/cmdline-string-LOADED.test | 3 + .../integration/rhel-7.6/cmdline-string.patch | 12 + .../integration/rhel-7.6/data-new-LOADED.test | 3 + test/integration/rhel-7.6/data-new.patch | 29 + .../rhel-7.6/data-read-mostly.patch.disabled | 12 + test/integration/rhel-7.6/fixup-section.patch | 12 + .../rhel-7.6/gcc-constprop.patch.disabled | 13 + test/integration/rhel-7.6/gcc-isra.patch | 12 + test/integration/rhel-7.6/gcc-mangled-3.patch | 14 + .../rhel-7.6/gcc-static-local-var-2.patch | 14 + .../rhel-7.6/gcc-static-local-var-3.patch | 20 + .../rhel-7.6/gcc-static-local-var-4.patch | 21 + .../rhel-7.6/gcc-static-local-var-4.test | 8 + .../rhel-7.6/gcc-static-local-var-5.patch | 45 + .../rhel-7.6/gcc-static-local-var-6.patch | 23 + .../rhel-7.6/macro-callbacks.patch | 160 + test/integration/rhel-7.6/macro-printk.patch | 151 + .../rhel-7.6/meminfo-init-FAIL.patch | 12 + .../rhel-7.6/meminfo-init2-FAIL.patch | 20 + .../rhel-7.6/meminfo-string-LOADED.test | 3 + .../integration/rhel-7.6/meminfo-string.patch | 12 + .../rhel-7.6/module-call-external.patch | 40 + test/integration/rhel-7.6/multiple.test | 7 + test/integration/rhel-7.6/new-function.patch | 26 + test/integration/rhel-7.6/new-globals.patch | 34 + .../rhel-7.6/parainstructions-section.patch | 11 + .../rhel-7.6/shadow-newpid-LOADED.test | 3 + test/integration/rhel-7.6/shadow-newpid.patch | 72 + .../rhel-7.6/smp-locks-section.patch | 14 + .../integration/rhel-7.6/special-static.patch | 23 + .../rhel-7.6/symvers-disagreement-FAIL.patch | 51 + .../rhel-7.6/tracepoints-section.patch | 14 + .../rhel-7.6/warn-detect-FAIL.patch | 8 + .../rhel-7.7/bug-table-section.patch | 13 + .../rhel-7.7/cmdline-string-LOADED.test | 3 + .../integration/rhel-7.7/cmdline-string.patch | 13 + .../integration/rhel-7.7/data-new-LOADED.test | 3 + test/integration/rhel-7.7/data-new.patch | 29 + .../rhel-7.7/data-read-mostly.patch.disabled | 12 + test/integration/rhel-7.7/fixup-section.patch | 12 + .../rhel-7.7/gcc-constprop.patch.disabled | 13 + test/integration/rhel-7.7/gcc-isra.patch | 12 + test/integration/rhel-7.7/gcc-mangled-3.patch | 14 + .../rhel-7.7/gcc-static-local-var-2.patch | 14 + .../rhel-7.7/gcc-static-local-var-3.patch | 20 + .../rhel-7.7/gcc-static-local-var-4.patch | 21 + .../rhel-7.7/gcc-static-local-var-4.test | 8 + .../rhel-7.7/gcc-static-local-var-5.patch | 46 + .../rhel-7.7/gcc-static-local-var-6.patch | 23 + .../rhel-7.7/macro-callbacks.patch | 166 + test/integration/rhel-7.7/macro-printk.patch | 151 + .../rhel-7.7/meminfo-init-FAIL.patch | 12 + .../rhel-7.7/meminfo-init2-FAIL.patch | 20 + .../rhel-7.7/meminfo-string-LOADED.test | 3 + .../integration/rhel-7.7/meminfo-string.patch | 13 + .../rhel-7.7/module-call-external.patch | 40 + test/integration/rhel-7.7/multiple.test | 7 + test/integration/rhel-7.7/new-function.patch | 26 + test/integration/rhel-7.7/new-globals.patch | 36 + .../rhel-7.7/parainstructions-section.patch | 12 + .../rhel-7.7/shadow-newpid-LOADED.test | 3 + test/integration/rhel-7.7/shadow-newpid.patch | 72 + .../rhel-7.7/smp-locks-section.patch | 15 + .../integration/rhel-7.7/special-static.patch | 23 + .../rhel-7.7/symvers-disagreement-FAIL.patch | 51 + .../rhel-7.7/tracepoints-section.patch | 14 + .../rhel-7.7/warn-detect-FAIL.patch | 9 + .../rhel-7.8/bug-table-section.patch | 12 + .../rhel-7.8/cmdline-string-LOADED.test | 3 + .../integration/rhel-7.8/cmdline-string.patch | 12 + .../integration/rhel-7.8/data-new-LOADED.test | 3 + test/integration/rhel-7.8/data-new.patch | 28 + .../rhel-7.8/data-read-mostly.patch.disabled | 11 + test/integration/rhel-7.8/fixup-section.patch | 11 + .../rhel-7.8/gcc-constprop.patch.disabled | 13 + test/integration/rhel-7.8/gcc-isra.patch | 11 + test/integration/rhel-7.8/gcc-mangled-3.patch | 13 + .../rhel-7.8/gcc-static-local-var-2.patch | 13 + .../rhel-7.8/gcc-static-local-var-3.patch | 19 + .../rhel-7.8/gcc-static-local-var-4.patch | 20 + .../rhel-7.8/gcc-static-local-var-4.test | 8 + .../rhel-7.8/gcc-static-local-var-5.patch | 45 + .../rhel-7.8/gcc-static-local-var-6.patch | 22 + .../rhel-7.8/macro-callbacks.patch | 163 + test/integration/rhel-7.8/macro-printk.patch | 148 + .../rhel-7.8/meminfo-init-FAIL.patch | 11 + .../rhel-7.8/meminfo-init2-FAIL.patch | 19 + .../rhel-7.8/meminfo-string-LOADED.test | 3 + .../integration/rhel-7.8/meminfo-string.patch | 12 + .../rhel-7.8/module-call-external.patch | 38 + test/integration/rhel-7.8/multiple.test | 7 + test/integration/rhel-7.8/new-function.patch | 25 + test/integration/rhel-7.8/new-globals.patch | 34 + .../rhel-7.8/parainstructions-section.patch | 11 + .../rhel-7.8/shadow-newpid-LOADED.test | 3 + test/integration/rhel-7.8/shadow-newpid.patch | 69 + .../rhel-7.8/smp-locks-section.patch | 14 + .../integration/rhel-7.8/special-static.patch | 22 + .../rhel-7.8/symvers-disagreement-FAIL.patch | 49 + .../rhel-7.8/tracepoints-section.patch | 13 + .../rhel-7.8/warn-detect-FAIL.patch | 8 + .../rhel-7.9/bug-table-section.patch | 12 + .../rhel-7.9/cmdline-string-LOADED.test | 3 + .../integration/rhel-7.9/cmdline-string.patch | 12 + .../integration/rhel-7.9/data-new-LOADED.test | 3 + test/integration/rhel-7.9/data-new.patch | 28 + .../rhel-7.9/data-read-mostly.patch.disabled | 11 + test/integration/rhel-7.9/fixup-section.patch | 11 + test/integration/rhel-7.9/gcc-constprop.patch | 13 + test/integration/rhel-7.9/gcc-isra.patch | 11 + test/integration/rhel-7.9/gcc-mangled-3.patch | 13 + .../rhel-7.9/gcc-static-local-var-2.patch | 13 + .../rhel-7.9/gcc-static-local-var-3.patch | 19 + .../rhel-7.9/gcc-static-local-var-4.patch | 20 + .../rhel-7.9/gcc-static-local-var-4.test | 8 + .../rhel-7.9/gcc-static-local-var-5.patch | 45 + .../rhel-7.9/gcc-static-local-var-6.patch | 22 + .../rhel-7.9/macro-callbacks.patch | 163 + test/integration/rhel-7.9/macro-printk.patch | 148 + .../rhel-7.9/meminfo-init-FAIL.patch | 11 + .../rhel-7.9/meminfo-init2-FAIL.patch | 19 + .../rhel-7.9/meminfo-string-LOADED.test | 3 + .../integration/rhel-7.9/meminfo-string.patch | 12 + .../rhel-7.9/module-call-external.patch | 38 + test/integration/rhel-7.9/multiple.test | 7 + test/integration/rhel-7.9/new-function.patch | 25 + test/integration/rhel-7.9/new-globals.patch | 34 + .../rhel-7.9/parainstructions-section.patch | 11 + .../rhel-7.9/shadow-newpid-LOADED.test | 3 + test/integration/rhel-7.9/shadow-newpid.patch | 69 + .../rhel-7.9/smp-locks-section.patch | 14 + .../integration/rhel-7.9/special-static.patch | 22 + .../rhel-7.9/symvers-disagreement-FAIL.patch | 49 + test/integration/rhel-7.9/syscall-LOADED.test | 3 + test/integration/rhel-7.9/syscall.patch | 26 + .../rhel-7.9/tracepoints-section.patch | 13 + .../rhel-7.9/warn-detect-FAIL.patch | 8 + test/integration/rhel-8.0/README | 1 + .../rhel-8.0/bug-table-section.patch | 13 + .../rhel-8.0/cmdline-string-LOADED.test | 3 + .../integration/rhel-8.0/cmdline-string.patch | 14 + .../integration/rhel-8.0/data-new-LOADED.test | 3 + test/integration/rhel-8.0/data-new.patch | 21 + .../rhel-8.0/data-read-mostly.patch.disabled | 14 + test/integration/rhel-8.0/fixup-section.patch | 12 + .../rhel-8.0/gcc-constprop.patch.disabled | 14 + test/integration/rhel-8.0/gcc-isra.patch | 12 + test/integration/rhel-8.0/gcc-mangled-3.patch | 14 + .../rhel-8.0/gcc-static-local-var-2.patch | 14 + .../rhel-8.0/gcc-static-local-var-3.patch | 20 + .../gcc-static-local-var-4.patch.disabled | 25 + .../gcc-static-local-var-4.test.disabled | 8 + .../rhel-8.0/gcc-static-local-var-5.patch | 46 + .../rhel-8.0/gcc-static-local-var-6.patch | 23 + .../rhel-8.0/macro-callbacks.patch | 158 + .../rhel-8.0/macro-printk.patch.disabled | 151 + .../rhel-8.0/meminfo-init-FAIL.patch | 12 + .../rhel-8.0/meminfo-init2-FAIL.patch | 20 + .../rhel-8.0/meminfo-string-LOADED.test | 3 + .../integration/rhel-8.0/meminfo-string.patch | 13 + .../rhel-8.0/module-call-external.patch | 35 + test/integration/rhel-8.0/multiple.test | 7 + test/integration/rhel-8.0/new-function.patch | 26 + test/integration/rhel-8.0/new-globals.patch | 36 + .../rhel-8.0/parainstructions-section.patch | 12 + .../rhel-8.0/shadow-newpid-LOADED.test | 3 + .../rhel-8.0/shadow-newpid.patch.disabled | 80 + .../rhel-8.0/smp-locks-section.patch | 14 + .../integration/rhel-8.0/special-static.patch | 23 + .../rhel-8.0/symvers-disagreement-FAIL.patch | 51 + .../rhel-8.0/tracepoints-section.patch | 14 + .../rhel-8.0/warn-detect-FAIL.patch | 9 + .../rhel-8.1/bug-table-section.patch | 12 + .../rhel-8.1/cmdline-string-LOADED.test | 3 + .../integration/rhel-8.1/cmdline-string.patch | 13 + .../integration/rhel-8.1/data-new-LOADED.test | 3 + test/integration/rhel-8.1/data-new.patch | 20 + .../rhel-8.1/data-read-mostly.patch.disabled | 13 + test/integration/rhel-8.1/fixup-section.patch | 11 + .../rhel-8.1/gcc-constprop.patch.disabled | 13 + test/integration/rhel-8.1/gcc-isra.patch | 11 + test/integration/rhel-8.1/gcc-mangled-3.patch | 13 + .../rhel-8.1/gcc-static-local-var-2.patch | 13 + .../rhel-8.1/gcc-static-local-var-3.patch | 19 + .../gcc-static-local-var-4.patch.disabled | 24 + .../gcc-static-local-var-4.test.disabled | 8 + .../rhel-8.1/gcc-static-local-var-5.patch | 45 + .../rhel-8.1/gcc-static-local-var-6.patch | 22 + .../rhel-8.1/macro-callbacks.patch | 155 + .../rhel-8.1/macro-printk.patch.disabled | 148 + .../rhel-8.1/meminfo-init-FAIL.patch | 11 + .../rhel-8.1/meminfo-init2-FAIL.patch | 19 + .../rhel-8.1/meminfo-string-LOADED.test | 3 + .../integration/rhel-8.1/meminfo-string.patch | 12 + test/integration/rhel-8.1/module-LOADED.test | 13 + test/integration/rhel-8.1/module.patch | 90 + test/integration/rhel-8.1/multiple.test | 7 + test/integration/rhel-8.1/new-function.patch | 25 + test/integration/rhel-8.1/new-globals.patch | 34 + .../rhel-8.1/parainstructions-section.patch | 11 + .../shadow-newpid-LOADED.test.disabled | 3 + .../rhel-8.1/shadow-newpid.patch.disabled | 77 + .../rhel-8.1/smp-locks-section.patch | 13 + .../rhel-8.1/special-static.patch.disabled | 22 + .../rhel-8.1/symvers-disagreement-FAIL.patch | 51 + .../rhel-8.1/tracepoints-section.patch | 13 + .../rhel-8.1/warn-detect-FAIL.patch | 8 + .../rhel-8.2/bug-table-section.patch | 12 + .../rhel-8.2/cmdline-string-LOADED.test | 3 + .../integration/rhel-8.2/cmdline-string.patch | 13 + .../integration/rhel-8.2/data-new-LOADED.test | 3 + test/integration/rhel-8.2/data-new.patch | 20 + .../rhel-8.2/data-read-mostly.patch.disabled | 13 + test/integration/rhel-8.2/fixup-section.patch | 11 + test/integration/rhel-8.2/gcc-constprop.patch | 13 + test/integration/rhel-8.2/gcc-isra.patch | 11 + test/integration/rhel-8.2/gcc-mangled-3.patch | 13 + .../rhel-8.2/gcc-static-local-var-2.patch | 13 + .../rhel-8.2/gcc-static-local-var-3.patch | 19 + .../gcc-static-local-var-4.patch.disabled | 24 + .../gcc-static-local-var-4.test.disabled | 8 + .../rhel-8.2/gcc-static-local-var-5.patch | 45 + .../rhel-8.2/gcc-static-local-var-6.patch | 22 + .../rhel-8.2/macro-callbacks.patch | 155 + test/integration/rhel-8.2/macro-printk.patch | 148 + .../rhel-8.2/meminfo-init-FAIL.patch | 11 + .../rhel-8.2/meminfo-init2-FAIL.patch | 19 + .../rhel-8.2/meminfo-string-LOADED.test | 3 + .../integration/rhel-8.2/meminfo-string.patch | 12 + test/integration/rhel-8.2/module-LOADED.test | 13 + test/integration/rhel-8.2/module.patch | 85 + test/integration/rhel-8.2/multiple.test | 7 + test/integration/rhel-8.2/new-function.patch | 25 + test/integration/rhel-8.2/new-globals.patch | 34 + .../rhel-8.2/parainstructions-section.patch | 11 + .../shadow-newpid-LOADED.test.disabled | 3 + .../rhel-8.2/shadow-newpid.patch.disabled | 77 + .../rhel-8.2/smp-locks-section.patch | 13 + .../rhel-8.2/special-static.patch.disabled | 22 + .../rhel-8.2/symvers-disagreement-FAIL.patch | 51 + .../rhel-8.2/tracepoints-section.patch | 13 + .../rhel-8.2/warn-detect-FAIL.patch | 8 + .../rhel-8.3/bug-table-section.patch | 12 + .../rhel-8.3/cmdline-string-LOADED.test | 3 + .../integration/rhel-8.3/cmdline-string.patch | 13 + .../integration/rhel-8.3/data-new-LOADED.test | 3 + test/integration/rhel-8.3/data-new.patch | 20 + .../rhel-8.3/data-read-mostly.patch.disabled | 13 + test/integration/rhel-8.3/fixup-section.patch | 11 + test/integration/rhel-8.3/gcc-constprop.patch | 13 + test/integration/rhel-8.3/gcc-isra.patch | 11 + test/integration/rhel-8.3/gcc-mangled-3.patch | 13 + .../rhel-8.3/gcc-static-local-var-2.patch | 13 + .../rhel-8.3/gcc-static-local-var-3.patch | 19 + .../gcc-static-local-var-4.patch.disabled | 24 + .../gcc-static-local-var-4.test.disabled | 8 + .../rhel-8.3/gcc-static-local-var-5.patch | 45 + .../rhel-8.3/gcc-static-local-var-6.patch | 22 + .../rhel-8.3/macro-callbacks.patch | 155 + test/integration/rhel-8.3/macro-printk.patch | 148 + .../rhel-8.3/meminfo-init-FAIL.patch | 11 + .../rhel-8.3/meminfo-init2-FAIL.patch | 19 + .../rhel-8.3/meminfo-string-LOADED.test | 3 + .../integration/rhel-8.3/meminfo-string.patch | 12 + test/integration/rhel-8.3/module-LOADED.test | 13 + test/integration/rhel-8.3/module.patch | 85 + test/integration/rhel-8.3/multiple.test | 7 + test/integration/rhel-8.3/new-function.patch | 25 + test/integration/rhel-8.3/new-globals.patch | 34 + .../rhel-8.3/parainstructions-section.patch | 11 + .../shadow-newpid-LOADED.test.disabled | 3 + .../rhel-8.3/shadow-newpid.patch.disabled | 77 + .../rhel-8.3/smp-locks-section.patch | 13 + .../rhel-8.3/special-static.patch.disabled | 22 + .../rhel-8.3/symvers-disagreement-FAIL.patch | 51 + .../rhel-8.3/tracepoints-section.patch | 13 + .../rhel-8.3/warn-detect-FAIL.patch | 8 + .../rhel-8.4/bug-table-section.patch | 12 + .../rhel-8.4/cmdline-string-LOADED.test | 3 + .../integration/rhel-8.4/cmdline-string.patch | 13 + .../integration/rhel-8.4/data-new-LOADED.test | 3 + test/integration/rhel-8.4/data-new.patch | 20 + .../rhel-8.4/data-read-mostly.patch | 11 + test/integration/rhel-8.4/fixup-section.patch | 11 + test/integration/rhel-8.4/gcc-constprop.patch | 13 + test/integration/rhel-8.4/gcc-isra.patch | 11 + test/integration/rhel-8.4/gcc-mangled-3.patch | 13 + .../rhel-8.4/gcc-static-local-var-2.patch | 13 + .../rhel-8.4/gcc-static-local-var-3.patch | 19 + .../rhel-8.4/gcc-static-local-var-4.patch | 23 + .../rhel-8.4/gcc-static-local-var-4.test | 8 + .../rhel-8.4/gcc-static-local-var-5.patch | 45 + .../rhel-8.4/gcc-static-local-var-6.patch | 22 + .../rhel-8.4/macro-callbacks.patch | 155 + test/integration/rhel-8.4/macro-printk.patch | 149 + .../rhel-8.4/meminfo-init-FAIL.patch | 11 + .../rhel-8.4/meminfo-init2-FAIL.patch | 19 + .../rhel-8.4/meminfo-string-LOADED.test | 3 + .../integration/rhel-8.4/meminfo-string.patch | 12 + test/integration/rhel-8.4/module-LOADED.test | 13 + test/integration/rhel-8.4/module.patch | 85 + test/integration/rhel-8.4/multiple.test | 7 + test/integration/rhel-8.4/new-function.patch | 25 + test/integration/rhel-8.4/new-globals.patch | 34 + .../rhel-8.4/parainstructions-section.patch | 11 + .../rhel-8.4/shadow-newpid-LOADED.test | 3 + test/integration/rhel-8.4/shadow-newpid.patch | 75 + .../rhel-8.4/smp-locks-section.patch | 13 + .../integration/rhel-8.4/special-static.patch | 22 + .../rhel-8.4/symvers-disagreement-FAIL.patch | 46 + test/integration/rhel-8.4/syscall-LOADED.test | 3 + test/integration/rhel-8.4/syscall.patch | 26 + .../rhel-8.4/tracepoints-section.patch | 13 + .../rhel-8.4/warn-detect-FAIL.patch | 9 + .../rhel-8.6/bug-table-section.patch | 12 + .../rhel-8.6/cmdline-string-LOADED.test | 3 + .../integration/rhel-8.6/cmdline-string.patch | 13 + .../integration/rhel-8.6/data-new-LOADED.test | 3 + test/integration/rhel-8.6/data-new.patch | 20 + .../rhel-8.6/data-read-mostly.patch | 11 + test/integration/rhel-8.6/fixup-section.patch | 11 + test/integration/rhel-8.6/gcc-constprop.patch | 13 + test/integration/rhel-8.6/gcc-isra.patch | 11 + test/integration/rhel-8.6/gcc-mangled-3.patch | 13 + .../rhel-8.6/gcc-static-local-var-2.patch | 13 + .../rhel-8.6/gcc-static-local-var-3.patch | 19 + .../rhel-8.6/gcc-static-local-var-4.patch | 23 + .../rhel-8.6/gcc-static-local-var-4.test | 8 + .../rhel-8.6/gcc-static-local-var-5.patch | 45 + .../rhel-8.6/gcc-static-local-var-6.patch | 22 + .../rhel-8.6/macro-callbacks.patch | 155 + test/integration/rhel-8.6/macro-printk.patch | 149 + .../rhel-8.6/meminfo-init-FAIL.patch | 11 + .../rhel-8.6/meminfo-init2-FAIL.patch | 19 + .../rhel-8.6/meminfo-string-LOADED.test | 3 + .../integration/rhel-8.6/meminfo-string.patch | 12 + test/integration/rhel-8.6/module-LOADED.test | 13 + test/integration/rhel-8.6/module.patch | 85 + test/integration/rhel-8.6/multiple.test | 7 + test/integration/rhel-8.6/new-function.patch | 25 + test/integration/rhel-8.6/new-globals.patch | 34 + .../rhel-8.6/parainstructions-section.patch | 11 + .../rhel-8.6/shadow-newpid-LOADED.test | 3 + test/integration/rhel-8.6/shadow-newpid.patch | 75 + .../rhel-8.6/smp-locks-section.patch | 13 + .../integration/rhel-8.6/special-static.patch | 22 + .../rhel-8.6/symvers-disagreement-FAIL.patch | 46 + test/integration/rhel-8.6/syscall-LOADED.test | 3 + test/integration/rhel-8.6/syscall.patch | 25 + .../rhel-8.6/tracepoints-section.patch | 13 + .../rhel-8.6/warn-detect-FAIL.patch | 9 + .../integration/rhel-9.0/data-new-LOADED.test | 3 + test/integration/rhel-9.0/data-new.patch | 20 + .../rhel-9.0/gcc-static-local-var-6.patch | 22 + .../rhel-9.0/macro-callbacks.patch | 155 + test/integration/rhel-9.0/module-LOADED.test | 13 + test/integration/rhel-9.0/module.patch | 79 + test/integration/rhel-9.0/multiple.test | 7 + test/integration/rhel-9.0/new-function.patch | 25 + test/integration/rhel-9.0/new-globals.patch | 34 + .../rhel-9.0/shadow-newpid-LOADED.test | 3 + test/integration/rhel-9.0/shadow-newpid.patch | 75 + .../integration/rhel-9.0/special-static.patch | 22 + .../rhel-9.0/symvers-disagreement-FAIL.patch | 46 + test/integration/rhel-9.0/syscall-LOADED.test | 3 + test/integration/rhel-9.0/syscall.patch | 20 + .../rhel-9.0/warn-detect-FAIL.patch | 9 + test/integration/test-vagrant | 42 + test/integration/ubuntu-16.04/README | 1 + .../ubuntu-16.04/bug-table-section.patch | 12 + .../ubuntu-16.04/cmdline-string-LOADED.test | 3 + .../ubuntu-16.04/cmdline-string.patch | 12 + .../ubuntu-16.04/data-new-LOADED.test | 3 + test/integration/ubuntu-16.04/data-new.patch | 28 + .../ubuntu-16.04/data-read-mostly.patch | 11 + .../ubuntu-16.04/fixup-section.patch | 12 + .../ubuntu-16.04/gcc-constprop.patch | 16 + test/integration/ubuntu-16.04/gcc-isra.patch | 11 + .../ubuntu-16.04/gcc-mangled-3.patch.disabled | 19 + .../ubuntu-16.04/gcc-static-local-var-2.patch | 13 + .../ubuntu-16.04/gcc-static-local-var-3.patch | 19 + .../ubuntu-16.04/gcc-static-local-var-4.patch | 20 + .../ubuntu-16.04/gcc-static-local-var-4.test | 8 + .../ubuntu-16.04/gcc-static-local-var-5.patch | 45 + .../ubuntu-16.04/gcc-static-local-var-6.patch | 23 + .../ubuntu-16.04/gcc-static-local-var.patch | 25 + .../ubuntu-16.04/macro-callbacks.patch | 163 + .../ubuntu-16.04/macro-printk.patch | 148 + ...-cmdline-rebuild-SLOW-LOADED.test.disabled | 3 + ...eminfo-cmdline-rebuild-SLOW.patch.disabled | 42 + .../ubuntu-16.04/meminfo-init-FAIL.patch | 11 + .../ubuntu-16.04/meminfo-init2-FAIL.patch | 19 + .../ubuntu-16.04/meminfo-string-LOADED.test | 3 + .../ubuntu-16.04/meminfo-string.patch | 12 + .../module-call-external.patch.disable | 38 + .../ubuntu-16.04/module-kvm-fixup.patch | 12 + test/integration/ubuntu-16.04/multiple.test | 7 + .../ubuntu-16.04/new-function.patch | 25 + .../ubuntu-16.04/new-globals.patch | 34 + .../parainstructions-section.patch | 11 + .../replace-section-references.patch | 12 + .../ubuntu-16.04/smp-locks-section.patch | 12 + .../ubuntu-16.04/special-static-2.patch | 24 + .../ubuntu-16.04/special-static.patch | 22 + .../ubuntu-16.04/tracepoints-section.patch | 13 + .../ubuntu-16.04/warn-detect-FAIL.patch | 8 + test/integration/vm-integration-run | 85 + test/test-functions.sh | 12 + test/unit/Makefile | 17 + test/unit/Makefile.include | 73 + 613 files changed, 33663 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 Makefile.inc create mode 100644 contrib/Makefile create mode 100644 contrib/kpatch.conf create mode 100644 contrib/kpatch.service create mode 100644 contrib/kpatch.spec create mode 100644 doc/INSTALL.md create mode 100644 doc/patch-author-guide.md create mode 100644 doc/s390-upstream-prerequisites.md create mode 100644 examples/tcp_cubic-better-follow-cubic-curve-converted.patch create mode 100644 examples/tcp_cubic-better-follow-cubic-curve-original.patch create mode 100644 kmod/Makefile create mode 100644 kmod/core/Makefile create mode 100644 kmod/core/core.c create mode 100644 kmod/core/kpatch.h create mode 100644 kmod/core/shadow.c create mode 100644 kmod/patch/Makefile create mode 100644 kmod/patch/kpatch-macros.h create mode 100644 kmod/patch/kpatch-patch-hook.c create mode 100644 kmod/patch/kpatch-patch.h create mode 100644 kmod/patch/kpatch-syscall.h create mode 120000 kmod/patch/kpatch.h create mode 100644 kmod/patch/kpatch.lds.S create mode 100644 kmod/patch/livepatch-patch-hook.c create mode 100644 kmod/patch/patch-hook.c create mode 100644 kpatch-build/.kpatch-cc.swp create mode 100644 kpatch-build/Makefile create mode 100755 kpatch-build/create-diff-object create mode 100644 kpatch-build/create-diff-object.c create mode 100644 kpatch-build/create-diff-object.d create mode 100644 kpatch-build/create-diff-object.o create mode 100755 kpatch-build/create-klp-module create mode 100644 kpatch-build/create-klp-module.c create mode 100644 kpatch-build/create-klp-module.d create mode 100644 kpatch-build/create-klp-module.o create mode 100755 kpatch-build/create-kpatch-module create mode 100644 kpatch-build/create-kpatch-module.c create mode 100644 kpatch-build/create-kpatch-module.d create mode 100644 kpatch-build/create-kpatch-module.o create mode 100644 kpatch-build/gcc-plugins/gcc-common.h create mode 100644 kpatch-build/gcc-plugins/gcc-generate-rtl-pass.h create mode 100644 kpatch-build/gcc-plugins/ppc64le-plugin.c create mode 100644 kpatch-build/insn/asm/inat.h create mode 100644 kpatch-build/insn/asm/inat_types.h create mode 100644 kpatch-build/insn/asm/insn.h create mode 100644 kpatch-build/insn/inat-tables.c create mode 100644 kpatch-build/insn/inat.c create mode 100644 kpatch-build/insn/inat.d create mode 100644 kpatch-build/insn/inat.o create mode 100644 kpatch-build/insn/insn.c create mode 100644 kpatch-build/insn/insn.d create mode 100644 kpatch-build/insn/insn.o create mode 100755 kpatch-build/kpatch-build create mode 100755 kpatch-build/kpatch-cc create mode 100644 kpatch-build/kpatch-elf.c create mode 100644 kpatch-build/kpatch-elf.d create mode 100644 kpatch-build/kpatch-elf.h create mode 100644 kpatch-build/kpatch-elf.o create mode 100644 kpatch-build/kpatch-intermediate.h create mode 100644 kpatch-build/kpatch.h create mode 100644 kpatch-build/list.h create mode 100644 kpatch-build/log.h create mode 100644 kpatch-build/lookup.c create mode 100644 kpatch-build/lookup.d create mode 100644 kpatch-build/lookup.h create mode 100644 kpatch-build/lookup.o create mode 100644 kpatch/Makefile create mode 100755 kpatch/kpatch create mode 100644 man/Makefile create mode 100644 man/kpatch-build.1 create mode 100644 man/kpatch-build.1.gz create mode 100644 man/kpatch.1 create mode 100644 man/kpatch.1.gz create mode 100755 test/difftree.sh create mode 100644 test/integration/.gitignore create mode 100644 test/integration/Makefile create mode 120000 test/integration/centos-7 create mode 120000 test/integration/centos-8 create mode 100755 test/integration/common/multiple.template create mode 100644 test/integration/fedora-27/README create mode 100755 test/integration/fedora-27/data-new-LOADED.test create mode 100644 test/integration/fedora-27/data-new.patch create mode 100644 test/integration/fedora-27/gcc-static-local-var-6.patch create mode 100644 test/integration/fedora-27/macro-callbacks.patch create mode 100755 test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled create mode 100644 test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW.patch.disabled create mode 100644 test/integration/fedora-27/module-call-external.patch create mode 100644 test/integration/fedora-27/module-shadow.patch.disabled create mode 100755 test/integration/fedora-27/multiple.test create mode 100644 test/integration/fedora-27/new-function.patch create mode 100644 test/integration/fedora-27/new-globals.patch create mode 100755 test/integration/fedora-27/remote-setup create mode 100755 test/integration/fedora-27/shadow-newpid-LOADED.test create mode 100644 test/integration/fedora-27/shadow-newpid.patch.disabled create mode 100644 test/integration/fedora-27/warn-detect-FAIL.patch create mode 100755 test/integration/kpatch-test create mode 100644 test/integration/lib.sh create mode 100755 test/integration/linux-5.10.11/data-new-LOADED.test create mode 100644 test/integration/linux-5.10.11/data-new.patch create mode 100644 test/integration/linux-5.10.11/gcc-static-local-var-6.patch create mode 100644 test/integration/linux-5.10.11/macro-callbacks.patch create mode 100644 test/integration/linux-5.10.11/module-call-external.patch create mode 100755 test/integration/linux-5.10.11/multiple.test create mode 100644 test/integration/linux-5.10.11/new-function.patch create mode 100644 test/integration/linux-5.10.11/new-globals.patch create mode 100755 test/integration/linux-5.10.11/syscall-LOADED.test create mode 100644 test/integration/linux-5.10.11/syscall.patch create mode 100644 test/integration/linux-5.10.11/warn-detect-FAIL.patch create mode 100755 test/integration/linux-5.18.0/data-new-LOADED.test create mode 100644 test/integration/linux-5.18.0/data-new.patch create mode 100644 test/integration/linux-5.18.0/gcc-static-local-var-6.patch create mode 100644 test/integration/linux-5.18.0/macro-callbacks.patch create mode 100755 test/integration/linux-5.18.0/module-LOADED.test create mode 100644 test/integration/linux-5.18.0/module.patch create mode 100755 test/integration/linux-5.18.0/multiple.test create mode 100644 test/integration/linux-5.18.0/new-function.patch create mode 100644 test/integration/linux-5.18.0/new-globals.patch create mode 100755 test/integration/linux-5.18.0/shadow-newpid-LOADED.test create mode 100644 test/integration/linux-5.18.0/shadow-newpid.patch create mode 100644 test/integration/linux-5.18.0/special-static.patch create mode 100644 test/integration/linux-5.18.0/symvers-disagreement-FAIL.patch create mode 100755 test/integration/linux-5.18.0/syscall-LOADED.test create mode 100644 test/integration/linux-5.18.0/syscall.patch create mode 100644 test/integration/linux-5.18.0/warn-detect-FAIL.patch create mode 100755 test/integration/rebase-patches create mode 100644 test/integration/rhel-7.4/bug-table-section.patch create mode 100755 test/integration/rhel-7.4/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-7.4/cmdline-string.patch create mode 100755 test/integration/rhel-7.4/data-new-LOADED.test create mode 100644 test/integration/rhel-7.4/data-new.patch create mode 100644 test/integration/rhel-7.4/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-7.4/fixup-section.patch create mode 100644 test/integration/rhel-7.4/gcc-constprop.patch create mode 100644 test/integration/rhel-7.4/gcc-isra.patch create mode 100644 test/integration/rhel-7.4/gcc-mangled-3.patch create mode 100644 test/integration/rhel-7.4/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-7.4/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-7.4/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-7.4/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-7.4/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-7.4/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-7.4/gcc-static-local-var.patch create mode 100644 test/integration/rhel-7.4/macro-callbacks.patch create mode 100644 test/integration/rhel-7.4/macro-printk.patch create mode 100644 test/integration/rhel-7.4/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-7.4/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-7.4/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-7.4/meminfo-string.patch create mode 100644 test/integration/rhel-7.4/module-call-external.patch create mode 100644 test/integration/rhel-7.4/module-kvm-fixup.patch create mode 100644 test/integration/rhel-7.4/module-shadow.patch create mode 100755 test/integration/rhel-7.4/multiple.test create mode 100644 test/integration/rhel-7.4/new-function.patch create mode 100644 test/integration/rhel-7.4/new-globals.patch create mode 100644 test/integration/rhel-7.4/parainstructions-section.patch create mode 100644 test/integration/rhel-7.4/replace-section-references.patch create mode 100755 test/integration/rhel-7.4/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-7.4/shadow-newpid.patch create mode 100644 test/integration/rhel-7.4/smp-locks-section.patch create mode 100644 test/integration/rhel-7.4/special-static-2.patch create mode 100644 test/integration/rhel-7.4/special-static.patch create mode 100644 test/integration/rhel-7.4/tracepoints-section.patch create mode 100644 test/integration/rhel-7.4/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-7.5/bug-table-section.patch create mode 100755 test/integration/rhel-7.5/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-7.5/cmdline-string.patch create mode 100755 test/integration/rhel-7.5/data-new-LOADED.test create mode 100644 test/integration/rhel-7.5/data-new.patch create mode 100644 test/integration/rhel-7.5/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-7.5/fixup-section.patch create mode 100644 test/integration/rhel-7.5/gcc-constprop.patch create mode 100644 test/integration/rhel-7.5/gcc-isra.patch create mode 100644 test/integration/rhel-7.5/gcc-mangled-3.patch create mode 100644 test/integration/rhel-7.5/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-7.5/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-7.5/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-7.5/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-7.5/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-7.5/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-7.5/gcc-static-local-var.patch create mode 100644 test/integration/rhel-7.5/macro-callbacks.patch create mode 100644 test/integration/rhel-7.5/macro-printk.patch create mode 100644 test/integration/rhel-7.5/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-7.5/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-7.5/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-7.5/meminfo-string.patch create mode 100644 test/integration/rhel-7.5/module-call-external.patch create mode 100644 test/integration/rhel-7.5/module-kvm-fixup.patch create mode 100644 test/integration/rhel-7.5/module-shadow.patch.disabled create mode 100755 test/integration/rhel-7.5/multiple.test create mode 100644 test/integration/rhel-7.5/new-function.patch create mode 100644 test/integration/rhel-7.5/new-globals.patch create mode 100644 test/integration/rhel-7.5/parainstructions-section.patch create mode 100644 test/integration/rhel-7.5/replace-section-references.patch create mode 100755 test/integration/rhel-7.5/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-7.5/shadow-newpid.patch create mode 100644 test/integration/rhel-7.5/smp-locks-section.patch create mode 100644 test/integration/rhel-7.5/special-static-2.patch create mode 100644 test/integration/rhel-7.5/special-static.patch create mode 100644 test/integration/rhel-7.5/tracepoints-section.patch create mode 100644 test/integration/rhel-7.5/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-7.6/bug-table-section.patch create mode 100755 test/integration/rhel-7.6/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-7.6/cmdline-string.patch create mode 100755 test/integration/rhel-7.6/data-new-LOADED.test create mode 100644 test/integration/rhel-7.6/data-new.patch create mode 100644 test/integration/rhel-7.6/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-7.6/fixup-section.patch create mode 100644 test/integration/rhel-7.6/gcc-constprop.patch.disabled create mode 100644 test/integration/rhel-7.6/gcc-isra.patch create mode 100644 test/integration/rhel-7.6/gcc-mangled-3.patch create mode 100644 test/integration/rhel-7.6/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-7.6/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-7.6/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-7.6/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-7.6/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-7.6/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-7.6/macro-callbacks.patch create mode 100644 test/integration/rhel-7.6/macro-printk.patch create mode 100644 test/integration/rhel-7.6/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-7.6/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-7.6/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-7.6/meminfo-string.patch create mode 100644 test/integration/rhel-7.6/module-call-external.patch create mode 100755 test/integration/rhel-7.6/multiple.test create mode 100644 test/integration/rhel-7.6/new-function.patch create mode 100644 test/integration/rhel-7.6/new-globals.patch create mode 100644 test/integration/rhel-7.6/parainstructions-section.patch create mode 100755 test/integration/rhel-7.6/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-7.6/shadow-newpid.patch create mode 100644 test/integration/rhel-7.6/smp-locks-section.patch create mode 100644 test/integration/rhel-7.6/special-static.patch create mode 100644 test/integration/rhel-7.6/symvers-disagreement-FAIL.patch create mode 100644 test/integration/rhel-7.6/tracepoints-section.patch create mode 100644 test/integration/rhel-7.6/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-7.7/bug-table-section.patch create mode 100755 test/integration/rhel-7.7/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-7.7/cmdline-string.patch create mode 100755 test/integration/rhel-7.7/data-new-LOADED.test create mode 100644 test/integration/rhel-7.7/data-new.patch create mode 100644 test/integration/rhel-7.7/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-7.7/fixup-section.patch create mode 100644 test/integration/rhel-7.7/gcc-constprop.patch.disabled create mode 100644 test/integration/rhel-7.7/gcc-isra.patch create mode 100644 test/integration/rhel-7.7/gcc-mangled-3.patch create mode 100644 test/integration/rhel-7.7/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-7.7/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-7.7/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-7.7/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-7.7/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-7.7/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-7.7/macro-callbacks.patch create mode 100644 test/integration/rhel-7.7/macro-printk.patch create mode 100644 test/integration/rhel-7.7/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-7.7/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-7.7/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-7.7/meminfo-string.patch create mode 100644 test/integration/rhel-7.7/module-call-external.patch create mode 100755 test/integration/rhel-7.7/multiple.test create mode 100644 test/integration/rhel-7.7/new-function.patch create mode 100644 test/integration/rhel-7.7/new-globals.patch create mode 100644 test/integration/rhel-7.7/parainstructions-section.patch create mode 100755 test/integration/rhel-7.7/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-7.7/shadow-newpid.patch create mode 100644 test/integration/rhel-7.7/smp-locks-section.patch create mode 100644 test/integration/rhel-7.7/special-static.patch create mode 100644 test/integration/rhel-7.7/symvers-disagreement-FAIL.patch create mode 100644 test/integration/rhel-7.7/tracepoints-section.patch create mode 100644 test/integration/rhel-7.7/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-7.8/bug-table-section.patch create mode 100755 test/integration/rhel-7.8/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-7.8/cmdline-string.patch create mode 100755 test/integration/rhel-7.8/data-new-LOADED.test create mode 100644 test/integration/rhel-7.8/data-new.patch create mode 100644 test/integration/rhel-7.8/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-7.8/fixup-section.patch create mode 100644 test/integration/rhel-7.8/gcc-constprop.patch.disabled create mode 100644 test/integration/rhel-7.8/gcc-isra.patch create mode 100644 test/integration/rhel-7.8/gcc-mangled-3.patch create mode 100644 test/integration/rhel-7.8/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-7.8/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-7.8/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-7.8/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-7.8/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-7.8/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-7.8/macro-callbacks.patch create mode 100644 test/integration/rhel-7.8/macro-printk.patch create mode 100644 test/integration/rhel-7.8/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-7.8/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-7.8/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-7.8/meminfo-string.patch create mode 100644 test/integration/rhel-7.8/module-call-external.patch create mode 100755 test/integration/rhel-7.8/multiple.test create mode 100644 test/integration/rhel-7.8/new-function.patch create mode 100644 test/integration/rhel-7.8/new-globals.patch create mode 100644 test/integration/rhel-7.8/parainstructions-section.patch create mode 100755 test/integration/rhel-7.8/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-7.8/shadow-newpid.patch create mode 100644 test/integration/rhel-7.8/smp-locks-section.patch create mode 100644 test/integration/rhel-7.8/special-static.patch create mode 100644 test/integration/rhel-7.8/symvers-disagreement-FAIL.patch create mode 100644 test/integration/rhel-7.8/tracepoints-section.patch create mode 100644 test/integration/rhel-7.8/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-7.9/bug-table-section.patch create mode 100755 test/integration/rhel-7.9/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-7.9/cmdline-string.patch create mode 100755 test/integration/rhel-7.9/data-new-LOADED.test create mode 100644 test/integration/rhel-7.9/data-new.patch create mode 100644 test/integration/rhel-7.9/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-7.9/fixup-section.patch create mode 100644 test/integration/rhel-7.9/gcc-constprop.patch create mode 100644 test/integration/rhel-7.9/gcc-isra.patch create mode 100644 test/integration/rhel-7.9/gcc-mangled-3.patch create mode 100644 test/integration/rhel-7.9/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-7.9/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-7.9/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-7.9/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-7.9/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-7.9/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-7.9/macro-callbacks.patch create mode 100644 test/integration/rhel-7.9/macro-printk.patch create mode 100644 test/integration/rhel-7.9/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-7.9/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-7.9/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-7.9/meminfo-string.patch create mode 100644 test/integration/rhel-7.9/module-call-external.patch create mode 100755 test/integration/rhel-7.9/multiple.test create mode 100644 test/integration/rhel-7.9/new-function.patch create mode 100644 test/integration/rhel-7.9/new-globals.patch create mode 100644 test/integration/rhel-7.9/parainstructions-section.patch create mode 100755 test/integration/rhel-7.9/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-7.9/shadow-newpid.patch create mode 100644 test/integration/rhel-7.9/smp-locks-section.patch create mode 100644 test/integration/rhel-7.9/special-static.patch create mode 100644 test/integration/rhel-7.9/symvers-disagreement-FAIL.patch create mode 100755 test/integration/rhel-7.9/syscall-LOADED.test create mode 100644 test/integration/rhel-7.9/syscall.patch create mode 100644 test/integration/rhel-7.9/tracepoints-section.patch create mode 100644 test/integration/rhel-7.9/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-8.0/README create mode 100644 test/integration/rhel-8.0/bug-table-section.patch create mode 100755 test/integration/rhel-8.0/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-8.0/cmdline-string.patch create mode 100755 test/integration/rhel-8.0/data-new-LOADED.test create mode 100644 test/integration/rhel-8.0/data-new.patch create mode 100644 test/integration/rhel-8.0/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-8.0/fixup-section.patch create mode 100644 test/integration/rhel-8.0/gcc-constprop.patch.disabled create mode 100644 test/integration/rhel-8.0/gcc-isra.patch create mode 100644 test/integration/rhel-8.0/gcc-mangled-3.patch create mode 100644 test/integration/rhel-8.0/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-8.0/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-8.0/gcc-static-local-var-4.patch.disabled create mode 100755 test/integration/rhel-8.0/gcc-static-local-var-4.test.disabled create mode 100644 test/integration/rhel-8.0/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-8.0/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-8.0/macro-callbacks.patch create mode 100644 test/integration/rhel-8.0/macro-printk.patch.disabled create mode 100644 test/integration/rhel-8.0/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-8.0/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-8.0/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-8.0/meminfo-string.patch create mode 100644 test/integration/rhel-8.0/module-call-external.patch create mode 100755 test/integration/rhel-8.0/multiple.test create mode 100644 test/integration/rhel-8.0/new-function.patch create mode 100644 test/integration/rhel-8.0/new-globals.patch create mode 100644 test/integration/rhel-8.0/parainstructions-section.patch create mode 100755 test/integration/rhel-8.0/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-8.0/shadow-newpid.patch.disabled create mode 100644 test/integration/rhel-8.0/smp-locks-section.patch create mode 100644 test/integration/rhel-8.0/special-static.patch create mode 100644 test/integration/rhel-8.0/symvers-disagreement-FAIL.patch create mode 100644 test/integration/rhel-8.0/tracepoints-section.patch create mode 100644 test/integration/rhel-8.0/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-8.1/bug-table-section.patch create mode 100755 test/integration/rhel-8.1/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-8.1/cmdline-string.patch create mode 100755 test/integration/rhel-8.1/data-new-LOADED.test create mode 100644 test/integration/rhel-8.1/data-new.patch create mode 100644 test/integration/rhel-8.1/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-8.1/fixup-section.patch create mode 100644 test/integration/rhel-8.1/gcc-constprop.patch.disabled create mode 100644 test/integration/rhel-8.1/gcc-isra.patch create mode 100644 test/integration/rhel-8.1/gcc-mangled-3.patch create mode 100644 test/integration/rhel-8.1/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-8.1/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-8.1/gcc-static-local-var-4.patch.disabled create mode 100755 test/integration/rhel-8.1/gcc-static-local-var-4.test.disabled create mode 100644 test/integration/rhel-8.1/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-8.1/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-8.1/macro-callbacks.patch create mode 100644 test/integration/rhel-8.1/macro-printk.patch.disabled create mode 100644 test/integration/rhel-8.1/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-8.1/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-8.1/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-8.1/meminfo-string.patch create mode 100755 test/integration/rhel-8.1/module-LOADED.test create mode 100644 test/integration/rhel-8.1/module.patch create mode 100755 test/integration/rhel-8.1/multiple.test create mode 100644 test/integration/rhel-8.1/new-function.patch create mode 100644 test/integration/rhel-8.1/new-globals.patch create mode 100644 test/integration/rhel-8.1/parainstructions-section.patch create mode 100755 test/integration/rhel-8.1/shadow-newpid-LOADED.test.disabled create mode 100644 test/integration/rhel-8.1/shadow-newpid.patch.disabled create mode 100644 test/integration/rhel-8.1/smp-locks-section.patch create mode 100644 test/integration/rhel-8.1/special-static.patch.disabled create mode 100644 test/integration/rhel-8.1/symvers-disagreement-FAIL.patch create mode 100644 test/integration/rhel-8.1/tracepoints-section.patch create mode 100644 test/integration/rhel-8.1/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-8.2/bug-table-section.patch create mode 100755 test/integration/rhel-8.2/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-8.2/cmdline-string.patch create mode 100755 test/integration/rhel-8.2/data-new-LOADED.test create mode 100644 test/integration/rhel-8.2/data-new.patch create mode 100644 test/integration/rhel-8.2/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-8.2/fixup-section.patch create mode 100644 test/integration/rhel-8.2/gcc-constprop.patch create mode 100644 test/integration/rhel-8.2/gcc-isra.patch create mode 100644 test/integration/rhel-8.2/gcc-mangled-3.patch create mode 100644 test/integration/rhel-8.2/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-8.2/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-8.2/gcc-static-local-var-4.patch.disabled create mode 100755 test/integration/rhel-8.2/gcc-static-local-var-4.test.disabled create mode 100644 test/integration/rhel-8.2/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-8.2/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-8.2/macro-callbacks.patch create mode 100644 test/integration/rhel-8.2/macro-printk.patch create mode 100644 test/integration/rhel-8.2/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-8.2/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-8.2/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-8.2/meminfo-string.patch create mode 100755 test/integration/rhel-8.2/module-LOADED.test create mode 100644 test/integration/rhel-8.2/module.patch create mode 100755 test/integration/rhel-8.2/multiple.test create mode 100644 test/integration/rhel-8.2/new-function.patch create mode 100644 test/integration/rhel-8.2/new-globals.patch create mode 100644 test/integration/rhel-8.2/parainstructions-section.patch create mode 100755 test/integration/rhel-8.2/shadow-newpid-LOADED.test.disabled create mode 100644 test/integration/rhel-8.2/shadow-newpid.patch.disabled create mode 100644 test/integration/rhel-8.2/smp-locks-section.patch create mode 100644 test/integration/rhel-8.2/special-static.patch.disabled create mode 100644 test/integration/rhel-8.2/symvers-disagreement-FAIL.patch create mode 100644 test/integration/rhel-8.2/tracepoints-section.patch create mode 100644 test/integration/rhel-8.2/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-8.3/bug-table-section.patch create mode 100755 test/integration/rhel-8.3/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-8.3/cmdline-string.patch create mode 100755 test/integration/rhel-8.3/data-new-LOADED.test create mode 100644 test/integration/rhel-8.3/data-new.patch create mode 100644 test/integration/rhel-8.3/data-read-mostly.patch.disabled create mode 100644 test/integration/rhel-8.3/fixup-section.patch create mode 100644 test/integration/rhel-8.3/gcc-constprop.patch create mode 100644 test/integration/rhel-8.3/gcc-isra.patch create mode 100644 test/integration/rhel-8.3/gcc-mangled-3.patch create mode 100644 test/integration/rhel-8.3/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-8.3/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-8.3/gcc-static-local-var-4.patch.disabled create mode 100755 test/integration/rhel-8.3/gcc-static-local-var-4.test.disabled create mode 100644 test/integration/rhel-8.3/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-8.3/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-8.3/macro-callbacks.patch create mode 100644 test/integration/rhel-8.3/macro-printk.patch create mode 100644 test/integration/rhel-8.3/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-8.3/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-8.3/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-8.3/meminfo-string.patch create mode 100755 test/integration/rhel-8.3/module-LOADED.test create mode 100644 test/integration/rhel-8.3/module.patch create mode 100755 test/integration/rhel-8.3/multiple.test create mode 100644 test/integration/rhel-8.3/new-function.patch create mode 100644 test/integration/rhel-8.3/new-globals.patch create mode 100644 test/integration/rhel-8.3/parainstructions-section.patch create mode 100755 test/integration/rhel-8.3/shadow-newpid-LOADED.test.disabled create mode 100644 test/integration/rhel-8.3/shadow-newpid.patch.disabled create mode 100644 test/integration/rhel-8.3/smp-locks-section.patch create mode 100644 test/integration/rhel-8.3/special-static.patch.disabled create mode 100644 test/integration/rhel-8.3/symvers-disagreement-FAIL.patch create mode 100644 test/integration/rhel-8.3/tracepoints-section.patch create mode 100644 test/integration/rhel-8.3/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-8.4/bug-table-section.patch create mode 100755 test/integration/rhel-8.4/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-8.4/cmdline-string.patch create mode 100755 test/integration/rhel-8.4/data-new-LOADED.test create mode 100644 test/integration/rhel-8.4/data-new.patch create mode 100644 test/integration/rhel-8.4/data-read-mostly.patch create mode 100644 test/integration/rhel-8.4/fixup-section.patch create mode 100644 test/integration/rhel-8.4/gcc-constprop.patch create mode 100644 test/integration/rhel-8.4/gcc-isra.patch create mode 100644 test/integration/rhel-8.4/gcc-mangled-3.patch create mode 100644 test/integration/rhel-8.4/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-8.4/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-8.4/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-8.4/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-8.4/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-8.4/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-8.4/macro-callbacks.patch create mode 100644 test/integration/rhel-8.4/macro-printk.patch create mode 100644 test/integration/rhel-8.4/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-8.4/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-8.4/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-8.4/meminfo-string.patch create mode 100755 test/integration/rhel-8.4/module-LOADED.test create mode 100644 test/integration/rhel-8.4/module.patch create mode 100755 test/integration/rhel-8.4/multiple.test create mode 100644 test/integration/rhel-8.4/new-function.patch create mode 100644 test/integration/rhel-8.4/new-globals.patch create mode 100644 test/integration/rhel-8.4/parainstructions-section.patch create mode 100755 test/integration/rhel-8.4/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-8.4/shadow-newpid.patch create mode 100644 test/integration/rhel-8.4/smp-locks-section.patch create mode 100644 test/integration/rhel-8.4/special-static.patch create mode 100644 test/integration/rhel-8.4/symvers-disagreement-FAIL.patch create mode 100755 test/integration/rhel-8.4/syscall-LOADED.test create mode 100644 test/integration/rhel-8.4/syscall.patch create mode 100644 test/integration/rhel-8.4/tracepoints-section.patch create mode 100644 test/integration/rhel-8.4/warn-detect-FAIL.patch create mode 100644 test/integration/rhel-8.6/bug-table-section.patch create mode 100755 test/integration/rhel-8.6/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-8.6/cmdline-string.patch create mode 100755 test/integration/rhel-8.6/data-new-LOADED.test create mode 100644 test/integration/rhel-8.6/data-new.patch create mode 100644 test/integration/rhel-8.6/data-read-mostly.patch create mode 100644 test/integration/rhel-8.6/fixup-section.patch create mode 100644 test/integration/rhel-8.6/gcc-constprop.patch create mode 100644 test/integration/rhel-8.6/gcc-isra.patch create mode 100644 test/integration/rhel-8.6/gcc-mangled-3.patch create mode 100644 test/integration/rhel-8.6/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-8.6/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-8.6/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-8.6/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-8.6/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-8.6/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-8.6/macro-callbacks.patch create mode 100644 test/integration/rhel-8.6/macro-printk.patch create mode 100644 test/integration/rhel-8.6/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-8.6/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-8.6/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-8.6/meminfo-string.patch create mode 100755 test/integration/rhel-8.6/module-LOADED.test create mode 100644 test/integration/rhel-8.6/module.patch create mode 100755 test/integration/rhel-8.6/multiple.test create mode 100644 test/integration/rhel-8.6/new-function.patch create mode 100644 test/integration/rhel-8.6/new-globals.patch create mode 100644 test/integration/rhel-8.6/parainstructions-section.patch create mode 100755 test/integration/rhel-8.6/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-8.6/shadow-newpid.patch create mode 100644 test/integration/rhel-8.6/smp-locks-section.patch create mode 100644 test/integration/rhel-8.6/special-static.patch create mode 100644 test/integration/rhel-8.6/symvers-disagreement-FAIL.patch create mode 100755 test/integration/rhel-8.6/syscall-LOADED.test create mode 100644 test/integration/rhel-8.6/syscall.patch create mode 100644 test/integration/rhel-8.6/tracepoints-section.patch create mode 100644 test/integration/rhel-8.6/warn-detect-FAIL.patch create mode 100755 test/integration/rhel-9.0/data-new-LOADED.test create mode 100644 test/integration/rhel-9.0/data-new.patch create mode 100644 test/integration/rhel-9.0/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-9.0/macro-callbacks.patch create mode 100755 test/integration/rhel-9.0/module-LOADED.test create mode 100644 test/integration/rhel-9.0/module.patch create mode 100755 test/integration/rhel-9.0/multiple.test create mode 100644 test/integration/rhel-9.0/new-function.patch create mode 100644 test/integration/rhel-9.0/new-globals.patch create mode 100755 test/integration/rhel-9.0/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-9.0/shadow-newpid.patch create mode 100644 test/integration/rhel-9.0/special-static.patch create mode 100644 test/integration/rhel-9.0/symvers-disagreement-FAIL.patch create mode 100755 test/integration/rhel-9.0/syscall-LOADED.test create mode 100644 test/integration/rhel-9.0/syscall.patch create mode 100644 test/integration/rhel-9.0/warn-detect-FAIL.patch create mode 100755 test/integration/test-vagrant create mode 100644 test/integration/ubuntu-16.04/README create mode 100644 test/integration/ubuntu-16.04/bug-table-section.patch create mode 100755 test/integration/ubuntu-16.04/cmdline-string-LOADED.test create mode 100644 test/integration/ubuntu-16.04/cmdline-string.patch create mode 100755 test/integration/ubuntu-16.04/data-new-LOADED.test create mode 100644 test/integration/ubuntu-16.04/data-new.patch create mode 100644 test/integration/ubuntu-16.04/data-read-mostly.patch create mode 100644 test/integration/ubuntu-16.04/fixup-section.patch create mode 100644 test/integration/ubuntu-16.04/gcc-constprop.patch create mode 100644 test/integration/ubuntu-16.04/gcc-isra.patch create mode 100644 test/integration/ubuntu-16.04/gcc-mangled-3.patch.disabled create mode 100644 test/integration/ubuntu-16.04/gcc-static-local-var-2.patch create mode 100644 test/integration/ubuntu-16.04/gcc-static-local-var-3.patch create mode 100644 test/integration/ubuntu-16.04/gcc-static-local-var-4.patch create mode 100755 test/integration/ubuntu-16.04/gcc-static-local-var-4.test create mode 100644 test/integration/ubuntu-16.04/gcc-static-local-var-5.patch create mode 100644 test/integration/ubuntu-16.04/gcc-static-local-var-6.patch create mode 100644 test/integration/ubuntu-16.04/gcc-static-local-var.patch create mode 100644 test/integration/ubuntu-16.04/macro-callbacks.patch create mode 100644 test/integration/ubuntu-16.04/macro-printk.patch create mode 100755 test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled create mode 100644 test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW.patch.disabled create mode 100644 test/integration/ubuntu-16.04/meminfo-init-FAIL.patch create mode 100644 test/integration/ubuntu-16.04/meminfo-init2-FAIL.patch create mode 100755 test/integration/ubuntu-16.04/meminfo-string-LOADED.test create mode 100644 test/integration/ubuntu-16.04/meminfo-string.patch create mode 100644 test/integration/ubuntu-16.04/module-call-external.patch.disable create mode 100644 test/integration/ubuntu-16.04/module-kvm-fixup.patch create mode 100755 test/integration/ubuntu-16.04/multiple.test create mode 100644 test/integration/ubuntu-16.04/new-function.patch create mode 100644 test/integration/ubuntu-16.04/new-globals.patch create mode 100644 test/integration/ubuntu-16.04/parainstructions-section.patch create mode 100644 test/integration/ubuntu-16.04/replace-section-references.patch create mode 100644 test/integration/ubuntu-16.04/smp-locks-section.patch create mode 100644 test/integration/ubuntu-16.04/special-static-2.patch create mode 100644 test/integration/ubuntu-16.04/special-static.patch create mode 100644 test/integration/ubuntu-16.04/tracepoints-section.patch create mode 100644 test/integration/ubuntu-16.04/warn-detect-FAIL.patch create mode 100755 test/integration/vm-integration-run create mode 100644 test/test-functions.sh create mode 100644 test/unit/Makefile create mode 100644 test/unit/Makefile.include diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..37ab1e8 --- /dev/null +++ b/Makefile @@ -0,0 +1,95 @@ +include Makefile.inc + +SUBDIRS = kpatch-build kpatch kmod man contrib +BUILD_DIRS = $(SUBDIRS:%=build-%) +INSTALL_DIRS = $(SUBDIRS:%=install-%) +UNINSTALL_DIRS = $(SUBDIRS:%=uninstall-%) +CLEAN_DIRS = $(SUBDIRS:%=clean-%) + +UNITTEST_DIR = test/unit +INTEGRATION_DIR = test/integration +CLEAN_DIRS += clean-$(UNITTEST_DIR) + +.PHONY: all dependencies install uninstall clean check unit +.PHONY: $(SUBDIRS) $(BUILD_DIRS) $(INSTALL_DIRS) $(CLEAN_DIRS) +.PHONY: integration integration-slow integration-quick +.PHONY: vagrant-integration-slow vagrant-integration-quick vagrant-integration +.PHONY: vagrant-install +.PHONY: help + + +all: $(BUILD_DIRS) +$(BUILD_DIRS): + $(MAKE) -C $(@:build-%=%) + +dependencies: SHELL:=/bin/bash +dependencies: + source test/integration/lib.sh && kpatch_dependencies + +install: $(INSTALL_DIRS) +$(INSTALL_DIRS): + $(MAKE) -C $(@:install-%=%) install + +uninstall: $(UNINSTALL_DIRS) +$(UNINSTALL_DIRS): + $(MAKE) -C $(@:uninstall-%=%) uninstall + +clean: $(CLEAN_DIRS) +$(CLEAN_DIRS): + $(MAKE) -C $(@:clean-%=%) clean + +unit: $(UNITTEST_DIR)/Makefile build-kpatch-build + $(MAKE) -C $(UNITTEST_DIR) + +integration: integration-quick + +integration-slow: $(INTEGRATION_DIR)/Makefile build-kpatch-build build-kpatch build-kmod + $(MAKE) -C $(INTEGRATION_DIR) slow + +integration-quick: $(INTEGRATION_DIR)/Makefile build-kpatch-build build-kpatch build-kmod + $(MAKE) -C $(INTEGRATION_DIR) quick + +vagrant-install: $(INTEGRATION_DIR)/lib.sh +ifneq ($(shell id -u), 0) + @echo "WARNING: This target is intended for use on freshly-installed machines/vms only." && \ + echo "Do not proceed unless you read $(INTEGRATION_DIR)/lib.sh and realise what this target does." && \ + echo "Press ctrl-c to abort, return to proceed." && \ + read +endif + source $(INTEGRATION_DIR)/lib.sh && kpatch_check_install_vagrant + +vagrant-integration: vagrant-integration-quick + +vagrant-integration-slow: + $(MAKE) -C $(INTEGRATION_DIR) vagrant-slow + +vagrant-integration-quick: + $(MAKE) -C $(INTEGRATION_DIR) vagrant-quick + +check: + shellcheck kpatch/kpatch kpatch-build/kpatch-build kpatch-build/kpatch-cc + shellcheck test/difftree.sh test/integration/kpatch-test \ + test/integration/lib.sh test/integration/rebase-patches \ + test/integration/test-vagrant \ + test/integration/vm-integration-run + +help: + @echo "kpatch Makefile" + @echo + @echo "Targets:" + @echo " make dependencies install build dependencies [1]" + @echo " make all build entire project" + @echo " make install install programs to system [1]" + @echo " make uninstall remove programs from system [1]" + @echo " make clean clean build files" + @echo + @echo "Test targets:" + @echo " make check run static code analyzers" + @echo " make integration build and run integration tests [2]" + @echo " make integration-slow build and run integration tests [2]" + @echo " make integration-quick build and run integration tests [2]" + @echo " make unit run unit tests" + @echo + @echo "[1] requires admin privileges" + @echo "[2] installs test kpatch kernel modules, run at your own risk" + @echo diff --git a/Makefile.inc b/Makefile.inc new file mode 100644 index 0000000..15049f3 --- /dev/null +++ b/Makefile.inc @@ -0,0 +1,21 @@ +SHELL = /bin/sh +CC = gcc + +INSTALL = /usr/bin/install + +ARCH = $(shell uname -m) + +PREFIX ?= /usr/local +LIBDIR ?= lib +LIBEXEC ?= libexec +BINDIR = $(DESTDIR)$(PREFIX)/bin +SBINDIR = $(DESTDIR)$(PREFIX)/sbin +MODULESDIR = $(DESTDIR)$(PREFIX)/$(LIBDIR)/kpatch +LIBEXECDIR = $(DESTDIR)$(PREFIX)/$(LIBEXEC)/kpatch +DATADIR = $(DESTDIR)$(PREFIX)/share/kpatch +MANDIR = $(DESTDIR)$(PREFIX)/share/man/man1 +SYSTEMDDIR = $(DESTDIR)$(PREFIX)/lib/systemd/system +UPSTARTDIR = $(DESTDIR)/etc/init + +.PHONY: all install clean +.DEFAULT: all diff --git a/contrib/Makefile b/contrib/Makefile new file mode 100644 index 0000000..0b0eeeb --- /dev/null +++ b/contrib/Makefile @@ -0,0 +1,17 @@ +include ../Makefile.inc + +all: + +install: all + $(INSTALL) -d $(SYSTEMDDIR) + $(INSTALL) -m 0644 kpatch.service $(SYSTEMDDIR) + sed -i 's~PREFIX~$(PREFIX)~' $(SYSTEMDDIR)/kpatch.service + $(INSTALL) -d $(UPSTARTDIR) + $(INSTALL) -m 0644 kpatch.conf $(UPSTARTDIR) + sed -i 's~PREFIX~$(PREFIX)~' $(UPSTARTDIR)/kpatch.conf + +uninstall: + $(RM) $(SYSTEMDDIR)/kpatch.service + $(RM) $(UPSTARTDIR)/kpatch.conf + +clean: diff --git a/contrib/kpatch.conf b/contrib/kpatch.conf new file mode 100644 index 0000000..560ce3d --- /dev/null +++ b/contrib/kpatch.conf @@ -0,0 +1,29 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +# This upstart version lacks the ability of unloading modules with +# the "stop" directive, as upstart does not support a feature like +# systemd's RemainAfterExit option. + + +description "Apply kpatch kernel patches" + +start on runlevel [2345] # Roughly equivalent to multi-user.target + +# We are not a daemon +task + +# Emulating systemd's ConditionKernelCommandLine option. +pre-start script + if [[ -e /proc/cmdline ]] + then + grep -q "kpatch.enable=0" /proc/cmdline && exit 1 + else + dmesg | grep -q "Command line.*kpatch.enable=0" && exit 1 + fi + + exit 0 +end script + +# Main process (start) +exec PREFIX/sbin/kpatch load --all + diff --git a/contrib/kpatch.service b/contrib/kpatch.service new file mode 100644 index 0000000..6240256 --- /dev/null +++ b/contrib/kpatch.service @@ -0,0 +1,13 @@ +[Unit] +Description="Apply kpatch kernel patches" +ConditionKernelCommandLine=!kpatch.enable=0 +Before=network-pre.target +Wants=network-pre.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=PREFIX/sbin/kpatch load --all + +[Install] +WantedBy=multi-user.target diff --git a/contrib/kpatch.spec b/contrib/kpatch.spec new file mode 100644 index 0000000..96a2cac --- /dev/null +++ b/contrib/kpatch.spec @@ -0,0 +1,320 @@ +# needed for the kernel specific module +%define KVER %(uname -r) + +# Don't build kpatch kernel module by default +%bcond_with kpatch_mod + +Name: kpatch +Summary: Dynamic kernel patching +Version: 0.9.7 +License: GPLv2 +Group: System Environment/Kernel +URL: http://github.com/dynup/kpatch +Release: 1%{?dist} +Source0: %{name}-%{version}.tar.gz + +Requires: kmod bash +BuildRequires: gcc kernel-devel elfutils elfutils-devel +%if %{with kpatch_mod} +BuildRequires: kernel-devel-uname-r = %{KVER} +BuildRequires: kernel-uname-r = %{KVER} +%endif +BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) + +%description +kpatch is a Linux dynamic kernel patching tool which allows you to patch a +running kernel without rebooting or restarting any processes. It enables +sysadmins to apply critical security patches to the kernel immediately, without +having to wait for long-running tasks to complete, users to log off, or +for scheduled reboot windows. It gives more control over up-time without +sacrificing security or stability. + + +%package runtime +Summary: Dynamic kernel patching +Buildarch: noarch +Provides: %{name} = %{version} +%description runtime +kpatch is a Linux dynamic kernel patching tool which allows you to patch a +running kernel without rebooting or restarting any processes. It enables +sysadmins to apply critical security patches to the kernel immediately, without +having to wait for long-running tasks to complete, users to log off, or +for scheduled reboot windows. It gives more control over up-time without +sacrificing security or stability. + + +%package build +Requires: %{name} +Summary: Dynamic kernel patching +%description build +kpatch is a Linux dynamic kernel patching tool which allows you to patch a +running kernel without rebooting or restarting any processes. It enables +sysadmins to apply critical security patches to the kernel immediately, without +having to wait for long-running tasks to complete, users to log off, or +for scheduled reboot windows. It gives more control over up-time without +sacrificing security or stability. + +%if %{with kpatch_mod} +%package %{KVER} +Requires: %{name} +Summary: Dynamic kernel patching +%description %{KVER} +kpatch is a Linux dynamic kernel patching tool which allows you to patch a +running kernel without rebooting or restarting any processes. It enables +sysadmins to apply critical security patches to the kernel immediately, without +having to wait for long-running tasks to complete, users to log off, or +for scheduled reboot windows. It gives more control over up-time without +sacrificing security or stability. + +%endif + +%prep +%setup -q + +%build +make %{_smp_mflags} %{?with_kpatch_mod: BUILDMOD=yes KPATCH_BUILD=/lib/modules/%{KVER}/build} + +%install +rm -rf %{buildroot} + +make install PREFIX=/%{_usr} DESTDIR=%{buildroot} %{?with_kpatch_mod: BUILDMOD=yes KPATCH_BUILD=/lib/modules/%{KVER}/build} + +%clean +rm -rf %{buildroot} + +%files runtime +%defattr(-,root,root,-) +%doc COPYING README.md +%{_sbindir}/kpatch +%{_mandir}/man1/kpatch.1* +%{_usr}/lib/systemd/system/* +%{_sysconfdir}/init/kpatch.conf + +%if %{with kpatch_mod} +%files %{KVER} +%defattr(-,root,root,-) +%{_usr}/lib/kpatch/%{KVER} +%endif + +%files build +%defattr(-,root,root,-) +%{_bindir}/* +%{_libexecdir}/* +%{_datadir}/%{name} +%{_mandir}/man1/kpatch-build.1* + +%changelog +* Wed Sep 14 Yannick Cote - 0.9.7 +* S390x kpatch support +* Add support for openEuler + documentation (kpatch-build) +* Use err.h instead of error.h for musl support (kpatch-build) +* Add support for .return_sites section (kpatch-build x86) +* Create missing section symbol (kpatch-build) +* Fix symtab parsing lookup (kpatch-build) +* Many fixes and improvements in create-diff-object (kpatch-build) +* Unload already disabled modules (kpatch util) +* Add integration tests for: rhel-{8.6,9.0},5.18.0 (test) +* Add tests for patching a syscall (test) +* Combine and improve Fedora, CentOS with RHEL kpatch-build dependencies (test) +* Major revamp of README.md and documentation +* Add syscall patching macros (kmod) + +* Tue Apr 12 Joe Lawrence - 0.9.6 +* Allow OOT modules to be built with non-distro kernels +* Add cross-arch unit testing support +* Support ELF extended symbol section indexes +* Allow setting kernel version if --sourcedir and --vmlinux are used +* Cleanup and enhance __LINE__ macro detection for all arches +* Fix segfault on .LCx string literal symbols +* Include __dyndbg section when referenced by jump table +* Honor user provided KBUILD_EXTRA_SYMBOLS +* Support .retpoline_sites section +* Add native compiler selection via CROSS_COMPILE + +* Wed Oct 13 Artem Savkov - 0.9.5 +- openEuler support +- kpatch-build: Do not check KLP_REPLACE for kpatch.ko-based patches +- create-diff-object: fix use after free in kpatch-check-relocations() +- kpatch-build: Handle error in create-klp-module +- create-diff-object: support ppc64le relative jump labels +- kmod/patch: clean only rebuildable objs +- kpatch-build: save environment varibles to file + +* Wed Aug 25 Yannick Cote - 0.9.4 +- Support for multiple source files +- Makefile tweaks for handling non-replace kpatch building +- Support CONFIG_PRINTK_INDEX +- kpatch-build: set EXTRAVERSION and not localversion for RH kernels +- Make sure section symbols exist +- create-diff-object: Check that the section has a secsym +- kpatch: rmmod module of the same name before loading a module +- kpatch-build: enable option -R|--replace to build replace klp +- kpatch: use /sys/kernel/kpatch/ to check whether core module is loaded +- kpatch: Sync signal subcmd usage output with manpage +- fixes for the out-of-range relocation check + +* Tue Apr 20 Yannick Cote - 0.9.3 +- Initial support for clang compiler +- Add support for rhel-8.4 +- rhel-8.4: workaround pahole and extended ELF sections +- rhel-8.4: drop klp.arch support +- Kpatch command waits for module to fully unload +- Kpatch command informs user when signal subcommand is unnecessary +- kpatch-build skips ppc64le vdso files + +* Tue Sep 8 2020 Joe Lawrence - 0.9.2 +- Integration test support for rhel-{7.8,7.9,8.1,8.2}, centos-8 +- Better support for gcc child functions +- Batch jump label errors to report all instances +- Dynrela code cleanup +- Remove .klp.arch and add support for jump labels in v5.8+ kernels +- Mark ignored sections earlier to support functions missing ftrace hook +- Minor README.md improvements +- Add ppc64le mcount support to patched functions +- Show additional stalled process information in kpatch script +- Increased shellcheck coverage and fixes +- ppc64le plugin fixes for gcc v10 +- Ignore __UNIQUE_ID_ symbol from tristate config objects +- Don't clear dmesg during integration tests +- Detect and report MODVERSIONS symbol version CRC changes + +* Wed Mar 11 2020 Yannick Cote - 0.9.1 +- Handle ppc64le toc with only constants +- Don't strip callback section symbols +- Integration tests update +- Fix -Wconversion warnings +- Process debug sections last + +* Wed Mar 11 2020 Yannick Cote - 0.9.0 +- Many fixes in integration tests and adding rhel-8.0 +- Updates to documentation +- Many updates and additions to the patch author guide +- Fix to relocations used for ZERO_PAGE(0) +- Simplify static local variables correlation +- Make symvers reading code more flexible +- Free sections in elf teardown +- Fix kpatch-test module unloading +- Disable the build of kpatch.ko module by default +- Simplify mangled function correlation +- Use whole word filename matching in find_parent_obj() +- Simplify relocation processing + +* Wed Aug 21 2019 Artem Savkov - 0.8.0 +- kpatch.ko atomic replace fixes +- Fixes for potential problems found by covscan +- Remove manual signaling logic from kpatch utility +- Don't strip callback symbols +- Allow dynamic debug static keys + +* Wed Jul 24 2019 Josh Poimboeuf - 0.7.1 +- Fix several powerpc-specific bugs, including two which can result in kernel + panics +- Use rpmbuild --nodeps for installing srpm on Fedora/RHEL +- Fix inconsistent unit test failures for FAIL tests + +* Thu Jul 18 2019 Artem Savkov - 0.7.0 +- Multiple memory leak fixes in kpatch-build +- livepatch-patch-hook compatability fixes for kernels 5.1+ +- Making kpatch-build compatible with custom gcc names +- Added rhel-rebased integration tests +- kpatch.service will no longer unload modules on stop +- kpatch load will no longer fail if a module is already loaded and enabled +- kpatch-build will now check for *_fixup section changes on ppc64le and will + fail on such changes +- Add support for R_X86_64_PLT32 +- don't allow jump labels +- ppc64le-specific kpatch-build fixes + +* Fri Apr 12 2019 Joe Lawrence - 0.6.3 +- Lots of integration test work +- Better support for building out-of-tree modules +- Updated manpage options, drop deprecated distro specific mentions +- README.md updates for shadow variables, out-of-tree modules +- Fix core module compilation with CONFIG_HAVE_ARCH_PREL32_RELOCATIONS +- kpatch-build detects and abort on unsupported options + GCC_PLUGIN_LATENT_ENTROPY, GCC_PLUGIN_RANDSTRUCT +- Fix patch linking with 4.20+ +- Other minor shellcheck and kpatch-build fixups + +* Tue Oct 2 2018 Joe Lawrence - 0.6.2 +- ppc64le: relax .text section addralign value check +- gcc8: unit-tests +- gcc8: support parent/child symbol relations +- gcc8: handle functions changing subsection +- gcc8: consider ".text.hot" sections bundleable +- kpatch-build: bugfix for less aggressive clean build-cache +- ubuntu: remove "-signed" substring from the kernel source package name +- ubuntu: explicitly note elfutils dependency +- upstream 4.18: unit-tests +- upstream 4.18: KCFLAGS -mcount-record support support +- RHEL-8: don't care who provides yumdownloader +- RHEL-8: account for quirky SRPM / release name conventions + +* Tue May 29 2018 Joe Lawrence - 0.6.1 +- Increase the transition timeout, helpful for large CPU count systems +- Miscellaneous unit testing, ppc64, etc. fixes + +* Mon Apr 22 2018 Josh Poimboeuf - 0.6.0 +- Support and converted to livepatch-style hooks. +- Lots of misc bugfixes and cleanups +- Manpage, README.md fixups +- More PPC64 work +- "Undefined reference" build failure rework +- Livepatch disable retries +- New unit testing framework + +* Thu Dec 21 2017 Josh Poimboeuf - 0.5.0 +- Basic ppc64le support +- kpatch: load automatically signals stalled processes after a timeout +- kpatch: list shows stalled processes +- kpatch: signal signals stalled processes +- kpatch-build: multiple source patches can be combined into a single binary patch module +- kpatch-build: -n|--name option for giving a custom name to the patch module +- kpatch-build: additional -d options for more verbose debug modes +- The module prefix is now either livepatch- or kpatch- depending on the underlying patching technology + +* Mon Mar 13 2017 Josh Poimboeuf - 0.4.0 +- The tools underlying kpatch-build have been made more modular, in preparation for making create-diff-object more generally useful to other use cases (kernel livepatch, Xen live patching, user space patching). +- Support for all new upstream kernels up to 4.10. +- KASLR support. +- Many other bug fixes and improvements. + +* Thu Oct 11 2016 Jessica Yu - 0.3.4 +- bump version to 0.3.4 + +* Fri Aug 19 2016 Josh Poimboeuf - 0.3.3 +- bump version to 0.3.3 + +* Thu Feb 18 2016 Josh Poimboeuf - 0.3.2 +- bump version to 0.3.2 + +* Thu Nov 19 2015 Josh Poimboeuf - 0.3.1 +- Get kernel version from vmlinux if the kernel source tree is used + +* Wed Nov 18 2015 Josh Poimboeuf - 0.3.0 +- kpatch-build: fix gcc_version_check: both "GNU" and "GCC" are possible + +* Wed Dec 3 2014 Josh Poimboeuf - 0.2.2-1 +- rebased to current version + +* Tue Sep 2 2014 Josh Poimboeuf - 0.2.1-1 +- rebased to current version + +* Mon Jul 28 2014 Josh Poimboeuf - 0.1.9-1 +- moved core module to /usr/lib/kpatch +- rebased to current version + +* Mon Jul 07 2014 Udo Seidel - 0.1.7-1 +- rebased to current version + +* Sat May 24 2014 Udo Seidel - 0.1.1-1 +- rebased to current version + +* Thu Apr 10 2014 Udo Seidel - 0.0.1-3 +- added dracut module + +* Tue Mar 25 2014 Udo Seidel - 0.0.1-2 +- added man pages + +* Sat Mar 22 2014 Udo Seidel - 0.0.1-1 +- initial release diff --git a/doc/INSTALL.md b/doc/INSTALL.md new file mode 100644 index 0000000..3b2fdb7 --- /dev/null +++ b/doc/INSTALL.md @@ -0,0 +1,316 @@ +Installation +============ + +Table of contents +================= + +- [Prerequisites](#prerequisites) + - [Fedora, RHEL, CentOS](#fedora-rhel-centos) + - [Oracle Linux 7](#oracle-linux-7) + - [Ubuntu](#ubuntu) + - [Debian 9 (Stretch)](#debian-9-stretch) + - [Debian 8 (Jessie)](#debian-8-jessie) + - [Debian 7 (Lenny)](#debian-7-lenny) + - [Gentoo](#gentoo) + - [OpenEuler](#openeuler) +- [Build](#build) +- [Install](#install) + + +Prerequisites +------------- + +Before starting, see [Supported +Architectures](../README.md#supported-architectures) and check if your device's +architecture is supported. + +### Fedora, RHEL, CentOS + +*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in +`~/.kpatch` and for ccache.* + +Install the dependencies for compiling kpatch and running kpatch-build: + +```bash +# Will request root privileges +make dependencies +``` + +### Oracle Linux 7 + +*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in +`~/.kpatch` and for ccache.* + +Install the dependencies for compiling kpatch: + +```bash +UNAME=$(uname -r) +sudo yum install gcc kernel-devel-${UNAME%.*} elfutils elfutils-devel +``` + +Install the dependencies for the "kpatch-build" command: + +```bash +sudo yum install pesign yum-utils zlib-devel \ + binutils-devel newt-devel python-devel perl-ExtUtils-Embed \ + audit-libs numactl-devel pciutils-devel bison patchutils + +# enable ol7_optional_latest repo +sudo yum-config-manager --enable ol7_optional_latest + +sudo yum-builddep kernel-${UNAME%.*} + +# manually install kernel debuginfo packages +rpm -ivh https://oss.oracle.com/ol7/debuginfo/kernel-debuginfo-$(uname -r).rpm +rpm -ivh https://oss.oracle.com/ol7/debuginfo/kernel-debuginfo-common-x86_64-$(uname -r).rpm + +# optional, but highly recommended - enable EPEL 7 +sudo yum install ccache +ccache --max-size=5G +``` + +### Ubuntu + +*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in +`~/.kpatch` and for ccache.* + +Install the dependencies for compiling kpatch and running kpatch-build + +```bash +# required on ppc64le +# e.g., on Ubuntu 18.04 for gcc-7.3 +apt-get install gcc-7-plugin-dev + +# Will request root privileges +make dependencies +``` + +### Debian 9 (Stretch) + +Since Stretch the stock kernel can be used without changes, however the +version of kpatch in Stretch is too old so you still need to build it +manually. Follow the instructions for Debian Jessie (next section) but skip +building a custom kernel/rebooting. + +### Debian 8 (Jessie) + +*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in +`~/.kpatch` and for ccache.* + +Install the dependencies for compiling kpatch: + + apt-get install make gcc libelf-dev build-essential + +Install and prepare the kernel sources: + +```bash +apt-get install linux-source-$(uname -r) +cd /usr/src && tar xvf linux-source-$(uname -r).tar.xz && ln -s linux-source-$(uname -r) linux && cd linux +cp /boot/config-$(uname -r) .config +for OPTION in CONFIG_KALLSYMS_ALL CONFIG_FUNCTION_TRACER ; do sed -i "s/# $OPTION is not set/$OPTION=y/g" .config ; done +sed -i "s/^SUBLEVEL.*/SUBLEVEL =/" Makefile +make -j`getconf _NPROCESSORS_CONF` deb-pkg KDEB_PKGVERSION=$(uname -r).9-1 +``` + +Install the kernel packages and reboot + + dpkg -i /usr/src/*.deb + reboot + +Install the dependencies for the "kpatch-build" command: + + apt-get install dpkg-dev + apt-get build-dep linux + + # required on ppc64le + # e.g., on stretch for gcc-6.3 + apt-get install gcc-6-plugin-dev + + # optional, but highly recommended + apt-get install ccache + ccache --max-size=5G + +### Debian 7 (Lenny) + +*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in +`~/.kpatch` and for ccache.* + +Add backports repositories: + +```bash +echo "deb http://http.debian.net/debian wheezy-backports main" > /etc/apt/sources.list.d/wheezy-backports.list +echo "deb http://packages.incloudus.com backports-incloudus main" > /etc/apt/sources.list.d/incloudus.list +wget http://packages.incloudus.com/incloudus/incloudus.pub -O- | apt-key add - +aptitude update +``` + +Install the linux kernel, symbols and gcc 4.9: + + aptitude install -t wheezy-backports -y initramfs-tools + aptitude install -y gcc gcc-4.9 g++-4.9 linux-image-3.14 linux-image-3.14-dbg + +Configure gcc 4.9 as the default gcc compiler: + + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.7 20 + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 50 + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.7 20 + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 50 + +Install kpatch and these dependencies: + + aptitude install kpatch + +Configure ccache (installed by kpatch package): + + ccache --max-size=5G + +### Gentoo + +*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in +`~/.kpatch` and for ccache.* + +Install Kpatch and Kpatch dependencies: + +```bash +emerge --ask sys-kernel/kpatch +``` + +Install ccache (optional): + +```bash +emerge --ask dev-util/ccache +``` + +Configure ccache: + +```bash +ccache --max-size=5G +``` + +### OpenEuler + +*ATTENTION: openEuler maintains its own version of kpatch which work with its +own kernel. You can check this [link](https://gitee.com/src-openeuler/kpatch) +to see its documents. This document describes how to run mainline kpatch in openEuler.* + +*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in +`~/.kpatch` and for ccache.* + +Install the dependencies for compiling kpatch and running kpatch-build: + +```bash +# Will request root privileges +make dependencies +``` + +Before running kpatch-build, two more things need to be checked: +------- +1. Ensure current kernel compiled with *CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY* set + + openEuler has two strategies to apply kernel live patches and it is decided at compile time. + + When CONFIG_LIVEPATCH_STOP_MACHINE_CONSISTENCY set, openEuler uses its own strategy. + + When CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY set, openEuler uses the conventional strategy. + + Only one config option can take effect at the same time. + A [chinese blog](https://www.modb.pro/db/232858) written by the openEuler official describes + their modifications for kernel livepatch. The main difference is CONFIG_LIVEPATCH_STOP_MACHINE_CONSISTENCY + will disable the usage of ftrace handler in livepatch, they believe it will be faster. + + Check whether your current kernel compiled with *CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY* + ```bash + grep "CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY" /boot/config-$(uname -r) + ``` + + If you see any output, it means your kernel satisfies, you can go directly to check step 2. + + If not, then you need to recompile your current kernel with CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY set. + + You can reference the following steps to recompile the kernel if needed + 1. download source code of the current kernel + ```bash + # set working directories + TEMPDIR=~/.tmp + mkdir -p $TEMPDIR + mkdir -p $TEMPDIR/buildroot + + # download kernel source rpm package + yumdownloader --source --destdir "$TEMPDIR" kernel-$(uname -r) + + # obtain source code from package + rpm -D "_topdir $TEMPDIR/buildroot" -ivh $TEMPDIR/kernel-*.src.rpm + rpmbuild -D "_topdir $TEMPDIR/buildroot" -bp --nodeps --target=$(uname -m) $TEMPDIR/buildroot/SPECS/kernel.spec + + # check source code and copy config file + cd $TEMPDIR/buildroot/BUILD/kernel-*/linux-*[sS]ource + cp /boot/config-$(uname -r) .config + ``` + + 2. set CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY + ```bash + make menuconfig + ``` + select order + + -> Processor type and features + -> Enable Livepatch + -> Kernel Live Patching + -> live patching method + + choose + > based on ftrace + + After this step, you shoud see CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY in .config file + + 3. recompile kernel and install it to your running environment. + + Just to remind, after installing the recompiled kernel, the config file should also be updated. + + +2. Ensure */update/source* is in the rpm repo lists + + openEuler releases its source rpm package of the kernel in two places. + + One is /source and it is included in rpm repo lists by default. + + One is /update/source and it may not be included it in some release versions. + + ```bash + grep "/update/source" /etc/yum.repos.d/openEuler.repo + ``` + + If you can't see any output, add it to the end of /etc/yum.repos.d/openEuler.repo + + For example, if you use openEuler 21.09, you will add something like: + ``` + [update-source] + name=update-source + baseurl=https://repo.openeuler.org/openEuler-21.09/update/source/ + enabled=1 + gpgcheck=0 + ``` + + *baseurl* is releated with your release version, be careful please! + + Goto [openEuler repo](https://repo.openeuler.org/), find your own suitable baseurl. + +Build +----- + +Compile kpatch: + + make + + +Install +------- + +OPTIONAL: Install kpatch to `/usr/local`: + + sudo make install + +Alternatively, the kpatch and kpatch-build scripts can be run directly from the +git tree. + + diff --git a/doc/patch-author-guide.md b/doc/patch-author-guide.md new file mode 100644 index 0000000..0133cec --- /dev/null +++ b/doc/patch-author-guide.md @@ -0,0 +1,907 @@ +kpatch Patch Author Guide +========================= + +Because kpatch-build is relatively easy to use, it can be easy to assume that a +successful patch module build means that the patch is safe to apply. But in +fact that's a very dangerous assumption. + +There are many pitfalls that can be encountered when creating a live patch. +This document attempts to guide the patch creation process. It's a work in +progress. If you find it useful, please contribute! + +Table of contents +================= + +- [Patch analysis](#patch-analysis) +- [kpatch vs livepatch vs kGraft](#kpatch-vs-livepatch-vs-kgraft) +- [Patch upgrades](#patch-upgrades) +- [Data structure changes](#data-structure-changes) +- [Data semantic changes](#data-semantic-changes) +- [Init code changes](#init-code-changes) +- [Header file changes](#header-file-changes) +- [Dealing with unexpected changed functions](#dealing-with-unexpected-changed-functions) +- [Removing references to static local variables](#removing-references-to-static-local-variables) +- [Code removal](#code-removal) +- [Once macros](#once-macros) +- [inline implies notrace](#inline-implies-notrace) +- [Jump labels](#jump-labels) +- [Sibling calls](#sibling-calls) +- [Exported symbol versioning](#exported-symbol-versioning) +- [System calls](#system-calls) + +Patch analysis +-------------- + +kpatch provides _some_ guarantees, but it does not guarantee that all patches +are safe to apply. Every patch must also be analyzed in-depth by a human. + +The most important point here cannot be stressed enough. Here comes the bold: + +**Do not blindly apply patches. There is no substitute for human analysis and +reasoning on a per-patch basis. All patches must be thoroughly analyzed by a +human kernel expert who completely understands the patch and the affected code +and how they relate to the live patching environment.** + +kpatch vs livepatch vs kGraft +----------------------------- + +This document assumes that the kpatch-build tool is being used to create +livepatch kernel modules. Other live patching systems may have different +consistency models, their own guarantees, and other subtle differences. +The guidance in this document applies **only** to kpatch-build generated +livepatches. + +Patch upgrades +-------------- + +Due to potential unexpected interactions between patches, it's highly +recommended that when patching a system which has already been patched, the +second patch should be a cumulative upgrade which is a superset of the first +patch. + +Since upstream kernel 5.1, livepatch supports a "replace" flag to help the +management of cumulative patches. With the flag set, the kernel will load +the cumulative patch and unload all existing patches in one transition. +kpatch-build enables the replace flag by default. If replace behavior is +not desired, the user can disable it with -R|--non-replace. + + +Data structure changes +---------------------- + +kpatch patches functions, not data. If the original patch involves a change to +a data structure, the patch will require some rework, as changes to data +structures are not allowed by default. + +Usually you have to get creative. There are several possible ways to handle +this: + +### Change the code which uses the data structure + +Sometimes, instead of changing the data structure itself, you can change the +code which uses it. + +For example, consider this +[patch](http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=54a20552e1eae07aa240fa370a0293e006b5faed). +which has the following hunk: + +```diff +@@ -3270,6 +3277,7 @@ static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = { + [SVM_EXIT_EXCP_BASE + PF_VECTOR] = pf_interception, + [SVM_EXIT_EXCP_BASE + NM_VECTOR] = nm_interception, + [SVM_EXIT_EXCP_BASE + MC_VECTOR] = mc_interception, ++ [SVM_EXIT_EXCP_BASE + AC_VECTOR] = ac_interception, + [SVM_EXIT_INTR] = intr_interception, + [SVM_EXIT_NMI] = nmi_interception, + [SVM_EXIT_SMI] = nop_on_interception, +``` + +`svm_exit_handlers[]` is an array of function pointers. The patch adds a +`ac_interception` function pointer to the array at index `[SVM_EXIT_EXCP_BASE + +AC_VECTOR]`. That change is incompatible with kpatch. + +Looking at the source file, we can see that this function pointer is only +accessed by a single function, `handle_exit()`: + +```c + if (exit_code >= ARRAY_SIZE(svm_exit_handlers) + || !svm_exit_handlers[exit_code]) { + WARN_ONCE(1, "svm: unexpected exit reason 0x%x\n", exit_code); + kvm_queue_exception(vcpu, UD_VECTOR); + return 1; + } + + return svm_exit_handlers[exit_code](svm); +``` + +So an easy solution here is to just change the code to manually check for the +new case before looking in the data structure: + +```diff +@@ -3580,6 +3580,9 @@ static int handle_exit(struct kvm_vcpu *vcpu) + return 1; + } + ++ if (exit_code == SVM_EXIT_EXCP_BASE + AC_VECTOR) ++ return ac_interception(svm); ++ + return svm_exit_handlers[exit_code](svm); + } +``` + +Not only is this an easy solution, it's also safer than touching data since +`svm_exit_handlers[]` may be in use by tasks that haven't been patched +yet. + +### Use a kpatch callback macro + +Kpatch supports the kernel's livepatch [(Un)patching +callbacks](https://github.com/torvalds/linux/blob/master/Documentation/livepatch/callbacks.rst). +The kernel API requires callback registration through `struct klp_callbacks`, +but to do so through kpatch-build, `kpatch-macros.h` defines the following: + +* `KPATCH_PRE_PATCH_CALLBACK` - executed before patching +* `KPATCH_POST_PATCH_CALLBACK` - executed after patching +* `KPATCH_PRE_UNPATCH_CALLBACK` - executed before unpatching, complements the + post-patch callback. +* `KPATCH_POST_UNPATCH_CALLBACK` - executed after unpatching, complements the + pre-patch callback. + +A pre-patch callback routine has the following signature: + +```c +static int callback(patch_object *obj) { } +KPATCH_PRE_PATCH_CALLBACK(callback); +``` + +and any non-zero return status indicates failure to the kernel. For more +information on pre-patch callback failure, see the **Pre-patch return status** +section below. + +Post-patch, pre-unpatch, and post-unpatch callback routines all share the +following signature: + +```c +static void callback(patch_object *obj) { } +KPATCH_POST_PATCH_CALLBACK(callback); /* or */ +KPATCH_PRE_UNPATCH_CALLBACK(callback); /* or */ +KPATCH_POST_UNPATCH_CALLBACK(callback); +``` + +Generally pre-patch callbacks are paired with post-unpatch callbacks, meaning +that anything the former allocates or sets up should be torn down by the former +callback. Likewise for post-patch and pre-unpatch callbacks. + +#### Pre-patch return status + +If kpatch is currently patching already loaded objects (vmlinux always by +definition as well as any currently loaded kernel modules), a non-zero pre-patch +callback status stops the current patch in progress. The kpatch-module +is rejected, completely reverted, and unloaded. + +If an already loaded kpatch is patching an incoming kernel module, then +a failing pre-patch callback will result in the kernel module loader +rejecting the new module. + +In both cases, if a pre-patch callback fails, none of its other +associated callbacks will be executed. + +#### Callback context + +* For patches to vmlinux or already loaded kernel modules, callback functions +will be run around the livepatch transitions in the `klp_enable_patch()` +callchain. This is executed automatically on kpatch module init. + +* For patches to kernel modules which haven't been loaded yet, a +module-notifier will execute callbacks when the module is loaded into +the `MODULE_STATE_COMING` state. The pre and post-patch callbacks are +called before any module_init code. + +Example: a kpatch fix for CVE-2016-5389 could utilize the +`KPATCH_PRE_PATCH_CALLBACK` and `KPATCH_POST_UNPATCH_CALLBACK` macros to modify +variable `sysctl_tcp_challenge_ack_limit` in-place: + +```diff ++#include "kpatch-macros.h" ++ ++static bool kpatch_write = false; ++static int kpatch_pre_patch_tcp_send_challenge_ack(patch_object *obj) ++{ ++ if (sysctl_tcp_challenge_ack_limit == 100) { ++ sysctl_tcp_challenge_ack_limit = 1000; ++ kpatch_write = true; ++ } ++ return 0; ++} +static void kpatch_post_unpatch_tcp_send_challenge_ack(patch_object *obj) ++{ ++ if (kpatch_write && sysctl_tcp_challenge_ack_limit == 1000) ++ sysctl_tcp_challenge_ack_limit = 100; ++} ++KPATCH_PRE_PATCH_CALLBACK(kpatch_pre_patch_tcp_send_challenge_ack); ++KPATCH_POST_UNPATCH_CALLBACK(kpatch_post_unpatch_tcp_send_challenge_ack); +``` + +Don't forget to protect access to data as needed. Spinlocks and mutexes / +sleeping locks **may be used** (this is a change of behavior from when kpatch +relied on the kpatch.ko support module and `stop_machine()` context.) + +Be careful when upgrading. If patch A has a pre/post-patch callback which +writes to X, and then you load patch B which is a superset of A, in some cases +you may want to prevent patch B from writing to X, if A is already loaded. + + +### Use a shadow variable + +If you need to add a field to an existing data structure, or even many existing +data structures, you can use the kernel's +[Shadow Variable](https://www.kernel.org/doc/html/latest/livepatch/shadow-vars.html) API. + +Example: The `shadow-newpid.patch` integration test employs shadow variables +to add a rolling counter to the new `struct task_struct` instances. A +simplified version is presented here. + +A shadow PID variable is allocated in `do_fork()`: it is associated with the +current `struct task_struct *p` value, given an ID of `KPATCH_SHADOW_NEWPID`, +sized accordingly, and allocated as per `GFP_KERNEL` flag rules. Note that +the shadow variable association is global -- hence it is best to +provide unique ID enumerations per kpatch as needed. + +`klp_shadow_alloc()` returns a pointer to the shadow variable, so we can +dereference and make assignments as usual. In this patch chunk, the shadow +`newpid` is allocated then assigned to a rolling `ctr` counter value: +```patch +diff --git a/kernel/fork.c b/kernel/fork.c +index 9bff3b28c357..18374fd35bd9 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1751,6 +1751,8 @@ struct task_struct *fork_idle(int cpu) + return task; + } + ++#include ++#define KPATCH_SHADOW_NEWPID 0 + /* + * Ok, this is the main fork-routine. + * +@@ -1794,6 +1796,14 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, KPATCH_SHADOW_NEWPID, ++ sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); +``` + +A shadow variable may be accessed via `klp_shadow_get()`. Here the patch +modifies `task_context_switch_counts()` to fetch the shadow variable +associated with the current `struct task_struct *p` object and a +`KPATCH_SHADOW_NEWPID ID`. As in the previous patch chunk, the shadow +variable pointer may be accessed as an ordinary pointer type: +```patch +diff --git a/fs/proc/array.c b/fs/proc/array.c +index 39684c79e8e2..fe0259d057a3 100644 +--- a/fs/proc/array.c ++++ b/fs/proc/array.c +@@ -394,13 +394,19 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p) + seq_putc(m, '\n'); + } + ++#include ++#define KPATCH_SHADOW_NEWPID 0 + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ newpid = klp_shadow_get(p, KPATCH_SHADOW_NEWPID); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +``` + +A shadow variable is freed by calling `klp_shadow_free()` and providing +the object / enum ID combination. Once freed, the shadow variable is no +longer safe to access: +```patch +diff --git a/kernel/exit.c b/kernel/exit.c +index 148a7842928d..44b6fe61e912 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -791,6 +791,8 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include ++#define KPATCH_SHADOW_NEWPID 0 + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +890,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, KPATCH_SHADOW_NEWPID, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +``` +Notes: +* `klp_shadow_alloc()` and `klp_shadow_get_or_alloc()` initialize only shadow + variable metadata. They allocate variable storage via `kmalloc` with the + `gfp_t` flags given, but otherwise leave the area untouched. Initialization + of a shadow variable is the responsibility of the caller. +* As soon as `klp_shadow_alloc()` or `klp_shadow_get_or_alloc()` create a shadow + variable, its presence will be reported by `klp_shadow_get()`. Care should be + taken to avoid any potential race conditions between a kernel thread that + allocates a shadow variable and concurrent threads that may attempt to use + it. +* Patches may need to call `klp_shadow_free_all()` from a post-unpatch handler + to safely cleanup any shadow variables of a particular ID. From post-unpatch + context, unloading kpatch module code (aside from .exit) should be + completely inactive. As long as these shadow variables were only accessed by + the unloaded kpatch, they are be safe to release. + +Data semantic changes +--------------------- + +Part of the stable-tree [backport](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit/fs/aio.c?h=linux-3.10.y&id=6745cb91b5ec93a1b34221279863926fba43d0d7) +to fix CVE-2014-0206 changed the reference count semantic of `struct +kioctx.reqs_active`. Associating a shadow variable to new instances of this +structure can be used by patched code to handle both new (post-patch) and +existing (pre-patch) instances. + +(Note: this example is trimmed to highlight this use-case. Boilerplate code is +also required to allocate/free a shadow variable with enum ID +`KPATCH_SHADOW_REQS_ACTIVE_V2` whenever a new `struct kioctx` is +created/released. No values are ever assigned to the shadow variable.) + +```patch +diff --git a/fs/aio.c b/fs/aio.c +index ebd06fd0de89..6a33b73c9107 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -280,6 +280,8 @@ static void free_ioctx_rcu(struct rcu_head *head) + * and ctx->users has dropped to 0, so we know no more kiocbs can be submitted - + * now it's safe to cancel any that need to be. + */ ++#include ++#define KPATCH_SHADOW_REQS_ACTIVE_V2 1 + static void free_ioctx(struct kioctx *ctx) + { + struct aio_ring *ring; +``` + +Shadow variable existence can be verified before applying the *new* data +semantic of the associated object: +```diff +@@ -678,6 +681,8 @@ void aio_complete(struct kiocb *iocb, long res, long res2) + put_rq: + /* everything turned out well, dispose of the aiocb. */ + aio_put_req(iocb); ++ if (klp_shadow_get(ctx, KPATCH_SHADOW_REQS_ACTIVE_V2)) ++ atomic_dec(&ctx->reqs_active); + + /* + * We have to order our ring_info tail store above and test +``` + +Likewise, shadow variable non-existence can be tested to continue applying the +*old* data semantic: +```diff +@@ -310,7 +312,8 @@ static void free_ioctx(struct kioctx *ctx) + + avail = (head <= ctx->tail ? ctx->tail : ctx->nr_events) - head; + +- atomic_sub(avail, &ctx->reqs_active); ++ if (!klp_shadow_get(ctx, KPATCH_SHADOW_REQS_ACTIVE_V2)) ++ atomic_sub(avail, &ctx->reqs_active); + head += avail; + head %= ctx->nr_events; + } +@@ -757,6 +762,8 @@ static long aio_read_events_ring(struct kioctx *ctx, + pr_debug("%li h%u t%u\n", ret, head, ctx->tail); + + atomic_sub(ret, &ctx->reqs_active); ++ if (!klp_shadow_get(ctx, KPATCH_SHADOW_REQS_ACTIVE_V2)) ++ atomic_sub(ret, &ctx->reqs_active); + out: + mutex_unlock(&ctx->ring_lock); +``` + +The previous example can be extended to use shadow variable storage to handle +locking semantic changes. Consider the [upstream fix](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1d147bfa64293b2723c4fec50922168658e613ba) +for CVE-2014-2706, which added a `ps_lock` to `struct sta_info` to protect +critical sections throughout `net/mac80211/sta_info.c`. + +When allocating a new `struct sta_info`, allocate a corresponding shadow +variable large enough to hold a `spinlock_t` instance, then initialize the +spinlock: +```patch +diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c +index decd30c1e290..758533dda4d8 100644 +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -287,6 +287,8 @@ static int sta_prepare_rate_control(struct ieee80211_local *local, + return 0; + } + ++#include ++#define KPATCH_SHADOW_PS_LOCK 2 + struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + const u8 *addr, gfp_t gfp) + { +@@ -295,6 +297,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + struct timespec uptime; + struct ieee80211_tx_latency_bin_ranges *tx_latency; + int i; ++ spinlock_t *ps_lock; + + sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); + if (!sta) +@@ -330,6 +333,10 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + rcu_read_unlock(); + + spin_lock_init(&sta->lock); ++ ps_lock = klp_shadow_alloc(sta, KPATCH_SHADOW_PS_LOCK, ++ sizeof(*ps_lock), gfp, NULL, NULL); ++ if (ps_lock) ++ spin_lock_init(ps_lock); + INIT_WORK(&sta->drv_unblock_wk, sta_unblock); + INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); + mutex_init(&sta->ampdu_mlme.mtx); +``` + +Patched code can reference the shadow variable associated with a given `struct +sta_info` to determine and apply the correct locking semantic for that +instance: +```patch +diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c +index 97a02d3f7d87..0edb0ed8dc60 100644 +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -459,12 +459,15 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta, + return 1; + } + ++#include ++#define KPATCH_SHADOW_PS_LOCK 2 + static ieee80211_tx_result + ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) + { + struct sta_info *sta = tx->sta; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + struct ieee80211_local *local = tx->local; ++ spinlock_t *ps_lock; + + if (unlikely(!sta)) + return TX_CONTINUE; +@@ -478,6 +481,23 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) + sta->sta.addr, sta->sta.aid, ac); + if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) + purge_old_ps_buffers(tx->local); ++ ++ /* sync with ieee80211_sta_ps_deliver_wakeup */ ++ ps_lock = klp_shadow_get(sta, KPATCH_SHADOW_PS_LOCK); ++ if (ps_lock) { ++ spin_lock(ps_lock); ++ /* ++ * STA woke up the meantime and all the frames on ps_tx_buf have ++ * been queued to pending queue. No reordering can happen, go ++ * ahead and Tx the packet. ++ */ ++ if (!test_sta_flag(sta, WLAN_STA_PS_STA) && ++ !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { ++ spin_unlock(ps_lock); ++ return TX_CONTINUE; ++ } ++ } ++ + if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) { + struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]); + ps_dbg(tx->sdata, +``` + +Init code changes +----------------- + +Any code which runs in an `__init` function or during module or device +initialization is problematic, as it may have already run before the patch was +applied. The patch may require a pre-patch callback which detects whether such +init code has run, and which rewrites or changes the original initialization to +force it into the desired state. Some changes involving hardware init are +inherently incompatible with live patching. + +Header file changes +------------------- + +When changing header files, be extra careful. If data is being changed, you +probably need to modify the patch. See "Data struct changes" above. + +If a function prototype is being changed, make sure it's not an exported +function. Otherwise it could break out-of-tree modules. One way to +workaround this is to define an entirely new copy of the function (with +updated code) and patch in-tree callers to invoke it rather than the +deprecated version. + +Many header file changes result in a complete rebuild of the kernel tree, which +makes kpatch-build have to compare every .o file in the kernel. It slows the +build down a lot, and can even fail to build if kpatch-build has any bugs +lurking. If it's a trivial header file change, like adding a macro, it's +advisable to just move that macro into the .c file where it's needed to avoid +changing the header file at all. + +Dealing with unexpected changed functions +----------------------------------------- + +In general, it's best to patch as minimally as possible. If kpatch-build is +reporting some unexpected function changes, it's always a good idea to try to +figure out why it thinks they changed. In many cases you can change the source +patch so that they no longer change. + +Some examples: + +* If a changed function was inlined, then the callers which inlined the + function will also change. In this case there's nothing you can do to + prevent the extra changes. + +* If a changed function was originally inlined, but turned into a callable + function after patching, consider adding `__always_inline` to the function + definition. Likewise, if a function is only inlined after patching, + consider using `noinline` to prevent the compiler from doing so. + +* If your patch adds a call to a function where the original version of the + function's ELF symbol has a .constprop or .isra suffix, and the corresponding + patched function doesn't, that means the patch caused gcc to no longer + perform an interprocedural optimization, which affects the function and all + its callers. If you want to prevent this from happening, copy/paste the + function with a new name and call the new function from your patch. + +* Moving around source code lines can introduce unique instructions if any + `__LINE__` preprocessor macros are in use. This can be mitigated by adding + any new functions to the bottom of source files, using newline whitespace to + maintain original line counts, etc. A more exact fix can be employed by + modifying the source code that invokes `__LINE__` and hard-coding the + original line number in place. This occurred in issue #1124 for example. + +Removing references to static local variables +--------------------------------------------- + +Removing references to static locals will fail to patch unless extra steps are taken. +Static locals are basically global variables because they outlive the function's +scope. They need to be correlated so that the new function will use the old static +local. That way patching the function doesn't inadvertently reset the variable +to zero; instead the variable keeps its old value. + +To work around this limitation one needs to retain the reference to the static local. +This might be as simple as adding the variable back in the patched function in a +non-functional way and ensuring the compiler doesn't optimize it away. + +Code removal +------------ + +Some fixes may replace or completely remove functions and references +to them. Remember that kpatch modules can only add new functions and +redirect existing functions, so "removed" functions will continue to exist in +kernel address space as effectively dead code. + +That means this patch (source code removal of `cmdline_proc_show`): +```patch +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2016-11-30 19:39:49.317737234 +0000 ++++ src/fs/proc/cmdline.c 2016-11-30 19:39:52.696737234 +0000 +@@ -3,15 +3,15 @@ + #include + #include + +-static int cmdline_proc_show(struct seq_file *m, void *v) +-{ +- seq_printf(m, "%s\n", saved_command_line); +- return 0; +-} ++static int cmdline_proc_show_v2(struct seq_file *m, void *v) ++{ ++ seq_printf(m, "%s kpatch\n", saved_command_line); ++ return 0; ++} + + static int cmdline_proc_open(struct inode *inode, struct file *file) + { +- return single_open(file, cmdline_proc_show, NULL); ++ return single_open(file, cmdline_proc_show_v2, NULL); + } + + static const struct file_operations cmdline_proc_fops = { +``` +will generate an equivalent kpatch module to this patch (dead +`cmdline_proc_show` left in source): +```patch +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2016-11-30 19:39:49.317737234 +0000 ++++ src/fs/proc/cmdline.c 2016-11-30 19:39:52.696737234 +0000 +@@ -9,9 +9,15 @@ static int cmdline_proc_show(struct seq_ + return 0; + } + ++static int cmdline_proc_show_v2(struct seq_file *m, void *v) ++{ ++ seq_printf(m, "%s kpatch\n", saved_command_line); ++ return 0; ++} ++ + static int cmdline_proc_open(struct inode *inode, struct file *file) + { +- return single_open(file, cmdline_proc_show, NULL); ++ return single_open(file, cmdline_proc_show_v2, NULL); + } + + static const struct file_operations cmdline_proc_fops = { +``` +In both versions, `kpatch-build` will determine that only +`cmdline_proc_open` has changed and that `cmdline_proc_show_v2` is a +new function. + +In some patching cases it might be necessary to completely remove the original +function to avoid the compiler complaining about a defined, but unused +function. This will depend on symbol scope and kernel build options. + +"Once" macros +------------- + +When adding a call to `printk_once()`, `pr_warn_once()`, or any other "once" +variation of `printk()`, you'll get the following eror: + +``` +ERROR: vmx.o: 1 unsupported section change(s) +vmx.o: WARNING: unable to correlate static local variable __print_once.60588 used by vmx_update_pi_irte, assuming variable is new +vmx.o: changed function: vmx_update_pi_irte +vmx.o: data section .data..read_mostly selected for inclusion +/usr/lib/kpatch/create-diff-object: unreconcilable difference +``` +This error occurs because the `printk_once()` adds a static local variable to +the `.data..read_mostly` section. kpatch-build strict disallows any changes to +that section, because in some cases a change to this section indicates a bug. + +To work around this issue, you'll need to manually implement your own "once" +logic which doesn't store the static variable in the `.data..read_mostly` +section. + +For example, a `pr_warn_once()` can be replaced with: +```c + static bool print_once; + ... + if (!print_once) { + print_once = true; + pr_warn("..."); + } +``` + +inline implies notrace +---------------------- + +The linux kernel defines its own version of "inline" in +include/linux/compiler_types.h which includes "notrace" as well: + +```c +#if !defined(CONFIG_OPTIMIZE_INLINING) +#define inline inline __attribute__((__always_inline__)) __gnu_inline \ + __inline_maybe_unused notrace +#else +#define inline inline __gnu_inline \ + __inline_maybe_unused notrace +#endif +``` + +With the implicit "notrace", use of "inline" in patch sources may lead +to kpatch-build errors like the following: + +1. `__tcp_mtu_to_mss()` is marked as inline: + +```c +net/ipv4/tcp_output.c: + +/* Calculate MSS not accounting any TCP options. */ +static inline int __tcp_mtu_to_mss(struct sock *sk, int pmtu) +{ +``` + +2. the compiler decides not to inline it and keeps it in its own + function-section. Then kpatch-build notices that it doesn't have an + fentry/mcount call: + +```console +% kpatch-build ... + +tcp_output.o: function __tcp_mtu_to_mss has no fentry/mcount call, unable to patch +``` + +3. a peek at the generated code: + +```c +Disassembly of section .text.__tcp_mtu_to_mss: + +0000000000000000 <__tcp_mtu_to_mss>: + 0: 48 8b 87 60 05 00 00 mov 0x560(%rdi),%rax + 7: 0f b7 50 30 movzwl 0x30(%rax),%edx + b: 0f b7 40 32 movzwl 0x32(%rax),%eax + f: 29 d6 sub %edx,%esi + 11: 83 ee 14 sub $0x14,%esi + ... +``` + +This could be a little confusing since one might have expected to see +changes to all of `__tcp_mtu_to_mss()` callers (ie, it was inlined as +requested). In this case, a simple workaround is to specify +`__tcp_mtu_to_mss()` as `__always_inline` to force the compiler to do so. + +Jump labels +----------- + +When modifying a function that contains a jump label, kpatch-build may +return an error like: `ERROR: oom_kill.o: kpatch_regenerate_special_section: 2109: Found a jump label at out_of_memory()+0x10a, using key cpusets_enabled_key. Jump labels aren't currently supported. Use static_key_enabled() instead.` + +This is due to a limitation in the kernel to process static key +livepatch relocations (resolved by late-module patching). Older +versions of kpatch-build may have reported successfully building +kpatch module, but issue +[#931](https://github.com/dynup/kpatch/issues/931) revealed potentially +dangerous behavior if the static key value had been modified from its +compiled default. + +The current workaround is to remove the jump label by explictly checking +the static key: + +```c +DEFINE_STATIC_KEY_TRUE(true_key); +DEFINE_STATIC_KEY_FALSE(false_key); + +/* unsupported */ +if (static_key_true(&true_key)) +if (static_key_false(&false_key)) +if (static_branch_likely(&key)) + +/* supported */ +if (static_key_enabled(&true_key)) +if (static_key_enabled(&false_key)) +if (likely(static_key_enabled(&key))) +``` + +Note that with Linux 5.8+, jump labels in patched functions are now supported +when the static key was originally defined in the kernel proper (vmlinux). The +above error will not be seen unless the static key lives in a module. + +Sibling calls +------------- + +GCC may generate sibling calls that are incompatible with kpatch, resulting in +an error like: `ERROR("Found an unsupported sibling call at foo()+0x123. Add __attribute__((optimize("-fno-optimize-sibling-calls"))) to foo() definition."` + +For example, if function A() calls function B() at the end of A() and both +return similar data-types, GCC may deem them "sibling calls" and apply a tail +call optimization in which A() restores the stack to is callee state before +setting up B()'s arguments and jumping to B(). + +This may be an issue for kpatches on PowerPC which modify only A() or B() and +the function call crosses a kernel module boundary: the sibling call +optimization has changed expected calling conventions and (un)patched code may +not be similarly modified. + +Commit [8b952bd77130](https://github.com/dynup/kpatch/commit/8b952bd77130) +("create-diff-object/ppc64le: Don't allow sibling calls") contains an +excellent example and description of this problem with annotated disassembly. + +Adding `__attribute__((optimize("-fno-optimize-sibling-calls")))` instructs +GCC to turn off the optimization for the given function. + +Exported symbol versioning +-------------------------- + +### Background + +`CONFIG_MODVERSIONS` enables an ABI check between exported kernel symbols and +modules referencing those symbols, enforced on module load. When building the +kernel, preprocessor output from `gcc -E` for each source file is passed to +scripts/genksyms. The genksyms script recursively expands each exported symbol +to its basic types. A hash is generated for each symbol as it traverses back up +the symbol tree. The end result is a CRC for each exported function in +the Module.symvers file and embedded in the vmlinux kernel object itself. + +A similar checksumming is performed when building modules: referenced exported +symbol CRCs are stored in the module’s `__versions` section (you can also find +these in plain-text intermediate \*.mod.c files.) + +When the kernel loads a module, the symbol CRCs found in its `__versions` are +compared to those of the kernel, if the two do not match, the kernel will refuse +to load it: +``` +: disagrees about version of symbol +: Unknown symbol (err -22) +``` + +### Kpatch detection + +After building the original and patched sources, kpatch-build compares the +newly calculated Module.symvers against the original. Discrepancies are +reported: + +``` +ERROR: Version disagreement for symbol +``` + +These reports should be addressed to ensure that the resulting kpatch module +can be loaded. + +#### False positives + +It is rare, but possible for a kpatch to introduce inadvertent symbol CRC +changes that are not true ABI changes. The following conditions must occur: + +1. The kpatch must modify the definition of an exported symbol. For example, + introducing a new header file may further define an opaque data type: + Before the kpatch, compilation unit U from the original kernel build only + knew about a `struct S` declaration (not its complete type). At the same + time, U contains function F, which has an interface that references S. If + the kpatch adds a header file to U that now fully defines `struct S { int + a, b, c; }`, its symbol type graph changes, CRCs generated for F are updated, + but its ABI remains consistent. + +2. The kpatch must introduce either a change or reference to F such that it is + included in the resulting kpatch module. This will force a `__version` + entry based on the new CRC. + + Note: if a kpatch doesn't change or reference F such that it is **not** + included in the resulting kpatch module, the new CRC value won't be added + to the module's `__version` table. However, if a future accumulative patch + does add a new change or reference to F, the new CRC will become a problem. + +#### Avoidance + +Kpatches should introduce new `#include` directives sparingly. Whenever +possible, extract the required definitions from header filers into kpatched +compilation units directly. + +If additional header files or symbol definitions cannot be avoided, consider +surrounding the offending include/definitions in an `#ifndef __GENKSYMS__` +macro. The genksyms script will skip over those blocks when performing its CRC +calculations. + +### But what about a real ABI change? + +If a kpatch introduces a true ABI change, each of calling functions would +consequently need to be updated in the kpatch module. For unexported functions, +this may be handled safely if the kpatch does indeed update all callers. +However, since motivation behind `CONFIG_MODVERSIONS` is to provide basic ABI +verification between the kernel and modules for **exported** functions, kpatch +cannot safely change this ABI without worrying about breaking other out-of-tree +drivers. Those drivers have been built against the reference kernel's original +set of CRCs and expect the original ABI. + +To track down specifically what caused a symbol CRC change, tools like +[kabi-dw](https://github.com/skozina/kabi-dw) can be employed to produce a +detailed symbol definition report. For a kpatch-build, kabi-dw can be modified +to operate on .o object files (not just .ko and vmlinux files) and the +`$CACHEDIR/tmp/{orig, patched}` directories compared. + +System calls +------------ + +Attempting to patch a syscall typically results in an error, due to a missing +fentry hook in the inner `__do_sys##name()` function. The fentry hook is +missing because of the 'inline' annotation, which invokes 'notrace'. + +This problem can be worked around by adding `#include "kpatch-syscall.h"` and +replacing the use of the `SYSCALL_DEFINE1` (or similar) macro with the +`KPATCH_` prefixed version. diff --git a/doc/s390-upstream-prerequisites.md b/doc/s390-upstream-prerequisites.md new file mode 100644 index 0000000..11173ef --- /dev/null +++ b/doc/s390-upstream-prerequisites.md @@ -0,0 +1,33 @@ +### s390 backporting + +**Prerequisite gcc patches (all backported to releases/gcc-11 branch):** +- gcc-mirror/gcc@a1c1b7a IBM Z: Define NO_PROFILE_COUNTERS +- gcc-mirror/gcc@0990d93 IBM Z: Use @PLT symbols for local functions in 64-bit mode +- gcc-mirror/gcc@935b522 S/390: New option -mpic-data-is-text-relative +- gcc-mirror/gcc@8753b13 IBM Z: fix section type conflict with -mindirect-branch-table + +**Prerequisite kernel patches:** +**v5.19:** +- 69505e3d9a39 bug: Use normal relative pointers in 'struct bug_entry' + +**v5.18:** +- 602bf1687e6f s390/nospec: align and size extern thunks +- 1d2ad084800e s390/nospec: add an option to use thunk-extern +- eed38cd2f46f s390/nospec: generate single register thunks if possible +- 2268169c14e5 s390: remove unused expoline to BC instructions +- f0003a9e4c18 s390/entry: remove unused expoline thunk + +**v5.16:** +- torvalds/linux@f6ac18f sched: Improve try_invoke_on_locked_down_task() +- torvalds/linux@9b3c4ab sched,rcu: Rework try_invoke_on_locked_down_task() +- torvalds/linux@00619f7 sched,livepatch: Use task_call_func() +- torvalds/linux@8850cb6 sched: Simplify wake_up_*idle*() +- torvalds/linux@5de62ea sched,livepatch: Use wake_up_if_idle() +- torvalds/linux@96611c2 sched: Improve wake_up_all_idle_cpus() take #2 + +**v5.15** +- torvalds/linux@de5012b s390/ftrace: implement hotpatching +- torvalds/linux@67ccddf ftrace: Introduce ftrace_need_init_nop() + +**v5.14:** +- torvalds/linux@7561c14 s390/vdso: add .got.plt in vdso linker script diff --git a/examples/tcp_cubic-better-follow-cubic-curve-converted.patch b/examples/tcp_cubic-better-follow-cubic-curve-converted.patch new file mode 100644 index 0000000..f16e5ff --- /dev/null +++ b/examples/tcp_cubic-better-follow-cubic-curve-converted.patch @@ -0,0 +1,105 @@ +The original patch changes the initialization of 'cubictcp' instance of +struct tcp_congestion_ops ('cubictcp.cwnd_event' field). Kpatch +intentionally rejects to process such changes. + +This modification of the patch uses Kpatch load/unload hooks to set +'cubictcp.cwnd_event' when the binary patch is loaded and reset it to NULL +when the patch is unloaded. + +It is still needed to check if changing that field could be problematic +due to concurrency issues, etc. + +'cwnd_event' callback is used only via tcp_ca_event() function. + +include/net/tcp.h: + +static inline void tcp_ca_event(struct sock *sk, const enum tcp_ca_event event) +{ + const struct inet_connection_sock *icsk = inet_csk(sk); + + if (icsk->icsk_ca_ops->cwnd_event) + icsk->icsk_ca_ops->cwnd_event(sk, event); +} + +In turn, tcp_ca_event() is called in a number of places in +net/ipv4/tcp_output.c and net/ipv4/tcp_input.c. + +One problem with this modification of the patch is that it may not be safe +to unload it. If it is possible for tcp_ca_event() to run concurrently with +the unloading of the patch, it may happen that 'icsk->icsk_ca_ops->cwnd_event' +is the address of bictcp_cwnd_event() when tcp_ca_event() checks it but is +set to NULL right after. So 'icsk->icsk_ca_ops->cwnd_event(sk, event)' would +result in a kernel oops. + +Whether such scenario is possible or not, it should be analyzed. If it is, +then at least, the body of tcp_ca_event() should be made atomic w.r.t. +changing 'cwnd_event' in the patch somehow. Perhaps, RCU could be suitable +for that: a read-side critical section for the body of tcp_ca_event() with +a single read of icsk->icsk_ca_ops->cwnd_event pointer with rcu_dereference(). +The pointer could be set by the patch with rcu_assign_pointer(). + +An alternative suggested by Josh Poimboeuf would be to patch the functions +that call 'cwnd_event' callback (tcp_ca_event() in this case) so that they +call bictcp_cwnd_event() directly when they detect the cubictcp struct [1]. + +Note that tcp_ca_event() is inlined in a number of places, so the binary +patch will provide replacements for all of the corresponding functions +rather than for just one. It is still needed to check if replacing these +functions in runtime is safe. + +References: +[1] https://www.redhat.com/archives/kpatch/2015-September/msg00005.html + +diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c +index 894b7ce..9bff8a0 100644 +--- a/net/ipv4/tcp_cubic.c ++++ b/net/ipv4/tcp_cubic.c +@@ -153,6 +153,27 @@ static void bictcp_init(struct sock *sk) + tcp_sk(sk)->snd_ssthresh = initial_ssthresh; + } + ++static void bictcp_cwnd_event(struct sock *sk, enum tcp_ca_event event) ++{ ++ if (event == CA_EVENT_TX_START) { ++ struct bictcp *ca = inet_csk_ca(sk); ++ u32 now = tcp_time_stamp; ++ s32 delta; ++ ++ delta = now - tcp_sk(sk)->lsndtime; ++ ++ /* We were application limited (idle) for a while. ++ * Shift epoch_start to keep cwnd growth to cubic curve. ++ */ ++ if (ca->epoch_start && delta > 0) { ++ ca->epoch_start += delta; ++ if (after(ca->epoch_start, now)) ++ ca->epoch_start = now; ++ } ++ return; ++ } ++} ++ + /* calculate the cubic root of x using a table lookup followed by one + * Newton-Raphson iteration. + * Avg err ~= 0.195% +@@ -444,6 +465,20 @@ static struct tcp_congestion_ops cubictcp __read_mostly = { + .name = "cubic", + }; + ++void kpatch_load_cubictcp_cwnd_event(void) ++{ ++ cubictcp.cwnd_event = bictcp_cwnd_event; ++} ++ ++void kpatch_unload_cubictcp_cwnd_event(void) ++{ ++ cubictcp.cwnd_event = NULL; ++} ++ ++#include "kpatch-macros.h" ++KPATCH_LOAD_HOOK(kpatch_load_cubictcp_cwnd_event); ++KPATCH_UNLOAD_HOOK(kpatch_unload_cubictcp_cwnd_event); ++ + static int __init cubictcp_register(void) + { + BUILD_BUG_ON(sizeof(struct bictcp) > ICSK_CA_PRIV_SIZE); diff --git a/examples/tcp_cubic-better-follow-cubic-curve-original.patch b/examples/tcp_cubic-better-follow-cubic-curve-original.patch new file mode 100644 index 0000000..dacc89f --- /dev/null +++ b/examples/tcp_cubic-better-follow-cubic-curve-original.patch @@ -0,0 +1,58 @@ +This patch is for 3.10.x. +It combines the following commits from the mainline: + +commit 30927520dbae297182990bb21d08762bcc35ce1d +Author: Eric Dumazet +Date: Wed Sep 9 21:55:07 2015 -0700 + + tcp_cubic: better follow cubic curve after idle period + +commit c2e7204d180f8efc80f27959ca9cf16fa17f67db +Author: Eric Dumazet +Date: Thu Sep 17 08:38:00 2015 -0700 + + tcp_cubic: do not set epoch_start in the future + +References: +http://www.phoronix.com/scan.php?page=news_item&px=Google-Fixes-TCP-Linux + +diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c +index 894b7ce..872b3a0 100644 +--- a/net/ipv4/tcp_cubic.c ++++ b/net/ipv4/tcp_cubic.c +@@ -153,6 +153,27 @@ static void bictcp_init(struct sock *sk) + tcp_sk(sk)->snd_ssthresh = initial_ssthresh; + } + ++static void bictcp_cwnd_event(struct sock *sk, enum tcp_ca_event event) ++{ ++ if (event == CA_EVENT_TX_START) { ++ struct bictcp *ca = inet_csk_ca(sk); ++ u32 now = tcp_time_stamp; ++ s32 delta; ++ ++ delta = now - tcp_sk(sk)->lsndtime; ++ ++ /* We were application limited (idle) for a while. ++ * Shift epoch_start to keep cwnd growth to cubic curve. ++ */ ++ if (ca->epoch_start && delta > 0) { ++ ca->epoch_start += delta; ++ if (after(ca->epoch_start, now)) ++ ca->epoch_start = now; ++ } ++ return; ++ } ++} ++ + /* calculate the cubic root of x using a table lookup followed by one + * Newton-Raphson iteration. + * Avg err ~= 0.195% +@@ -439,6 +460,7 @@ static struct tcp_congestion_ops cubictcp __read_mostly = { + .cong_avoid = bictcp_cong_avoid, + .set_state = bictcp_state, + .undo_cwnd = bictcp_undo_cwnd, ++ .cwnd_event = bictcp_cwnd_event, + .pkts_acked = bictcp_acked, + .owner = THIS_MODULE, + .name = "cubic", diff --git a/kmod/Makefile b/kmod/Makefile new file mode 100644 index 0000000..7b0aeed --- /dev/null +++ b/kmod/Makefile @@ -0,0 +1,28 @@ +include ../Makefile.inc +KPATCH_BUILD ?= /lib/modules/$(shell uname -r)/build +KERNELRELEASE := $(lastword $(subst /, , $(dir $(KPATCH_BUILD)))) + +all: clean +ifeq ($(BUILDMOD),yes) + $(MAKE) -C core +endif + +install: +ifeq ($(BUILDMOD),yes) + $(INSTALL) -d $(MODULESDIR)/$(KERNELRELEASE) + $(INSTALL) -m 644 core/kpatch.ko $(MODULESDIR)/$(KERNELRELEASE) + $(INSTALL) -m 644 core/Module.symvers $(MODULESDIR)/$(KERNELRELEASE) +endif + $(INSTALL) -d $(DATADIR)/patch + $(INSTALL) -m 644 patch/* $(DATADIR)/patch + +uninstall: +ifeq ($(BUILDMOD),yes) + $(RM) -R $(MODULESDIR) +endif + $(RM) -R $(DATADIR) + +clean: +ifeq ($(BUILDMOD),yes) + $(MAKE) -C core clean +endif diff --git a/kmod/core/Makefile b/kmod/core/Makefile new file mode 100644 index 0000000..93e8490 --- /dev/null +++ b/kmod/core/Makefile @@ -0,0 +1,24 @@ +# make rules +KPATCH_BUILD ?= /lib/modules/$(shell uname -r)/build +KERNELRELEASE := $(lastword $(subst /, , $(dir $(patsubst %/,%,$(KPATCH_BUILD))))) +THISDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) + +ifeq ($(wildcard $(KPATCH_BUILD)),) +$(error $(KPATCH_BUILD) doesn\'t exist. Try installing the kernel-devel-$(KERNELRELEASE) RPM or linux-headers-$(KERNELRELEASE) DEB.) +endif + +KPATCH_MAKE = $(MAKE) -C $(KPATCH_BUILD) M=$(THISDIR) + +kpatch.ko: core.c + $(KPATCH_MAKE) kpatch.ko + +all: kpatch.ko + +clean: + $(RM) -Rf .*.o.cmd .*.ko.cmd .tmp_versions *.o *.ko *.mod.c \ + Module.symvers + + +# kbuild rules +obj-m := kpatch.o +kpatch-y := core.o shadow.o diff --git a/kmod/core/core.c b/kmod/core/core.c new file mode 100644 index 0000000..cc97ad0 --- /dev/null +++ b/kmod/core/core.c @@ -0,0 +1,1334 @@ +/* + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2013-2014 Josh Poimboeuf + * + * 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 . + */ + +/* + * kpatch core module + * + * Patch modules register with this module to redirect old functions to new + * functions. + * + * For each function patched by the module we must: + * - Call stop_machine + * - Ensure that no task has the old function in its call stack + * - Add the new function address to kpatch_func_hash + * + * After that, each call to the old function calls into kpatch_ftrace_handler() + * which finds the new function in kpatch_func_hash table and updates the + * return instruction pointer so that ftrace will return to the new function. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kpatch.h" + +#ifndef UTS_UBUNTU_RELEASE_ABI +#define UTS_UBUNTU_RELEASE_ABI 0 +#endif + +#if !defined(CONFIG_FUNCTION_TRACER) || \ + !defined(CONFIG_HAVE_FENTRY) || \ + !defined(CONFIG_MODULES) || \ + !defined(CONFIG_SYSFS) || \ + !defined(CONFIG_STACKTRACE) || \ + !defined(CONFIG_KALLSYMS_ALL) +#error "CONFIG_FUNCTION_TRACER, CONFIG_HAVE_FENTRY, CONFIG_MODULES, CONFIG_SYSFS, CONFIG_KALLSYMS_ALL kernel config options are required" +#endif + +#define KPATCH_HASH_BITS 8 +static DEFINE_HASHTABLE(kpatch_func_hash, KPATCH_HASH_BITS); + +static DEFINE_SEMAPHORE(kpatch_mutex); + +static LIST_HEAD(kpmod_list); + +static int kpatch_num_patched; + +struct kobject *kpatch_root_kobj; +EXPORT_SYMBOL_GPL(kpatch_root_kobj); + +struct kpatch_kallsyms_args { + const char *objname; + const char *name; + unsigned long addr; + unsigned long count; + unsigned long pos; +}; + +struct kpatch_apply_patch_args { + struct kpatch_module *kpmod; + bool replace; +}; + +/* this is a double loop, use goto instead of break */ +#define do_for_each_linked_func(kpmod, func) { \ + struct kpatch_object *_object; \ + list_for_each_entry(_object, &kpmod->objects, list) { \ + if (!kpatch_object_linked(_object)) \ + continue; \ + list_for_each_entry(func, &_object->funcs, list) { + +#define while_for_each_linked_func() \ + } \ + } \ +} + + +/* + * The kpatch core module has a state machine which allows for proper + * synchronization with kpatch_ftrace_handler() when it runs in NMI context. + * + * +-----------------------------------------------------+ + * | | + * | + + * v +---> KPATCH_STATE_SUCCESS + * KPATCH_STATE_IDLE +---> KPATCH_STATE_UPDATING | + * ^ +---> KPATCH_STATE_FAILURE + * | + + * | | + * +-----------------------------------------------------+ + * + * KPATCH_STATE_IDLE: No updates are pending. The func hash is valid, and the + * reader doesn't need to check func->op. + * + * KPATCH_STATE_UPDATING: An update is in progress. The reader must call + * kpatch_state_finish(KPATCH_STATE_FAILURE) before accessing the func hash. + * + * KPATCH_STATE_FAILURE: An update failed, and the func hash might be + * inconsistent (pending patched funcs might not have been removed yet). If + * func->op is KPATCH_OP_PATCH, then rollback to the previous version of the + * func. + * + * KPATCH_STATE_SUCCESS: An update succeeded, but the func hash might be + * inconsistent (pending unpatched funcs might not have been removed yet). If + * func->op is KPATCH_OP_UNPATCH, then rollback to the previous version of the + * func. + */ +enum { + KPATCH_STATE_IDLE, + KPATCH_STATE_UPDATING, + KPATCH_STATE_SUCCESS, + KPATCH_STATE_FAILURE, +}; +static atomic_t kpatch_state; + +static int (*kpatch_set_memory_rw)(unsigned long addr, int numpages); +static int (*kpatch_set_memory_ro)(unsigned long addr, int numpages); + +#define MAX_STACK_TRACE_DEPTH 64 +static unsigned long stack_entries[MAX_STACK_TRACE_DEPTH]; +static struct stack_trace trace = { + .max_entries = ARRAY_SIZE(stack_entries), + .entries = &stack_entries[0], +}; + +static inline void kpatch_state_idle(void) +{ + int state = atomic_read(&kpatch_state); + + WARN_ON(state != KPATCH_STATE_SUCCESS && state != KPATCH_STATE_FAILURE); + atomic_set(&kpatch_state, KPATCH_STATE_IDLE); +} + +static inline void kpatch_state_updating(void) +{ + WARN_ON(atomic_read(&kpatch_state) != KPATCH_STATE_IDLE); + atomic_set(&kpatch_state, KPATCH_STATE_UPDATING); +} + +/* If state is updating, change it to success or failure and return new state */ +static inline int kpatch_state_finish(int state) +{ + int result; + + WARN_ON(state != KPATCH_STATE_SUCCESS && state != KPATCH_STATE_FAILURE); + result = atomic_cmpxchg(&kpatch_state, KPATCH_STATE_UPDATING, state); + return result == KPATCH_STATE_UPDATING ? state : result; +} + +static struct kpatch_func *kpatch_get_func(unsigned long ip) +{ + struct kpatch_func *f; + + /* Here, we have to use rcu safe hlist because of NMI concurrency */ + hash_for_each_possible_rcu(kpatch_func_hash, f, node, ip) + if (f->old_addr == ip) + return f; + return NULL; +} + +static struct kpatch_func *kpatch_get_prev_func(struct kpatch_func *f, + unsigned long ip) +{ + hlist_for_each_entry_continue_rcu(f, node) + if (f->old_addr == ip) + return f; + return NULL; +} + +static inline bool kpatch_object_linked(struct kpatch_object *object) +{ + return object->mod || !strcmp(object->name, "vmlinux"); +} + +static inline int kpatch_compare_addresses(unsigned long stack_addr, + unsigned long func_addr, + unsigned long func_size, + const char *func_name) +{ + if (stack_addr >= func_addr && stack_addr < func_addr + func_size) { + pr_err("activeness safety check failed for %s\n", func_name); + return -EBUSY; + } + return 0; +} + +static int kpatch_backtrace_address_verify(struct kpatch_module *kpmod, + unsigned long address, + bool replace) +{ + struct kpatch_func *func; + int i; + int ret; + + /* check kpmod funcs */ + do_for_each_linked_func(kpmod, func) { + unsigned long func_addr, func_size; + const char *func_name; + struct kpatch_func *active_func; + + if (func->force) + continue; + + active_func = kpatch_get_func(func->old_addr); + if (!active_func) { + /* patching an unpatched func */ + func_addr = func->old_addr; + func_size = func->old_size; + func_name = func->name; + } else { + /* repatching or unpatching */ + func_addr = active_func->new_addr; + func_size = active_func->new_size; + func_name = active_func->name; + } + + ret = kpatch_compare_addresses(address, func_addr, + func_size, func_name); + if (ret) + return ret; + } while_for_each_linked_func(); + + /* in the replace case, need to check the func hash as well */ + if (replace) { + hash_for_each_rcu(kpatch_func_hash, i, func, node) { + if (func->op != KPATCH_OP_UNPATCH || func->force) + continue; + + ret = kpatch_compare_addresses(address, + func->new_addr, + func->new_size, + func->name); + if (ret) + return ret; + } + } + + return ret; +} + +/* + * Verify activeness safety, i.e. that none of the to-be-patched functions are + * on the stack of any task. + * + * This function is called from stop_machine() context. + */ +static int kpatch_verify_activeness_safety(struct kpatch_module *kpmod, + bool replace) +{ + struct task_struct *g, *t; + int i; + int ret = 0; + + /* Check the stacks of all tasks. */ + do_each_thread(g, t) { + + trace.nr_entries = 0; + save_stack_trace_tsk(t, &trace); + if (trace.nr_entries >= trace.max_entries) { + ret = -EBUSY; + pr_err("more than %u trace entries!\n", + trace.max_entries); + goto out; + } + + for (i = 0; i < trace.nr_entries; i++) { + if (trace.entries[i] == ULONG_MAX) + break; + ret = kpatch_backtrace_address_verify(kpmod, + trace.entries[i], + replace); + if (ret) + goto out; + } + + } while_each_thread(g, t); + +out: + if (ret) { + pr_err("PID: %d Comm: %.20s\n", t->pid, t->comm); + for (i = 0; i < trace.nr_entries; i++) { + if (trace.entries[i] == ULONG_MAX) + break; + pr_err(" [<%pK>] %pB\n", + (void *)trace.entries[i], + (void *)trace.entries[i]); + } + } + + return ret; +} + +static inline int pre_patch_callback(struct kpatch_object *object) +{ + int ret; + + if (kpatch_object_linked(object) && + object->pre_patch_callback) { + ret = (*object->pre_patch_callback)(object); + if (ret) { + object->callbacks_enabled = false; + return ret; + } + } + object->callbacks_enabled = true; + + return 0; +} + +static inline void post_patch_callback(struct kpatch_object *object) +{ + if (kpatch_object_linked(object) && + object->post_patch_callback && + object->callbacks_enabled) + (*object->post_patch_callback)(object); +} + +static inline void pre_unpatch_callback(struct kpatch_object *object) +{ + if (kpatch_object_linked(object) && + object->pre_unpatch_callback && + object->callbacks_enabled) + (*object->pre_unpatch_callback)(object); +} + +static inline void post_unpatch_callback(struct kpatch_object *object) +{ + if (kpatch_object_linked(object) && + object->post_unpatch_callback && + object->callbacks_enabled) + (*object->post_unpatch_callback)(object); +} + +/* Called from stop_machine */ +static int kpatch_apply_patch(void *data) +{ + struct kpatch_apply_patch_args *args = data; + struct kpatch_module *kpmod; + struct kpatch_func *func; + struct hlist_node *tmp; + struct kpatch_object *object; + int ret; + int i; + + kpmod = args->kpmod; + + ret = kpatch_verify_activeness_safety(kpmod, args->replace); + if (ret) { + kpatch_state_finish(KPATCH_STATE_FAILURE); + return ret; + } + + /* tentatively add the new funcs to the global func hash */ + do_for_each_linked_func(kpmod, func) { + hash_add_rcu(kpatch_func_hash, &func->node, func->old_addr); + } while_for_each_linked_func(); + + /* memory barrier between func hash add and state change */ + smp_wmb(); + + /* + * Check if any inconsistent NMI has happened while updating. If not, + * move to success state. + */ + ret = kpatch_state_finish(KPATCH_STATE_SUCCESS); + if (ret == KPATCH_STATE_FAILURE) { + pr_err("NMI activeness safety check failed\n"); + + /* Failed, we have to rollback patching process */ + do_for_each_linked_func(kpmod, func) { + hash_del_rcu(&func->node); + } while_for_each_linked_func(); + + return -EBUSY; + } + + /* + * The new patch has been applied successfully. Remove the functions + * provided by the replaced patches (if any) from hash, to make sure + * they will not be executed anymore. + */ + if (args->replace) { + hash_for_each_safe(kpatch_func_hash, i, tmp, func, node) { + if (func->op != KPATCH_OP_UNPATCH) + continue; + hash_del_rcu(&func->node); + } + } + + /* run any user-defined post-patch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) + post_patch_callback(object); + + return 0; +} + +/* Called from stop_machine */ +static int kpatch_remove_patch(void *data) +{ + struct kpatch_module *kpmod = data; + struct kpatch_func *func; + struct kpatch_object *object; + int ret; + + ret = kpatch_verify_activeness_safety(kpmod, false); + if (ret) { + kpatch_state_finish(KPATCH_STATE_FAILURE); + return ret; + } + + /* run any user-defined pre-unpatch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) + pre_unpatch_callback(object); + + /* Check if any inconsistent NMI has happened while updating */ + ret = kpatch_state_finish(KPATCH_STATE_SUCCESS); + if (ret == KPATCH_STATE_FAILURE) { + ret = -EBUSY; + goto err; + } + + /* Succeeded, remove all updating funcs from hash table */ + do_for_each_linked_func(kpmod, func) { + hash_del_rcu(&func->node); + } while_for_each_linked_func(); + + return 0; + +err: + /* undo pre-unpatch callbacks by calling post-patch counterparts */ + list_for_each_entry(object, &kpmod->objects, list) + post_patch_callback(object); + + return ret; +} + +/* + * This is where the magic happens. Update regs->ip to tell ftrace to return + * to the new function. + * + * If there are multiple patch modules that have registered to patch the same + * function, the last one to register wins, as it'll be first in the hash + * bucket. + */ +static void notrace +kpatch_ftrace_handler(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *fops, struct pt_regs *regs) +{ + struct kpatch_func *func; + int state; + + preempt_disable_notrace(); + + if (likely(!in_nmi())) + func = kpatch_get_func(ip); + else { + /* Checking for NMI inconsistency */ + state = kpatch_state_finish(KPATCH_STATE_FAILURE); + + /* no memory reordering between state and func hash read */ + smp_rmb(); + + func = kpatch_get_func(ip); + + if (likely(state == KPATCH_STATE_IDLE)) + goto done; + + if (state == KPATCH_STATE_SUCCESS) { + /* + * Patching succeeded. If the function was being + * unpatched, roll back to the previous version. + */ + if (func && func->op == KPATCH_OP_UNPATCH) + func = kpatch_get_prev_func(func, ip); + } else { + /* + * Patching failed. If the function was being patched, + * roll back to the previous version. + */ + if (func && func->op == KPATCH_OP_PATCH) + func = kpatch_get_prev_func(func, ip); + } + } +done: + if (func) + regs->ip = func->new_addr + MCOUNT_INSN_SIZE; + + preempt_enable_notrace(); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) +#define FTRACE_OPS_FL_IPMODIFY 0 +#endif + +static struct ftrace_ops kpatch_ftrace_ops __read_mostly = { + .func = kpatch_ftrace_handler, + .flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY, +}; + +static int kpatch_ftrace_add_func(unsigned long ip) +{ + int ret; + + /* check if any other patch modules have also patched this func */ + if (kpatch_get_func(ip)) + return 0; + + ret = ftrace_set_filter_ip(&kpatch_ftrace_ops, ip, 0, 0); + if (ret) { + pr_err("can't set ftrace filter at address 0x%lx\n", ip); + return ret; + } + + if (!kpatch_num_patched) { + ret = register_ftrace_function(&kpatch_ftrace_ops); + if (ret) { + pr_err("can't register ftrace handler\n"); + ftrace_set_filter_ip(&kpatch_ftrace_ops, ip, 1, 0); + return ret; + } + } + kpatch_num_patched++; + + return 0; +} + +static int kpatch_ftrace_remove_func(unsigned long ip) +{ + int ret; + + /* check if any other patch modules have also patched this func */ + if (kpatch_get_func(ip)) + return 0; + + if (kpatch_num_patched == 1) { + ret = unregister_ftrace_function(&kpatch_ftrace_ops); + if (ret) { + pr_err("can't unregister ftrace handler\n"); + return ret; + } + } + kpatch_num_patched--; + + ret = ftrace_set_filter_ip(&kpatch_ftrace_ops, ip, 1, 0); + if (ret) { + pr_err("can't remove ftrace filter at address 0x%lx\n", ip); + return ret; + } + + return 0; +} + +static int kpatch_kallsyms_callback(void *data, const char *name, + struct module *mod, + unsigned long addr) +{ + struct kpatch_kallsyms_args *args = data; + bool vmlinux = !strcmp(args->objname, "vmlinux"); + + if ((mod && vmlinux) || (!mod && !vmlinux)) + return 0; + + if (strcmp(args->name, name)) + return 0; + + if (!vmlinux && strcmp(args->objname, mod->name)) + return 0; + + args->addr = addr; + args->count++; + + /* + * Finish the search when the symbol is found for the desired position + * or the position is not defined for a non-unique symbol. + */ + if ((args->pos && (args->count == args->pos)) || + (!args->pos && (args->count > 1))) { + return 1; + } + + return 0; +} + +static int kpatch_find_object_symbol(const char *objname, const char *name, + unsigned long sympos, unsigned long *addr) +{ + struct kpatch_kallsyms_args args = { + .objname = objname, + .name = name, + .addr = 0, + .count = 0, + .pos = sympos, + }; + + mutex_lock(&module_mutex); + kallsyms_on_each_symbol(kpatch_kallsyms_callback, &args); + mutex_unlock(&module_mutex); + + /* + * Ensure an address was found. If sympos is 0, ensure symbol is unique; + * otherwise ensure the symbol position count matches sympos. + */ + if (args.addr == 0) + pr_err("symbol '%s' not found in symbol table\n", name); + else if (args.count > 1 && sympos == 0) { + pr_err("unresolvable ambiguity for symbol '%s' in object '%s'\n", + name, objname); + } else if (sympos != args.count && sympos > 0) { + pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n", + sympos, name, objname); + } else { + *addr = args.addr; + return 0; + } + + *addr = 0; + return -EINVAL; +} + +/* + * External symbols are located outside the parent object (where the parent + * object is either vmlinux or the kmod being patched). + */ +static int kpatch_find_external_symbol(const char *objname, const char *name, + unsigned long sympos, unsigned long *addr) + +{ + const struct kernel_symbol *sym; + + /* first, check if it's an exported symbol */ + preempt_disable(); + sym = find_symbol(name, NULL, NULL, true, true); + preempt_enable(); + if (sym) { +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS + *addr = (unsigned long)offset_to_ptr(&sym->value_offset); +#else + *addr = sym->value; +#endif + return 0; + } + + /* otherwise check if it's in another .o within the patch module */ + return kpatch_find_object_symbol(objname, name, sympos, addr); +} + +static int kpatch_write_relocations(struct kpatch_module *kpmod, + struct kpatch_object *object) +{ + int ret, size, readonly = 0, numpages; + struct kpatch_dynrela *dynrela; + u64 loc, val; +#if (( LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) ) || \ + ( LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) && \ + UTS_UBUNTU_RELEASE_ABI >= 7 ) \ + ) + unsigned long core = (unsigned long)kpmod->mod->core_layout.base; + unsigned long core_size = kpmod->mod->core_layout.size; +#else + unsigned long core = (unsigned long)kpmod->mod->module_core; + unsigned long core_size = kpmod->mod->core_size; +#endif + + list_for_each_entry(dynrela, &object->dynrelas, list) { + if (dynrela->external) + ret = kpatch_find_external_symbol(kpmod->mod->name, + dynrela->name, + dynrela->sympos, + &dynrela->src); + else + ret = kpatch_find_object_symbol(object->name, + dynrela->name, + dynrela->sympos, + &dynrela->src); + if (ret) { + pr_err("unable to find symbol '%s'\n", dynrela->name); + return ret; + } + + switch (dynrela->type) { + case R_X86_64_NONE: + continue; + case R_X86_64_PC32: + case R_X86_64_PLT32: + loc = dynrela->dest; + val = (u32)(dynrela->src + dynrela->addend - + dynrela->dest); + size = 4; + break; + case R_X86_64_32S: + loc = dynrela->dest; + val = (s32)dynrela->src + dynrela->addend; + size = 4; + break; + case R_X86_64_64: + loc = dynrela->dest; + val = dynrela->src + dynrela->addend; + size = 8; + break; + default: + pr_err("unsupported rela type %ld for source %s (0x%lx <- 0x%lx)\n", + dynrela->type, dynrela->name, dynrela->dest, + dynrela->src); + return -EINVAL; + } + + if (loc < core || loc >= core + core_size) { + pr_err("bad dynrela location 0x%llx for symbol %s\n", + loc, dynrela->name); + return -EINVAL; + } + + /* + * Skip it if the instruction to be relocated has been + * changed already (paravirt or alternatives may do this). + */ + if (memchr_inv((void *)loc, 0, size)) { + pr_notice("Skipped dynrela for %s (0x%lx <- 0x%lx): the instruction has been changed already.\n", + dynrela->name, dynrela->dest, dynrela->src); + pr_notice_once( +"This is not necessarily a bug but it may indicate in some cases " +"that the binary patch does not handle paravirt operations, alternatives or the like properly.\n"); + continue; + } + +#if defined(CONFIG_DEBUG_SET_MODULE_RONX) || defined(CONFIG_ARCH_HAS_SET_MEMORY) +#if (( LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) ) || \ + ( LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) && \ + UTS_UBUNTU_RELEASE_ABI >= 7 ) \ + ) + readonly = (loc < core + kpmod->mod->core_layout.ro_size); +#else + readonly = (loc < core + kpmod->mod->core_ro_size); +#endif +#endif + + numpages = (PAGE_SIZE - (loc & ~PAGE_MASK) >= size) ? 1 : 2; + + if (readonly) + kpatch_set_memory_rw(loc & PAGE_MASK, numpages); + + ret = probe_kernel_write((void *)loc, &val, size); + + if (readonly) + kpatch_set_memory_ro(loc & PAGE_MASK, numpages); + + if (ret) { + pr_err("write to 0x%llx failed for symbol %s\n", + loc, dynrela->name); + return ret; + } + } + + return 0; +} + +static int kpatch_unlink_object(struct kpatch_object *object) +{ + struct kpatch_func *func; + int ret; + + list_for_each_entry(func, &object->funcs, list) { + if (!func->old_addr) + continue; + ret = kpatch_ftrace_remove_func(func->old_addr); + if (ret) { + WARN(1, "can't unregister ftrace for address 0x%lx\n", + func->old_addr); + return ret; + } + } + + if (object->mod) { + module_put(object->mod); + object->mod = NULL; + } + + return 0; +} + +/* + * Link to a to-be-patched object in preparation for patching it. + * + * - Find the object module + * - Write patch module relocations which reference the object + * - Calculate the patched functions' addresses + * - Register them with ftrace + */ +static int kpatch_link_object(struct kpatch_module *kpmod, + struct kpatch_object *object) +{ + struct module *mod = NULL; + struct kpatch_func *func, *func_err = NULL; + int ret; + bool vmlinux = !strcmp(object->name, "vmlinux"); + + if (!vmlinux) { + mutex_lock(&module_mutex); + mod = find_module(object->name); + if (!mod) { + /* + * The module hasn't been loaded yet. We can patch it + * later in kpatch_module_notify(). + */ + mutex_unlock(&module_mutex); + return 0; + } + + /* should never fail because we have the mutex */ + WARN_ON(!try_module_get(mod)); + mutex_unlock(&module_mutex); + object->mod = mod; + } + + ret = kpatch_write_relocations(kpmod, object); + if (ret) + goto err_put; + + list_for_each_entry(func, &object->funcs, list) { + + /* lookup the old location */ + ret = kpatch_find_object_symbol(object->name, + func->name, + func->sympos, + &func->old_addr); + if (ret) { + func_err = func; + goto err_ftrace; + } + + /* add to ftrace filter and register handler if needed */ + ret = kpatch_ftrace_add_func(func->old_addr); + if (ret) { + func_err = func; + goto err_ftrace; + } + } + + return 0; + +err_ftrace: + list_for_each_entry(func, &object->funcs, list) { + if (func == func_err) + break; + WARN_ON(kpatch_ftrace_remove_func(func->old_addr)); + } +err_put: + if (!vmlinux) + module_put(mod); + return ret; +} + +static int kpatch_module_notify_coming(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct module *mod = data; + struct kpatch_module *kpmod; + struct kpatch_object *object; + struct kpatch_func *func; + int ret = 0; + bool found = false; + + if (action != MODULE_STATE_COMING) + return 0; + + down(&kpatch_mutex); + + list_for_each_entry(kpmod, &kpmod_list, list) { + list_for_each_entry(object, &kpmod->objects, list) { + if (kpatch_object_linked(object)) + continue; + if (!strcmp(object->name, mod->name)) { + found = true; + goto done; + } + } + } +done: + if (!found) + goto out; + + ret = kpatch_link_object(kpmod, object); + if (ret) + goto out; + + BUG_ON(!object->mod); + + pr_notice("patching newly loaded module '%s'\n", object->name); + + /* run user-defined pre-patch callback */ + ret = pre_patch_callback(object); + if (ret) { + pr_err("pre-patch callback failed!\n"); + goto out; /* and WARN */ + } + + /* add to the global func hash */ + list_for_each_entry(func, &object->funcs, list) + hash_add_rcu(kpatch_func_hash, &func->node, func->old_addr); + + /* run user-defined post-patch callback */ + post_patch_callback(object); +out: + up(&kpatch_mutex); + + /* no way to stop the module load on error */ + WARN(ret, "error (%d) patching newly loaded module '%s'\n", ret, + object->name); + + return 0; +} + +static int kpatch_module_notify_going(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct module *mod = data; + struct kpatch_module *kpmod; + struct kpatch_object *object; + struct kpatch_func *func; + bool found = false; + + if (action != MODULE_STATE_GOING) + return 0; + + down(&kpatch_mutex); + + list_for_each_entry(kpmod, &kpmod_list, list) { + list_for_each_entry(object, &kpmod->objects, list) { + if (!kpatch_object_linked(object)) + continue; + if (!strcmp(object->name, mod->name)) { + found = true; + goto done; + } + } + } +done: + if (!found) + goto out; + + /* run user-defined pre-unpatch callback */ + pre_unpatch_callback(object); + + /* remove from the global func hash */ + list_for_each_entry(func, &object->funcs, list) + hash_del_rcu(&func->node); + + /* run user-defined pre-unpatch callback */ + post_unpatch_callback(object); + + kpatch_unlink_object(object); + +out: + up(&kpatch_mutex); + + return 0; +} + +/* + * Remove the obsolete functions from the ftrace filter. + * Return 1 if one or more of such functions have 'force' flag set, + * 0 otherwise. + */ +static int kpatch_ftrace_remove_unpatched_funcs(void) +{ + struct kpatch_module *kpmod; + struct kpatch_func *func; + int force = 0; + + list_for_each_entry(kpmod, &kpmod_list, list) { + do_for_each_linked_func(kpmod, func) { + if (func->op != KPATCH_OP_UNPATCH) + continue; + if (func->force) + force = 1; + WARN_ON(kpatch_ftrace_remove_func(func->old_addr)); + } while_for_each_linked_func(); + } + + return force; +} + +int kpatch_register(struct kpatch_module *kpmod, bool replace) +{ + int ret, i; + struct kpatch_object *object, *object_err = NULL; + struct kpatch_func *func; + + struct kpatch_apply_patch_args args = { + .kpmod = kpmod, + .replace = replace, + }; + + if (!kpmod->mod || list_empty(&kpmod->objects)) + return -EINVAL; + + down(&kpatch_mutex); + + if (kpmod->enabled) { + ret = -EINVAL; + goto err_up; + } + + list_add_tail(&kpmod->list, &kpmod_list); + + if (!try_module_get(kpmod->mod)) { + ret = -ENODEV; + goto err_list; + } + + list_for_each_entry(object, &kpmod->objects, list) { + + ret = kpatch_link_object(kpmod, object); + if (ret) { + object_err = object; + goto err_unlink; + } + + if (!kpatch_object_linked(object)) { + pr_notice("delaying patch of unloaded module '%s'\n", + object->name); + continue; + } + + if (strcmp(object->name, "vmlinux")) + pr_notice("patching module '%s'\n", object->name); + + list_for_each_entry(func, &object->funcs, list) + func->op = KPATCH_OP_PATCH; + } + + if (replace) + hash_for_each_rcu(kpatch_func_hash, i, func, node) + func->op = KPATCH_OP_UNPATCH; + + /* memory barrier between func hash and state write */ + smp_wmb(); + + kpatch_state_updating(); + + /* run any user-defined pre-patch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) { + ret = pre_patch_callback(object); + if(ret){ + pr_err("pre-patch callback failed!\n"); + kpatch_state_finish(KPATCH_STATE_FAILURE); + break; + } + } + + /* if pre_patch_callback succeed. */ + if (!ret) { + /* + * Idle the CPUs, verify activeness safety, and atomically make the new + * functions visible to the ftrace handler. + */ + ret = stop_machine(kpatch_apply_patch, &args, NULL); + } + + /* if pre_patch_callback or stop_machine failed */ + if (ret) { + list_for_each_entry(object, &kpmod->objects, list) + post_unpatch_callback(object); + } + + /* + * For the replace case, remove any obsolete funcs from the ftrace + * filter, and disable the owning patch module so that it can be + * removed. + */ + if (!ret && replace) { + struct kpatch_module *kpmod2, *safe; + int force; + + force = kpatch_ftrace_remove_unpatched_funcs(); + + list_for_each_entry_safe(kpmod2, safe, &kpmod_list, list) { + if (kpmod == kpmod2) + continue; + + kpmod2->enabled = false; + pr_notice("unloaded patch module '%s'\n", + kpmod2->mod->name); + + /* + * Don't allow modules with forced functions to be + * removed because they might still be in use. + */ + if (!force) + module_put(kpmod2->mod); + + list_del(&kpmod2->list); + } + } + + + /* memory barrier between func hash and state write */ + smp_wmb(); + + /* NMI handlers can return to normal now */ + kpatch_state_idle(); + + /* + * Wait for all existing NMI handlers to complete so that they don't + * see any changes to funcs or funcs->op that might occur after this + * point. + * + * Any NMI handlers starting after this point will see the IDLE state. + */ + synchronize_rcu(); + + if (ret) + goto err_ops; + + do_for_each_linked_func(kpmod, func) { + func->op = KPATCH_OP_NONE; + } while_for_each_linked_func(); + +/* HAS_MODULE_TAINT - upstream 2992ef29ae01 "livepatch/module: make TAINT_LIVEPATCH module-specific" */ +/* HAS_MODULE_TAINT_LONG - upstream 7fd8329ba502 "taint/module: Clean up global and module taint flags handling" */ +#ifdef RHEL_RELEASE_CODE +# if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 4) +# define HAS_MODULE_TAINT +# endif +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +# define HAS_MODULE_TAINT_LONG +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) +# define HAS_MODULE_TAINT +#endif + +#ifdef TAINT_LIVEPATCH + pr_notice_once("tainting kernel with TAINT_LIVEPATCH\n"); + add_taint(TAINT_LIVEPATCH, LOCKDEP_STILL_OK); +# ifdef HAS_MODULE_TAINT_LONG + set_bit(TAINT_LIVEPATCH, &kpmod->mod->taints); +# elif defined(HAS_MODULE_TAINT) + kpmod->mod->taints |= (1 << TAINT_LIVEPATCH); +# endif +#else + pr_notice_once("tainting kernel with TAINT_USER\n"); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); +#endif + + pr_notice("loaded patch module '%s'\n", kpmod->mod->name); + + kpmod->enabled = true; + + up(&kpatch_mutex); + return 0; + +err_ops: + if (replace) + hash_for_each_rcu(kpatch_func_hash, i, func, node) + func->op = KPATCH_OP_NONE; +err_unlink: + list_for_each_entry(object, &kpmod->objects, list) { + if (object == object_err) + break; + if (!kpatch_object_linked(object)) + continue; + WARN_ON(kpatch_unlink_object(object)); + } + module_put(kpmod->mod); +err_list: + list_del(&kpmod->list); +err_up: + up(&kpatch_mutex); + return ret; +} +EXPORT_SYMBOL(kpatch_register); + +int kpatch_unregister(struct kpatch_module *kpmod) +{ + struct kpatch_object *object; + struct kpatch_func *func; + int ret, force = 0; + + down(&kpatch_mutex); + + if (!kpmod->enabled) { + ret = -EINVAL; + goto out; + } + + do_for_each_linked_func(kpmod, func) { + func->op = KPATCH_OP_UNPATCH; + if (func->force) + force = 1; + } while_for_each_linked_func(); + + /* memory barrier between func hash and state write */ + smp_wmb(); + + kpatch_state_updating(); + + ret = stop_machine(kpatch_remove_patch, kpmod, NULL); + + if (!ret) { + /* run any user-defined post-unpatch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) + post_unpatch_callback(object); + } + /* NMI handlers can return to normal now */ + kpatch_state_idle(); + + /* + * Wait for all existing NMI handlers to complete so that they don't + * see any changes to funcs or funcs->op that might occur after this + * point. + * + * Any NMI handlers starting after this point will see the IDLE state. + */ + synchronize_rcu(); + + if (ret) { + do_for_each_linked_func(kpmod, func) { + func->op = KPATCH_OP_NONE; + } while_for_each_linked_func(); + goto out; + } + + list_for_each_entry(object, &kpmod->objects, list) { + if (!kpatch_object_linked(object)) + continue; + ret = kpatch_unlink_object(object); + if (ret) + goto out; + } + + pr_notice("unloaded patch module '%s'\n", kpmod->mod->name); + + kpmod->enabled = false; + + /* + * Don't allow modules with forced functions to be removed because they + * might still be in use. + */ + if (!force) + module_put(kpmod->mod); + + list_del(&kpmod->list); + +out: + up(&kpatch_mutex); + return ret; +} +EXPORT_SYMBOL(kpatch_unregister); + + +static struct notifier_block kpatch_module_nb_coming = { + .notifier_call = kpatch_module_notify_coming, + .priority = INT_MIN, /* called last */ +}; +static struct notifier_block kpatch_module_nb_going = { + .notifier_call = kpatch_module_notify_going, + .priority = INT_MAX, /* called first */ +}; + +static int kpatch_init(void) +{ + int ret; + + kpatch_set_memory_rw = (void *)kallsyms_lookup_name("set_memory_rw"); + if (!kpatch_set_memory_rw) { + pr_err("can't find set_memory_rw symbol\n"); + return -ENXIO; + } + + kpatch_set_memory_ro = (void *)kallsyms_lookup_name("set_memory_ro"); + if (!kpatch_set_memory_ro) { + pr_err("can't find set_memory_ro symbol\n"); + return -ENXIO; + } + + kpatch_root_kobj = kobject_create_and_add("kpatch", kernel_kobj); + if (!kpatch_root_kobj) + return -ENOMEM; + + ret = register_module_notifier(&kpatch_module_nb_coming); + if (ret) + goto err_root_kobj; + ret = register_module_notifier(&kpatch_module_nb_going); + if (ret) + goto err_unregister_coming; + + return 0; + +err_unregister_coming: + WARN_ON(unregister_module_notifier(&kpatch_module_nb_coming)); +err_root_kobj: + kobject_put(kpatch_root_kobj); + return ret; +} + +static void kpatch_exit(void) +{ + rcu_barrier(); + + WARN_ON(kpatch_num_patched != 0); + WARN_ON(unregister_module_notifier(&kpatch_module_nb_coming)); + WARN_ON(unregister_module_notifier(&kpatch_module_nb_going)); + kobject_put(kpatch_root_kobj); +} + +module_init(kpatch_init); +module_exit(kpatch_exit); +MODULE_LICENSE("GPL"); diff --git a/kmod/core/kpatch.h b/kmod/core/kpatch.h new file mode 100644 index 0000000..36f021f --- /dev/null +++ b/kmod/core/kpatch.h @@ -0,0 +1,102 @@ +/* + * kpatch.h + * + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2013-2014 Josh Poimboeuf + * + * 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 . + * + * Contains the API for the core kpatch module used by the patch modules + */ + +#ifndef _KPATCH_H_ +#define _KPATCH_H_ + +#include +#include + +enum kpatch_op { + KPATCH_OP_NONE, + KPATCH_OP_PATCH, + KPATCH_OP_UNPATCH, +}; + +struct kpatch_func { + /* public */ + unsigned long new_addr; + unsigned long new_size; + unsigned long old_addr; + unsigned long old_size; + unsigned long sympos; + const char *name; + struct list_head list; + int force; + + /* private */ + struct hlist_node node; + enum kpatch_op op; + struct kobject kobj; +}; + +struct kpatch_dynrela { + unsigned long dest; + unsigned long src; + unsigned long type; + unsigned long sympos; + const char *name; + int addend; + int external; + struct list_head list; +}; + +struct kpatch_object { + struct list_head list; + const char *name; + struct list_head funcs; + struct list_head dynrelas; + + int (*pre_patch_callback)(struct kpatch_object *); + void (*post_patch_callback)(struct kpatch_object *); + void (*pre_unpatch_callback)(struct kpatch_object *); + void (*post_unpatch_callback)(struct kpatch_object *); + bool callbacks_enabled; + + /* private */ + struct module *mod; + struct kobject kobj; +}; + +struct kpatch_module { + /* public */ + struct module *mod; + struct list_head objects; + + /* public read-only */ + bool enabled; + + /* private */ + struct list_head list; + struct kobject kobj; +}; + +extern struct kobject *kpatch_root_kobj; + +extern int kpatch_register(struct kpatch_module *kpmod, bool replace); +extern int kpatch_unregister(struct kpatch_module *kpmod); + +extern void *kpatch_shadow_alloc(void *obj, char *var, size_t size, gfp_t gfp); +extern void kpatch_shadow_free(void *obj, char *var); +extern void *kpatch_shadow_get(void *obj, char *var); + +#endif /* _KPATCH_H_ */ diff --git a/kmod/core/shadow.c b/kmod/core/shadow.c new file mode 100644 index 0000000..627928a --- /dev/null +++ b/kmod/core/shadow.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2014 Josh Poimboeuf + * 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 . + */ + +/* + * kpatch shadow variables + * + * These functions can be used to add new "shadow" fields to existing data + * structures. For example, to allocate a "newpid" variable associated with an + * instance of task_struct, and assign it a value of 1000: + * + * struct task_struct *tsk = current; + * int *newpid; + * newpid = kpatch_shadow_alloc(tsk, "newpid", sizeof(int), GFP_KERNEL); + * if (newpid) + * *newpid = 1000; + * + * To retrieve a pointer to the variable: + * + * struct task_struct *tsk = current; + * int *newpid; + * newpid = kpatch_shadow_get(tsk, "newpid"); + * if (newpid) + * printk("task newpid = %d\n", *newpid); // prints "task newpid = 1000" + * + * To free it: + * + * kpatch_shadow_free(tsk, "newpid"); + */ + +#include +#include +#include "kpatch.h" + +static DEFINE_HASHTABLE(kpatch_shadow_hash, 12); +static DEFINE_SPINLOCK(kpatch_shadow_lock); + +struct kpatch_shadow { + struct hlist_node node; + struct rcu_head rcu_head; + void *obj; + union { + char *var; /* assumed to be 4-byte aligned */ + unsigned long flags; + }; + void *data; +}; + +#define SHADOW_FLAG_INPLACE 0x1 +#define SHADOW_FLAG_RESERVED0 0x2 /* reserved for future use */ + +#define SHADOW_FLAG_MASK 0x3 +#define SHADOW_PTR_MASK (~(SHADOW_FLAG_MASK)) + +static inline void shadow_set_inplace(struct kpatch_shadow *shadow) +{ + shadow->flags |= SHADOW_FLAG_INPLACE; +} + +static inline int shadow_is_inplace(struct kpatch_shadow *shadow) +{ + return shadow->flags & SHADOW_FLAG_INPLACE; +} + +static inline char *shadow_var(struct kpatch_shadow *shadow) +{ + return (char *)((unsigned long)shadow->var & SHADOW_PTR_MASK); +} + +void *kpatch_shadow_alloc(void *obj, char *var, size_t size, gfp_t gfp) +{ + unsigned long flags; + struct kpatch_shadow *shadow; + + shadow = kmalloc(sizeof(*shadow), gfp); + if (!shadow) + return NULL; + + shadow->obj = obj; + + shadow->var = kstrdup(var, gfp); + if (!shadow->var) { + kfree(shadow); + return NULL; + } + + if (size <= sizeof(shadow->data)) { + shadow->data = &shadow->data; + shadow_set_inplace(shadow); + } else { + shadow->data = kmalloc(size, gfp); + if (!shadow->data) { + kfree(shadow->var); + kfree(shadow); + return NULL; + } + } + + spin_lock_irqsave(&kpatch_shadow_lock, flags); + hash_add_rcu(kpatch_shadow_hash, &shadow->node, (unsigned long)obj); + spin_unlock_irqrestore(&kpatch_shadow_lock, flags); + + return shadow->data; +} +EXPORT_SYMBOL_GPL(kpatch_shadow_alloc); + +static void kpatch_shadow_rcu_free(struct rcu_head *head) +{ + struct kpatch_shadow *shadow; + + shadow = container_of(head, struct kpatch_shadow, rcu_head); + + if (!shadow_is_inplace(shadow)) + kfree(shadow->data); + kfree(shadow_var(shadow)); + kfree(shadow); +} + +void kpatch_shadow_free(void *obj, char *var) +{ + unsigned long flags; + struct kpatch_shadow *shadow; + + spin_lock_irqsave(&kpatch_shadow_lock, flags); + + hash_for_each_possible(kpatch_shadow_hash, shadow, node, + (unsigned long)obj) { + if (shadow->obj == obj && !strcmp(shadow_var(shadow), var)) { + hash_del_rcu(&shadow->node); + spin_unlock_irqrestore(&kpatch_shadow_lock, flags); + call_rcu(&shadow->rcu_head, kpatch_shadow_rcu_free); + return; + } + } + + spin_unlock_irqrestore(&kpatch_shadow_lock, flags); +} +EXPORT_SYMBOL_GPL(kpatch_shadow_free); + +void *kpatch_shadow_get(void *obj, char *var) +{ + struct kpatch_shadow *shadow; + + rcu_read_lock(); + + hash_for_each_possible_rcu(kpatch_shadow_hash, shadow, node, + (unsigned long)obj) { + if (shadow->obj == obj && !strcmp(shadow_var(shadow), var)) { + rcu_read_unlock(); + if (shadow_is_inplace(shadow)) + return &(shadow->data); + + return shadow->data; + } + } + + rcu_read_unlock(); + + return NULL; +} +EXPORT_SYMBOL_GPL(kpatch_shadow_get); diff --git a/kmod/patch/Makefile b/kmod/patch/Makefile new file mode 100644 index 0000000..bb4d49c --- /dev/null +++ b/kmod/patch/Makefile @@ -0,0 +1,26 @@ +KPATCH_BUILD ?= /lib/modules/$(shell uname -r)/build +KPATCH_MAKE = $(MAKE) -C $(KPATCH_BUILD) M=$(PWD) CFLAGS_MODULE='$(CFLAGS_MODULE)' +LDFLAGS += $(KPATCH_LDFLAGS) + +# object files that this Makefile can (re)build on its own +BUILDABLE_OBJS=$(filter-out output.o, $(wildcard *.o)) + +obj-m += $(KPATCH_NAME).o +ldflags-y += -T $(src)/kpatch.lds +targets += kpatch.lds + +$(KPATCH_NAME)-objs += patch-hook.o output.o + +all: $(KPATCH_NAME).ko + +$(KPATCH_NAME).ko: + $(KPATCH_MAKE) + +$(obj)/$(KPATCH_NAME).o: $(src)/kpatch.lds + +patch-hook.o: patch-hook.c kpatch-patch-hook.c livepatch-patch-hook.c + $(KPATCH_MAKE) patch-hook.o + +clean: + $(RM) -Rf .*.o.cmd .*.ko.cmd .tmp_versions $(BUILDABLE_OBJS) *.ko *.mod.c \ + Module.symvers diff --git a/kmod/patch/kpatch-macros.h b/kmod/patch/kpatch-macros.h new file mode 100644 index 0000000..8e09702 --- /dev/null +++ b/kmod/patch/kpatch-macros.h @@ -0,0 +1,144 @@ +#ifndef __KPATCH_MACROS_H_ +#define __KPATCH_MACROS_H_ + +#include +#include +#include +#include "kpatch-syscall.h" + +/* upstream 33def8498fdd "treewide: Convert macro and uses of __section(foo) to __section("foo")" */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) +# define __kpatch_section(section) __section(section) +#else +# define __kpatch_section(section) __section(#section) +#endif + +/* + * KPATCH_IGNORE_SECTION macro + * + * This macro is for ignoring sections that may change as a side effect of + * another change or might be a non-bundlable section; that is one that does + * not honor -ffunction-section and create a one-to-one relation from function + * symbol to section. + */ +#define KPATCH_IGNORE_SECTION(_sec) \ + char *__UNIQUE_ID(kpatch_ignore_section_) __kpatch_section(.kpatch.ignore.sections) = _sec; + +/* + * KPATCH_IGNORE_FUNCTION macro + * + * This macro is for ignoring functions that may change as a side effect of a + * change in another function. The WARN class of macros, for example, embed + * the line number in an instruction, which will cause the function to be + * detected as changed when, in fact, there has been no functional change. + */ +#define KPATCH_IGNORE_FUNCTION(_fn) \ + void *__kpatch_ignore_func_##_fn __kpatch_section(.kpatch.ignore.functions) = _fn; + + +/* Support for livepatch callbacks */ +#if IS_ENABLED(CONFIG_LIVEPATCH) +# ifdef RHEL_RELEASE_CODE +# if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 5) +# define HAS_LIVEPATCH_CALLBACKS +# endif +# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +# define HAS_LIVEPATCH_CALLBACKS +# endif +#endif + +#ifdef HAS_LIVEPATCH_CALLBACKS +# include +typedef struct klp_object patch_object; +#else +# include "kpatch.h" +typedef struct kpatch_object patch_object; +#endif /* HAS_LIVEPATCH_CALLBACKS */ + +typedef int (*kpatch_pre_patch_call_t)(patch_object *obj); +typedef void (*kpatch_post_patch_call_t)(patch_object *obj); +typedef void (*kpatch_pre_unpatch_call_t)(patch_object *obj); +typedef void (*kpatch_post_unpatch_call_t)(patch_object *obj); + +struct kpatch_pre_patch_callback { + kpatch_pre_patch_call_t fn; + char *objname; /* filled in by create-diff-object */ +}; + +struct kpatch_post_patch_callback { + kpatch_post_patch_call_t fn; + char *objname; /* filled in by create-diff-object */ +}; + +struct kpatch_pre_unpatch_callback { + kpatch_pre_unpatch_call_t fn; + char *objname; /* filled in by create-diff-object */ +}; + +struct kpatch_post_unpatch_callback { + kpatch_post_unpatch_call_t fn; + char *objname; /* filled in by create-diff-object */ +}; + + +#define KPATCH_PRE_PATCH_CALLBACK(_fn) \ + static inline kpatch_pre_patch_call_t __pre_patchtest(void) { return _fn; } \ + static struct kpatch_pre_patch_callback kpatch_pre_patch_data __kpatch_section(.kpatch.callbacks.pre_patch) __used = { \ + .fn = _fn, \ + .objname = NULL \ + }; +#define KPATCH_POST_PATCH_CALLBACK(_fn) \ + static inline kpatch_post_patch_call_t __post_patchtest(void) { return _fn; } \ + static struct kpatch_post_patch_callback kpatch_post_patch_data __kpatch_section(.kpatch.callbacks.post_patch) __used = { \ + .fn = _fn, \ + .objname = NULL \ + }; +#define KPATCH_PRE_UNPATCH_CALLBACK(_fn) \ + static inline kpatch_pre_unpatch_call_t __pre_unpatchtest(void) { return _fn; } \ + static struct kpatch_pre_unpatch_callback kpatch_pre_unpatch_data __kpatch_section(.kpatch.callbacks.pre_unpatch) __used = { \ + .fn = _fn, \ + .objname = NULL \ + }; +#define KPATCH_POST_UNPATCH_CALLBACK(_fn) \ + static inline kpatch_post_unpatch_call_t __post_unpatchtest(void) { return _fn; } \ + static struct kpatch_post_unpatch_callback kpatch_post_unpatch_data __kpatch_section(.kpatch.callbacks.post_unpatch) __used = { \ + .fn = _fn, \ + .objname = NULL \ + }; + +/* + * KPATCH_FORCE_UNSAFE macro + * + * USE WITH EXTREME CAUTION! + * + * Allows patch authors to bypass the activeness safety check at patch load + * time. Do this ONLY IF 1) the patch application will always/likely fail due + * to the function being on the stack of at least one thread at all times and + * 2) it is safe for both the original and patched versions of the function to + * run concurrently. + */ +#define KPATCH_FORCE_UNSAFE(_fn) \ + void *__kpatch_force_func_##_fn __kpatch_section(.kpatch.force) = _fn; + +/* + * KPATCH_PRINTK macro + * + * Use this instead of calling printk to avoid unwanted compiler optimizations + * which cause kpatch-build errors. + * + * The printk function is annotated with the __cold attribute, which tells gcc + * that the function is unlikely to be called. A side effect of this is that + * code paths containing calls to printk might also be marked cold, leading to + * other functions called in those code paths getting moved into .text.unlikely + * or being uninlined. + * + * This macro places printk in its own code path so as not to make the + * surrounding code path cold. + */ +#define KPATCH_PRINTK(_fmt, ...) \ +({ \ + if (jiffies) \ + printk(_fmt, ## __VA_ARGS__); \ +}) + +#endif /* __KPATCH_MACROS_H_ */ diff --git a/kmod/patch/kpatch-patch-hook.c b/kmod/patch/kpatch-patch-hook.c new file mode 100644 index 0000000..92f101d --- /dev/null +++ b/kmod/patch/kpatch-patch-hook.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2013-2014 Josh Poimboeuf + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "kpatch.h" +#include "kpatch-patch.h" + +static bool replace; +module_param(replace, bool, S_IRUGO); +MODULE_PARM_DESC(replace, "replace all previously loaded patch modules"); + +extern struct kpatch_patch_func __kpatch_funcs[], __kpatch_funcs_end[]; +extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[]; +extern struct kpatch_pre_patch_callback __kpatch_callbacks_pre_patch[], __kpatch_callbacks_pre_patch_end[]; +extern struct kpatch_post_patch_callback __kpatch_callbacks_post_patch[], __kpatch_callbacks_post_patch_end[]; +extern struct kpatch_pre_unpatch_callback __kpatch_callbacks_pre_unpatch[], __kpatch_callbacks_pre_unpatch_end[]; +extern struct kpatch_post_unpatch_callback __kpatch_callbacks_post_unpatch[], __kpatch_callbacks_post_unpatch_end[]; +extern unsigned long __kpatch_force_funcs[], __kpatch_force_funcs_end[]; +extern char __kpatch_checksum[]; + +static struct kpatch_module kpmod; + +static ssize_t patch_enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", kpmod.enabled); +} + +static ssize_t patch_enabled_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, + size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + val = !!val; + + if (val) + ret = kpatch_register(&kpmod, replace); + else + ret = kpatch_unregister(&kpmod); + + if (ret) + return ret; + + return count; +} + +static ssize_t patch_checksum_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", __kpatch_checksum); +} + +static struct kobj_attribute patch_enabled_attr = + __ATTR(enabled, 0644, patch_enabled_show, patch_enabled_store); +static struct kobj_attribute patch_checksum_attr = + __ATTR(checksum, 0444, patch_checksum_show, NULL); + +static struct attribute *patch_attrs[] = { + &patch_enabled_attr.attr, + &patch_checksum_attr.attr, + NULL, +}; + +static void patch_kobj_free(struct kobject *kobj) +{ +} + +static struct kobj_type patch_ktype = { + .release = patch_kobj_free, + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = patch_attrs, +}; + +static ssize_t patch_func_old_addr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct kpatch_func *func = + container_of(kobj, struct kpatch_func, kobj); + + return sprintf(buf, "0x%lx\n", func->old_addr); +} + +static ssize_t patch_func_new_addr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct kpatch_func *func = + container_of(kobj, struct kpatch_func, kobj); + + return sprintf(buf, "0x%lx\n", func->new_addr); +} + +static struct kobj_attribute patch_old_addr_attr = + __ATTR(old_addr, S_IRUSR, patch_func_old_addr_show, NULL); + +static struct kobj_attribute patch_new_addr_attr = + __ATTR(new_addr, S_IRUSR, patch_func_new_addr_show, NULL); + +static struct attribute *patch_func_kobj_attrs[] = { + &patch_old_addr_attr.attr, + &patch_new_addr_attr.attr, + NULL, +}; + +static ssize_t patch_func_kobj_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kobj_attribute *func_attr = + container_of(attr, struct kobj_attribute, attr); + + return func_attr->show(kobj, func_attr, buf); +} + +static const struct sysfs_ops patch_func_sysfs_ops = { + .show = patch_func_kobj_show, +}; + +static void patch_func_kobj_free(struct kobject *kobj) +{ + struct kpatch_func *func = + container_of(kobj, struct kpatch_func, kobj); + kfree(func); +} + +static struct kobj_type patch_func_ktype = { + .release = patch_func_kobj_free, + .sysfs_ops = &patch_func_sysfs_ops, + .default_attrs = patch_func_kobj_attrs, +}; + +static void patch_object_kobj_free(struct kobject *kobj) +{ + struct kpatch_object *obj = + container_of(kobj, struct kpatch_object, kobj); + kfree(obj); +} + +static struct kobj_type patch_object_ktype = { + .release = patch_object_kobj_free, + .sysfs_ops = &kobj_sysfs_ops, +}; + +static struct kpatch_object *patch_find_or_add_object(struct list_head *head, + const char *name) +{ + struct kpatch_object *object; + int ret; + + list_for_each_entry(object, head, list) { + if (!strcmp(object->name, name)) + return object; + } + + object = kzalloc(sizeof(*object), GFP_KERNEL); + if (!object) + return NULL; + + object->name = name; + INIT_LIST_HEAD(&object->funcs); + INIT_LIST_HEAD(&object->dynrelas); + + list_add_tail(&object->list, head); + + ret = kobject_init_and_add(&object->kobj, &patch_object_ktype, + &kpmod.kobj, "%s", object->name); + if (ret) { + list_del(&object->list); + kfree(object); + return NULL; + } + + return object; +} + +static void patch_free_objects(void) +{ + struct kpatch_object *object, *object_safe; + struct kpatch_func *func, *func_safe; + struct kpatch_dynrela *dynrela, *dynrela_safe; + + list_for_each_entry_safe(object, object_safe, &kpmod.objects, list) { + list_for_each_entry_safe(func, func_safe, &object->funcs, + list) { + list_del(&func->list); + kobject_put(&func->kobj); + } + list_for_each_entry_safe(dynrela, dynrela_safe, + &object->dynrelas, list) { + list_del(&dynrela->list); + kfree(dynrela); + } + list_del(&object->list); + kobject_put(&object->kobj); + } +} + +static int patch_is_func_forced(unsigned long addr) +{ + unsigned long *a; + for (a = __kpatch_force_funcs; a < __kpatch_force_funcs_end; a++) + if (*a == addr) + return 1; + return 0; +} + +static int patch_make_funcs_list(struct list_head *objects) +{ + struct kpatch_object *object; + struct kpatch_patch_func *p_func; + struct kpatch_func *func; + int ret; + + for (p_func = __kpatch_funcs; p_func < __kpatch_funcs_end; p_func++) { + object = patch_find_or_add_object(&kpmod.objects, + p_func->objname); + if (!object) + return -ENOMEM; + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (!func) + return -ENOMEM; + + func->new_addr = p_func->new_addr; + func->new_size = p_func->new_size; + func->old_size = p_func->old_size; + func->sympos = p_func->sympos; + func->name = p_func->name; + func->force = patch_is_func_forced(func->new_addr); + list_add_tail(&func->list, &object->funcs); + + ret = kobject_init_and_add(&func->kobj, &patch_func_ktype, + &object->kobj, "%s,%lu", + func->name, func->sympos ? func->sympos : 1); + if (ret) + return ret; + } + + return 0; +} + +static int patch_make_dynrelas_list(struct list_head *objects) +{ + struct kpatch_object *object; + struct kpatch_patch_dynrela *p_dynrela; + struct kpatch_dynrela *dynrela; + + for (p_dynrela = __kpatch_dynrelas; p_dynrela < __kpatch_dynrelas_end; + p_dynrela++) { + object = patch_find_or_add_object(objects, p_dynrela->objname); + if (!object) + return -ENOMEM; + + dynrela = kzalloc(sizeof(*dynrela), GFP_KERNEL); + if (!dynrela) + return -ENOMEM; + + dynrela->dest = p_dynrela->dest; + dynrela->type = p_dynrela->type; + dynrela->sympos = p_dynrela->sympos; + dynrela->name = p_dynrela->name; + dynrela->external = p_dynrela->external; + dynrela->addend = p_dynrela->addend; + list_add_tail(&dynrela->list, &object->dynrelas); + } + + return 0; +} + +static int patch_set_callbacks(struct list_head *objects) +{ + struct kpatch_pre_patch_callback *p_pre_patch_callback; + struct kpatch_post_patch_callback *p_post_patch_callback; + struct kpatch_pre_unpatch_callback *p_pre_unpatch_callback; + struct kpatch_post_unpatch_callback *p_post_unpatch_callback; + struct kpatch_object *object; + + for (p_pre_patch_callback = __kpatch_callbacks_pre_patch; + p_pre_patch_callback < __kpatch_callbacks_pre_patch_end; + p_pre_patch_callback++) { + + object = patch_find_or_add_object(objects, p_pre_patch_callback->objname); + if (!object) + return -ENOMEM; + + if (object->pre_patch_callback) { + pr_err("extra pre-patch callback for object: %s\n", + object->name); + return -EINVAL; + } + + object->pre_patch_callback = + (int (*)(struct kpatch_object *)) p_pre_patch_callback->callback; + } + + for (p_post_patch_callback = __kpatch_callbacks_post_patch; + p_post_patch_callback < __kpatch_callbacks_post_patch_end; + p_post_patch_callback++) { + + object = patch_find_or_add_object(objects, p_post_patch_callback->objname); + if (!object) + return -ENOMEM; + + if (object->post_patch_callback) { + pr_err("extra post-patch callback for object: %s\n", + object->name); + return -EINVAL; + } + + object->post_patch_callback = + (void (*)(struct kpatch_object *)) p_post_patch_callback->callback; + } + + for (p_pre_unpatch_callback = __kpatch_callbacks_pre_unpatch; + p_pre_unpatch_callback < __kpatch_callbacks_pre_unpatch_end; + p_pre_unpatch_callback++) { + + object = patch_find_or_add_object(objects, p_pre_unpatch_callback->objname); + if (!object) + return -ENOMEM; + + if (object->pre_unpatch_callback) { + pr_err("extra pre-unpatch callback for object: %s\n", + object->name); + return -EINVAL; + } + + object->pre_unpatch_callback = + (void (*)(struct kpatch_object *)) p_pre_unpatch_callback->callback; + } + + for (p_post_unpatch_callback = __kpatch_callbacks_post_unpatch; + p_post_unpatch_callback < __kpatch_callbacks_post_unpatch_end; + p_post_unpatch_callback++) { + + object = patch_find_or_add_object(objects, p_post_unpatch_callback->objname); + if (!object) + return -ENOMEM; + + if (object->post_unpatch_callback) { + pr_err("extra post-unpatch callback for object: %s\n", + object->name); + return -EINVAL; + } + + object->post_unpatch_callback = + (void (*)(struct kpatch_object *)) p_post_unpatch_callback->callback; + } + return 0; +} + +static int __init patch_init(void) +{ + int ret; + + ret = kobject_init_and_add(&kpmod.kobj, &patch_ktype, + kpatch_root_kobj, "%s", + THIS_MODULE->name); + if (ret) + return -ENOMEM; + + kpmod.mod = THIS_MODULE; + INIT_LIST_HEAD(&kpmod.objects); + + ret = patch_make_funcs_list(&kpmod.objects); + if (ret) + goto err_objects; + + ret = patch_make_dynrelas_list(&kpmod.objects); + if (ret) + goto err_objects; + + ret = patch_set_callbacks(&kpmod.objects); + if (ret) + goto err_objects; + + ret = kpatch_register(&kpmod, replace); + if (ret) + goto err_objects; + + return 0; + +err_objects: + patch_free_objects(); + kobject_put(&kpmod.kobj); + return ret; +} + +static void __exit patch_exit(void) +{ + WARN_ON(kpmod.enabled); + + patch_free_objects(); + kobject_put(&kpmod.kobj); +} + +module_init(patch_init); +module_exit(patch_exit); +MODULE_LICENSE("GPL"); diff --git a/kmod/patch/kpatch-patch.h b/kmod/patch/kpatch-patch.h new file mode 100644 index 0000000..da4f6a0 --- /dev/null +++ b/kmod/patch/kpatch-patch.h @@ -0,0 +1,63 @@ +/* + * kpatch-patch.h + * + * Copyright (C) 2014 Josh Poimboeuf + * + * 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 . + * + * Contains the structs used for the patch module special sections + */ + +#ifndef _KPATCH_PATCH_H_ +#define _KPATCH_PATCH_H_ + +struct kpatch_patch_func { + unsigned long new_addr; + unsigned long new_size; + unsigned long old_addr; + unsigned long old_size; + unsigned long sympos; + char *name; + char *objname; +}; + +struct kpatch_patch_dynrela { + unsigned long dest; + unsigned long src; + unsigned long type; + unsigned long sympos; + char *name; + char *objname; + int external; + long addend; +}; + +struct kpatch_pre_patch_callback { + int (*callback)(void *obj); + char *objname; +}; +struct kpatch_post_patch_callback { + void (*callback)(void *obj); + char *objname; +}; +struct kpatch_pre_unpatch_callback { + void (*callback)(void *obj); + char *objname; +}; +struct kpatch_post_unpatch_callback { + void (*callback)(void *obj); + char *objname; +}; + +#endif /* _KPATCH_PATCH_H_ */ diff --git a/kmod/patch/kpatch-syscall.h b/kmod/patch/kpatch-syscall.h new file mode 100644 index 0000000..e69f3c1 --- /dev/null +++ b/kmod/patch/kpatch-syscall.h @@ -0,0 +1,180 @@ +#ifndef __KPATCH_SYSCALL_H_ +#define __KPATCH_SYSCALL_H_ + +#include "kpatch-macros.h" + +/* + * These kpatch-specific syscall definition macros can be used for patching a + * syscall. + * + * Attempting to patch a syscall typically results in an error, due to a + * missing fentry hook in the inner __do_sys##name() function. The fentry hook + * is missing because of the 'inline' annotation, which invokes 'notrace'. + * + * These macros are copied almost verbatim from the kernel, the main difference + * being a 'kpatch' prefix added to the __do_sys##name() function name. This + * causes kpatch-build to treat it as a new function (due to + * its new name), and its caller __se_sys##name() function is inlined by its own + * caller __x64_sys##name() function, which has an fentry hook. + + * To patch a syscall, just replace the use of the SYSCALL_DEFINE1 (or similar) + * macro with the "KPATCH_" prefixed version. + */ + +#define KPATCH_IGNORE_SYSCALL_SECTIONS \ + KPATCH_IGNORE_SECTION("__syscalls_metadata"); \ + KPATCH_IGNORE_SECTION("_ftrace_events") + +#define KPATCH_SYSCALL_DEFINE1(name, ...) KPATCH_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) +#define KPATCH_SYSCALL_DEFINE2(name, ...) KPATCH_SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) +#define KPATCH_SYSCALL_DEFINE3(name, ...) KPATCH_SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) +#define KPATCH_SYSCALL_DEFINE4(name, ...) KPATCH_SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) +#define KPATCH_SYSCALL_DEFINE5(name, ...) KPATCH_SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) +#define KPATCH_SYSCALL_DEFINE6(name, ...) KPATCH_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) + +#define KPATCH_SYSCALL_DEFINEx(x, sname, ...) \ + KPATCH_IGNORE_SYSCALL_SECTIONS; \ + __KPATCH_SYSCALL_DEFINEx(x, sname, __VA_ARGS__) + +#ifdef CONFIG_X86_64 + +/* x86/include/asm/syscall_wrapper.h versions */ + +# if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) + +# define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + __X64_SYS_STUBx(x, name, __VA_ARGS__) \ + __IA32_SYS_STUBx(x, name, __VA_ARGS__) \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ + long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ + } \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + +# define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long __x64_sys##name(const struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__x64_sys##name, ERRNO); \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + asmlinkage long __x64_sys##name(const struct pt_regs *regs) \ + { \ + return __se_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\ + } \ + __IA32_SYS_STUBx(x, name, __VA_ARGS__) \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ + long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ + } \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +# endif /* LINUX_VERSION_CODE */ + +#elif defined(CONFIG_S390) + +/* s390/include/asm/syscall_wrapper.h versions */ + +#define __KPATCH_S390_SYS_STUBx(x, name, ...) \ + long __s390_sys##name(struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO); \ + long __s390_sys##name(struct pt_regs *regs) \ + { \ + long ret = __kpatch_do_sys##name(SYSCALL_PT_ARGS(x, regs, \ + __SC_COMPAT_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + return ret; \ + } + +#define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ + __diag_push(); \ + __diag_ignore(GCC, 8, "-Wattribute-alias", \ + "Type aliasing is used to sanitize syscall arguments"); \ + long __s390x_sys##name(struct pt_regs *regs) \ + __attribute__((alias(__stringify(__se_sys##name)))); \ + ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ + long __se_sys##name(struct pt_regs *regs); \ + __KPATCH_S390_SYS_STUBx(x, name, __VA_ARGS__) \ + long __se_sys##name(struct pt_regs *regs) \ + { \ + long ret = __kpatch_do_sys##name(SYSCALL_PT_ARGS(x, regs, \ + __SC_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + return ret; \ + } \ + __diag_pop(); \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +#endif /* CONFIG_X86_64 */ + + +#ifndef __KPATCH_SYSCALL_DEFINEx + +/* include/linux/syscalls.h versions */ + +# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) +# define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ + __diag_push(); \ + __diag_ignore(GCC, 8, "-Wattribute-alias", \ + "Type aliasing is used to sanitize syscall arguments");\ + asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ + __attribute__((alias(__stringify(__se_sys##name)))); \ + ALLOW_ERROR_INJECTION(sys##name, ERRNO); \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + asmlinkage long __attribute__((optimize("-fno-optimize-sibling-calls")))\ + __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ + long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ + } \ + __diag_pop(); \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) +# define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ + __attribute__((alias(__stringify(__se_sys##name)))); \ + ALLOW_ERROR_INJECTION(sys##name, ERRNO); \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ + long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ + } \ + static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +# else +# define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ + __attribute__((alias(__stringify(SyS##name)))); \ + static inline long __kpatch_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ + asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ + long ret = __kpatch_SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ + } \ + static inline long __kpatch_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +# endif + +#endif /* __KPATCH_SYSCALL_DEFINEx */ + +#endif /* __KPATCH_SYSCALL_H_ */ diff --git a/kmod/patch/kpatch.h b/kmod/patch/kpatch.h new file mode 120000 index 0000000..e8de666 --- /dev/null +++ b/kmod/patch/kpatch.h @@ -0,0 +1 @@ +../core/kpatch.h \ No newline at end of file diff --git a/kmod/patch/kpatch.lds.S b/kmod/patch/kpatch.lds.S new file mode 100644 index 0000000..bc5de82 --- /dev/null +++ b/kmod/patch/kpatch.lds.S @@ -0,0 +1,50 @@ +__kpatch_funcs = ADDR(.kpatch.funcs); +__kpatch_funcs_end = ADDR(.kpatch.funcs) + SIZEOF(.kpatch.funcs); + +#ifdef __KPATCH_MODULE__ +__kpatch_dynrelas = ADDR(.kpatch.dynrelas); +__kpatch_dynrelas_end = ADDR(.kpatch.dynrelas) + SIZEOF(.kpatch.dynrelas); +__kpatch_checksum = ADDR(.kpatch.checksum); +#endif + +SECTIONS +{ + .kpatch.callbacks.pre_patch : { + __kpatch_callbacks_pre_patch = . ; + *(.kpatch.callbacks.pre_patch) + __kpatch_callbacks_pre_patch_end = . ; + /* + * Pad the end of the section with zeros in case the section is empty. + * This prevents the kernel from discarding the section at module + * load time. __kpatch_callbacks_pre_patch_end will still point to the + * end of the section before the padding. If the + * .kpatch.callbacks.pre_patch section is empty, + * __kpatch_callbacks_pre_patch equals __kpatch_callbacks_pre_patch_end. + */ + QUAD(0); + } + .kpatch.callbacks.post_patch : { + __kpatch_callbacks_post_patch = . ; + *(.kpatch.callbacks.post_patch) + __kpatch_callbacks_post_patch_end = . ; + QUAD(0); + } + .kpatch.callbacks.pre_unpatch : { + __kpatch_callbacks_pre_unpatch = . ; + *(.kpatch.callbacks.pre_unpatch) + __kpatch_callbacks_pre_unpatch_end = . ; + QUAD(0); + } + .kpatch.callbacks.post_unpatch : { + __kpatch_callbacks_post_unpatch = . ; + *(.kpatch.callbacks.post_unpatch) + __kpatch_callbacks_post_unpatch_end = . ; + QUAD(0); + } + .kpatch.force : { + __kpatch_force_funcs = . ; + *(.kpatch.force) + __kpatch_force_funcs_end = . ; + QUAD(0); + } +} diff --git a/kmod/patch/livepatch-patch-hook.c b/kmod/patch/livepatch-patch-hook.c new file mode 100644 index 0000000..3d13ab9 --- /dev/null +++ b/kmod/patch/livepatch-patch-hook.c @@ -0,0 +1,606 @@ +/* + * Copyright (C) 2013-2014 Josh Poimboeuf + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include + +#include "kpatch-patch.h" + +#ifndef UTS_UBUNTU_RELEASE_ABI +#define UTS_UBUNTU_RELEASE_ABI 0 +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || \ + defined(RHEL_RELEASE_CODE) +#define HAVE_ELF_RELOCS +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) && \ + UTS_UBUNTU_RELEASE_ABI >= 7) || \ + defined(RHEL_RELEASE_CODE) +#define HAVE_SYMPOS +#endif + +#ifdef RHEL_RELEASE_CODE +# if RHEL_RELEASE_CODE <= RHEL_RELEASE_VERSION(7, 5) +# define HAVE_IMMEDIATE +# endif +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) && \ + LINUX_VERSION_CODE <= KERNEL_VERSION(4, 15, 0)) +# define HAVE_IMMEDIATE +#endif + +#ifdef RHEL_RELEASE_CODE +# if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 5) +# define HAVE_CALLBACKS +# endif +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +# define HAVE_CALLBACKS +#endif + +#ifdef RHEL_RELEASE_CODE +# if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 8) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0)) || \ + RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8, 2) +# define HAVE_SIMPLE_ENABLE +# endif +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) +# define HAVE_SIMPLE_ENABLE +#endif + +#ifdef RHEL_RELEASE_CODE +# if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8, 2) +# define HAVE_KLP_REPLACE +# endif +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) +# define HAVE_KLP_REPLACE +#endif + +#ifndef KLP_REPLACE_ENABLE +#define KLP_REPLACE_ENABLE true +#endif + +/* + * There are quite a few similar structures at play in this file: + * - livepatch.h structs prefixed with klp_* + * - kpatch-patch.h structs prefixed with kpatch_patch_* + * - local scaffolding structs prefixed with patch_* + * + * The naming of the struct variables follows this convention: + * - livepatch struct being with "l" (e.g. lfunc) + * - kpatch_patch structs being with "k" (e.g. kfunc) + * - local scaffolding structs have no prefix (e.g. func) + * + * The program reads in kpatch_patch structures, arranges them into the + * scaffold structures, then creates a livepatch structure suitable for + * registration with the livepatch kernel API. The scaffold structs only + * exist to allow the construction of the klp_patch struct. Once that is + * done, the scaffold structs are no longer needed. + */ + +/* + * lpatch is the kernel data structure that will be created on patch + * init, registered with the livepatch API on init, and finally + * unregistered when the patch exits. Its struct klp_object *objs + * member must be dynamically allocated according to the number of + * target objects it will be patching. + */ +static struct klp_patch *lpatch; + +static LIST_HEAD(patch_objects); +static int patch_objects_nr; + +/** + * struct patch_object - scaffolding structure tracking patch target objects + * @list: list of patch_object (threaded onto patch_objects) + * @funcs: list of patch_func associated with this object + * @relocs: list of patch_reloc associated with this object + * @callbacks: kernel struct of object callbacks + * @name: patch target object name (NULL for vmlinux) + * @funcs_nr: count of kpatch_patch_func added to @funcs + * @relocs_nr: count of patch_reloc added to @relocs + */ +struct patch_object { + struct list_head list; + struct list_head funcs; + struct list_head relocs; +#ifdef HAVE_CALLBACKS + struct klp_callbacks callbacks; +#endif + const char *name; + int funcs_nr, relocs_nr; +}; + +/** + * struct patch_func - scaffolding structure for kpatch_patch_func + * @list: list of patch_func (threaded onto patch_object.funcs) + * @kfunc: array of kpatch_patch_func + */ +struct patch_func { + struct list_head list; + struct kpatch_patch_func *kfunc; +}; + +/** + * struct patch_reloc - scaffolding structure for kpatch_patch_dynrela + * @list: list of patch_reloc (threaded onto patch_object.relocs) + * @kdynrela: array of kpatch_patch_dynrela + */ +struct patch_reloc { + struct list_head list; + struct kpatch_patch_dynrela *kdynrela; +}; + +/** + * patch_alloc_new_object() - creates and initializes a new patch_object + * @name: target object name + * + * Return: pointer to new patch_object, NULL on failure. + * + * Does not check for previously created patch_objects with the same + * name. Updates patch_objects_nr and threads new data structure onto + * the patch_objects list. + */ +static struct patch_object *patch_alloc_new_object(const char *name) +{ + struct patch_object *object; + + object = kzalloc(sizeof(*object), GFP_KERNEL); + if (!object) + return NULL; + INIT_LIST_HEAD(&object->funcs); +#ifndef HAVE_ELF_RELOCS + INIT_LIST_HEAD(&object->relocs); +#endif + if (strcmp(name, "vmlinux")) + object->name = name; + list_add(&object->list, &patch_objects); + patch_objects_nr++; + return object; +} + +/** + * patch_find_object_by_name() - find or create a patch_object with a + * given name + * @name: target object name + * + * Return: pointer to patch_object, NULL on failure. + * + * Searches the patch_objects list for an already created instance with + * @name, otherwise tries to create it via patch_alloc_new_object() + */ +static struct patch_object *patch_find_object_by_name(const char *name) +{ + struct patch_object *object; + + list_for_each_entry(object, &patch_objects, list) + if ((!strcmp(name, "vmlinux") && !object->name) || + (object->name && !strcmp(object->name, name))) + return object; + return patch_alloc_new_object(name); +} + +/** + * patch_add_func_to_object() - create scaffolding from kpatch_patch_func data + * + * @kfunc: Individual kpatch_patch_func pointer + * + * Return: 0 on success, -ENOMEM on failure. + * + * Builds scaffolding data structures from .kpatch.funcs section's array + * of kpatch_patch_func structures. Updates the associated + * patch_object's funcs_nr count. + */ +static int patch_add_func_to_object(struct kpatch_patch_func *kfunc) +{ + struct patch_func *func; + struct patch_object *object; + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (!func) + return -ENOMEM; + INIT_LIST_HEAD(&func->list); + func->kfunc = kfunc; + + object = patch_find_object_by_name(kfunc->objname); + if (!object) { + kfree(func); + return -ENOMEM; + } + list_add(&func->list, &object->funcs); + object->funcs_nr++; + return 0; +} + +#ifndef HAVE_ELF_RELOCS +/** + * patch_add_reloc_to_object() - create scaffolding from kpatch_patch_dynrela data + * + * @kdynrela: Individual kpatch_patch_dynrela pointer + * + * Return: 0 on success, -ENOMEM on failure. + * + * Builds scaffolding data structures from .kpatch.dynrelas section's array + * of kpatch_patch_dynrela structures. Updates the associated + * patch_object's relocs_nr count. + */ +static int patch_add_reloc_to_object(struct kpatch_patch_dynrela *kdynrela) +{ + struct patch_reloc *reloc; + struct patch_object *object; + + reloc = kzalloc(sizeof(*reloc), GFP_KERNEL); + if (!reloc) + return -ENOMEM; + INIT_LIST_HEAD(&reloc->list); + reloc->kdynrela = kdynrela; + + object = patch_find_object_by_name(kdynrela->objname); + if (!object) { + kfree(reloc); + return -ENOMEM; + } + list_add(&reloc->list, &object->relocs); + object->relocs_nr++; + return 0; +} +#endif + +/** + * patch_free_scaffold() - tear down the temporary kpatch scaffolding + */ +static void patch_free_scaffold(void) { + struct patch_func *func, *safefunc; + struct patch_object *object, *safeobject; +#ifndef HAVE_ELF_RELOCS + struct patch_reloc *reloc, *safereloc; +#endif + + list_for_each_entry_safe(object, safeobject, &patch_objects, list) { + list_for_each_entry_safe(func, safefunc, + &object->funcs, list) { + list_del(&func->list); + kfree(func); + } +#ifndef HAVE_ELF_RELOCS + list_for_each_entry_safe(reloc, safereloc, + &object->relocs, list) { + list_del(&reloc->list); + kfree(reloc); + } +#endif + list_del(&object->list); + kfree(object); + } +} + +/** + * patch_free_livepatch() - release the klp_patch and friends + */ +static void patch_free_livepatch(struct klp_patch *patch) +{ + struct klp_object *object; + + if (patch) { + for (object = patch->objs; object && object->funcs; object++) { + if (object->funcs) + kfree(object->funcs); +#ifndef HAVE_ELF_RELOCS + if (object->relocs) + kfree(object->relocs); +#endif + } + if (patch->objs) + kfree(patch->objs); + kfree(patch); + } +} + +/* Defined by kpatch.lds.S */ +extern struct kpatch_pre_patch_callback __kpatch_callbacks_pre_patch[], __kpatch_callbacks_pre_patch_end[]; +extern struct kpatch_post_patch_callback __kpatch_callbacks_post_patch[], __kpatch_callbacks_post_patch_end[]; +extern struct kpatch_pre_unpatch_callback __kpatch_callbacks_pre_unpatch[], __kpatch_callbacks_pre_unpatch_end[]; +extern struct kpatch_post_unpatch_callback __kpatch_callbacks_post_unpatch[], __kpatch_callbacks_post_unpatch_end[]; + +#ifdef HAVE_CALLBACKS +/** + * add_callbacks_to_patch_objects() - create patch_objects that have callbacks + * + * Return: 0 on success, -ENOMEM or -EINVAL on failure + * + * Iterates through all kpatch pre/post-(un)patch callback data + * structures and creates scaffolding patch_objects for them. + */ +static int add_callbacks_to_patch_objects(void) +{ + struct kpatch_pre_patch_callback *p_pre_patch_callback; + struct kpatch_post_patch_callback *p_post_patch_callback; + struct kpatch_pre_unpatch_callback *p_pre_unpatch_callback; + struct kpatch_post_unpatch_callback *p_post_unpatch_callback; + struct patch_object *object; + + for (p_pre_patch_callback = __kpatch_callbacks_pre_patch; + p_pre_patch_callback < __kpatch_callbacks_pre_patch_end; + p_pre_patch_callback++) { + object = patch_find_object_by_name(p_pre_patch_callback->objname); + if (!object) + return -ENOMEM; + if (object->callbacks.pre_patch) { + pr_err("extra pre-patch callback for object: %s\n", + object->name ? object->name : "vmlinux"); + return -EINVAL; + } + object->callbacks.pre_patch = (int (*)(struct klp_object *)) + p_pre_patch_callback->callback; + } + + for (p_post_patch_callback = __kpatch_callbacks_post_patch; + p_post_patch_callback < __kpatch_callbacks_post_patch_end; + p_post_patch_callback++) { + object = patch_find_object_by_name(p_post_patch_callback->objname); + if (!object) + return -ENOMEM; + if (object->callbacks.post_patch) { + pr_err("extra post-patch callback for object: %s\n", + object->name ? object->name : "vmlinux"); + return -EINVAL; + } + object->callbacks.post_patch = (void (*)(struct klp_object *)) + p_post_patch_callback->callback; + } + + for (p_pre_unpatch_callback = __kpatch_callbacks_pre_unpatch; + p_pre_unpatch_callback < __kpatch_callbacks_pre_unpatch_end; + p_pre_unpatch_callback++) { + object = patch_find_object_by_name(p_pre_unpatch_callback->objname); + if (!object) + return -ENOMEM; + if (object->callbacks.pre_unpatch) { + pr_err("extra pre-unpatch callback for object: %s\n", + object->name ? object->name : "vmlinux"); + return -EINVAL; + } + object->callbacks.pre_unpatch = (void (*)(struct klp_object *)) + p_pre_unpatch_callback->callback; + } + + for (p_post_unpatch_callback = __kpatch_callbacks_post_unpatch; + p_post_unpatch_callback < __kpatch_callbacks_post_unpatch_end; + p_post_unpatch_callback++) { + object = patch_find_object_by_name(p_post_unpatch_callback->objname); + if (!object) + return -ENOMEM; + if (object->callbacks.post_unpatch) { + pr_err("extra post-unpatch callback for object: %s\n", + object->name ? object->name : "vmlinux"); + return -EINVAL; + } + object->callbacks.post_unpatch = (void (*)(struct klp_object *)) + p_post_unpatch_callback->callback; + } + + return 0; +} +#else /* HAVE_CALLBACKS */ +static inline int add_callbacks_to_patch_objects(void) +{ + if (__kpatch_callbacks_pre_patch != + __kpatch_callbacks_pre_patch_end || + __kpatch_callbacks_post_patch != + __kpatch_callbacks_post_patch_end || + __kpatch_callbacks_pre_unpatch != + __kpatch_callbacks_pre_unpatch_end || + __kpatch_callbacks_post_unpatch != + __kpatch_callbacks_post_unpatch_end) { + pr_err("patch callbacks are not supported\n"); + return -EINVAL; + } + + return 0; +} +#endif /* HAVE_CALLBACKS */ + +/* Defined by kpatch.lds.S */ +extern struct kpatch_patch_func __kpatch_funcs[], __kpatch_funcs_end[]; +#ifndef HAVE_ELF_RELOCS +extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[]; +#endif + +static int __init patch_init(void) +{ + struct kpatch_patch_func *kfunc; + struct klp_object *lobjects, *lobject; + struct klp_func *lfuncs, *lfunc; + struct patch_object *object; + struct patch_func *func; + int ret = 0, i, j; +#ifndef HAVE_ELF_RELOCS + struct kpatch_patch_dynrela *kdynrela; + struct patch_reloc *reloc; + struct klp_reloc *lrelocs, *lreloc; +#endif + + + /* + * Step 1 - read from output.o, create temporary scaffolding + * data-structures + */ + + /* organize functions and relocs by object in scaffold */ + for (kfunc = __kpatch_funcs; + kfunc != __kpatch_funcs_end; + kfunc++) { + ret = patch_add_func_to_object(kfunc); + if (ret) + goto out; + } + +#ifndef HAVE_ELF_RELOCS + for (kdynrela = __kpatch_dynrelas; + kdynrela != __kpatch_dynrelas_end; + kdynrela++) { + ret = patch_add_reloc_to_object(kdynrela); + if (ret) + goto out; + } +#endif + + ret = add_callbacks_to_patch_objects(); + if (ret) + goto out; + + /* past this point, only possible return code is -ENOMEM */ + ret = -ENOMEM; + + /* + * Step 2 - create livepatch klp_patch and friends + * + * There are two dynamically allocated parts: + * + * klp_patch + * klp_object objs [patch_objects_nr] <= i + * klp_func funcs [object->funcs_nr] <= j + */ + + /* allocate and fill livepatch structures */ + lpatch = kzalloc(sizeof(*lpatch), GFP_KERNEL); + if (!lpatch) + goto out; + + lobjects = kzalloc(sizeof(*lobjects) * (patch_objects_nr+1), + GFP_KERNEL); + if (!lobjects) + goto out; + lpatch->mod = THIS_MODULE; + lpatch->objs = lobjects; +#ifdef HAVE_KLP_REPLACE + lpatch->replace = KLP_REPLACE_ENABLE; +#endif +#if defined(__powerpc64__) && defined(HAVE_IMMEDIATE) + lpatch->immediate = true; +#endif + + i = 0; + list_for_each_entry(object, &patch_objects, list) { + lobject = &lobjects[i]; + lobject->name = object->name; + lfuncs = kzalloc(sizeof(struct klp_func) * + (object->funcs_nr+1), GFP_KERNEL); + if (!lfuncs) + goto out; + lobject->funcs = lfuncs; + j = 0; + list_for_each_entry(func, &object->funcs, list) { + lfunc = &lfuncs[j]; + lfunc->old_name = func->kfunc->name; + lfunc->new_func = (void *)func->kfunc->new_addr; +#ifdef HAVE_SYMPOS + lfunc->old_sympos = func->kfunc->sympos; +#else + lfunc->old_addr = func->kfunc->old_addr; +#endif + j++; + } + +#ifndef HAVE_ELF_RELOCS + lrelocs = kzalloc(sizeof(struct klp_reloc) * + (object->relocs_nr+1), GFP_KERNEL); + if (!lrelocs) + goto out; + lobject->relocs = lrelocs; + j = 0; + list_for_each_entry(reloc, &object->relocs, list) { + lreloc = &lrelocs[j]; + lreloc->loc = reloc->kdynrela->dest; +#ifdef HAVE_SYMPOS + lreloc->sympos = reloc->kdynrela->sympos; +#else + lreloc->val = reloc->kdynrela->src; +#endif /* HAVE_SYMPOS */ + lreloc->type = reloc->kdynrela->type; + lreloc->name = reloc->kdynrela->name; + lreloc->addend = reloc->kdynrela->addend; + lreloc->external = reloc->kdynrela->external; + j++; + } +#endif /* HAVE_ELF_RELOCS */ + +#ifdef HAVE_CALLBACKS + lobject->callbacks = object->callbacks; +#endif + + i++; + } + + /* + * Step 3 - throw away scaffolding + */ + + /* + * Once the patch structure that the live patching API expects + * has been built, we can release the scaffold structure. + */ + patch_free_scaffold(); + +#ifndef HAVE_SIMPLE_ENABLE + ret = klp_register_patch(lpatch); + if (ret) { + patch_free_livepatch(lpatch); + return ret; + } +#endif + + ret = klp_enable_patch(lpatch); + if (ret) { +#ifndef HAVE_SIMPLE_ENABLE + WARN_ON(klp_unregister_patch(lpatch)); +#endif + patch_free_livepatch(lpatch); + return ret; + } + + return 0; +out: + patch_free_livepatch(lpatch); + patch_free_scaffold(); + return ret; +} + +static void __exit patch_exit(void) +{ +#ifndef HAVE_SIMPLE_ENABLE + WARN_ON(klp_unregister_patch(lpatch)); +#endif + patch_free_livepatch(lpatch); +} + +module_init(patch_init); +module_exit(patch_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); diff --git a/kmod/patch/patch-hook.c b/kmod/patch/patch-hook.c new file mode 100644 index 0000000..ff314d9 --- /dev/null +++ b/kmod/patch/patch-hook.c @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#if IS_ENABLED(CONFIG_LIVEPATCH) +#include "livepatch-patch-hook.c" +#else +#include "kpatch-patch-hook.c" +#endif + diff --git a/kpatch-build/.kpatch-cc.swp b/kpatch-build/.kpatch-cc.swp new file mode 100644 index 0000000000000000000000000000000000000000..424e7620db583e31ece666f78de59ae9bd893b9b GIT binary patch literal 12288 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 c+6UEZUtJolECzn@96m;e9( literal 0 HcmV?d00001 diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c new file mode 100644 index 0000000..1967bd8 --- /dev/null +++ b/kpatch-build/create-diff-object.c @@ -0,0 +1,4041 @@ +/* + * create-diff-object.c + * + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2013-2014 Josh Poimboeuf + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +/* + * This file contains the heart of the ELF object differencing engine. + * + * The tool takes two ELF objects from two versions of the same source + * file; a "orig" object and a "patched" object. These object need to have + * been compiled with the -ffunction-sections and -fdata-sections GCC options. + * + * The tool compares the objects at a section level to determine what + * sections have changed. Once a list of changed sections has been generated, + * various rules are applied to determine any object local sections that + * are dependencies of the changed section and also need to be included in + * the output object. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "lookup.h" +#include "kpatch-patch.h" +#include "kpatch-elf.h" +#include "kpatch-intermediate.h" +#include "kpatch.h" + +#define DIFF_FATAL(format, ...) \ +({ \ + fprintf(stderr, "ERROR: %s: " format "\n", childobj, ##__VA_ARGS__); \ + err(EXIT_STATUS_DIFF_FATAL, "unreconcilable difference"); \ +}) + +char *childobj; + +enum subsection { + SUBSECTION_NORMAL, + SUBSECTION_HOT, + SUBSECTION_UNLIKELY +}; + +enum loglevel loglevel = NORMAL; + +bool KLP_ARCH; + +/******************* + * Data structures + * ****************/ +struct special_section { + char *name; + enum architecture arch; + int (*group_size)(struct kpatch_elf *kelf, int offset); +}; + +/************* + * Functions + * **********/ + +static bool is_bundleable(struct symbol *sym) +{ + if (sym->type == STT_FUNC && + !strncmp(sym->sec->name, ".text.",6) && + !strcmp(sym->sec->name + 6, sym->name)) + return true; + + if (sym->type == STT_FUNC && + !strncmp(sym->sec->name, ".text.unlikely.",15) && + (!strcmp(sym->sec->name + 15, sym->name) || + (strstr(sym->name, ".cold") && + !strncmp(sym->sec->name + 15, sym->name, strlen(sym->sec->name) - 15)))) + return true; + + if (sym->type == STT_FUNC && + !strncmp(sym->sec->name, ".text.hot.",10) && + !strcmp(sym->sec->name + 10, sym->name)) + return true; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.",6) && + !strcmp(sym->sec->name + 6, sym->name)) + return true; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.rel.", 10) && + !strcmp(sym->sec->name + 10, sym->name)) + return true; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.rel.ro.", 13) && + !strcmp(sym->sec->name + 13, sym->name)) + return true; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.rel.ro.local.", 19) && + !strcmp(sym->sec->name + 19, sym->name)) + return 1; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.rel.local.", 16) && + !strcmp(sym->sec->name + 16, sym->name)) + return 1; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".rodata.",8) && + !strcmp(sym->sec->name + 8, sym->name)) + return true; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".bss.",5) && + !strcmp(sym->sec->name + 5, sym->name)) + return true; + + return false; +} + +/* Symbol st_others value for powerpc */ +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) + +/* + * On ppc64le, the function prologue generated by GCC 6+ has the sequence: + * + * .globl my_func + * .type my_func, @function + * .quad .TOC.-my_func + * my_func: + * .reloc ., R_PPC64_ENTRY ; optional + * ld r2,-8(r12) + * add r2,r2,r12 + * .localentry my_func, .-my_func + * + * my_func is the global entry point, which, when called, sets up the TOC. + * .localentry is the local entry point, for calls to the function from within + * the object file. The local entry point is 8 bytes after the global entry + * point. + */ +static bool is_gcc6_localentry_bundled_sym(struct kpatch_elf *kelf, + struct symbol *sym) +{ + switch(kelf->arch) { + case PPC64: + return ((PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other) != 0) && + sym->sym.st_value == 8); + case X86_64: + return false; + case S390: + return false; + default: + ERROR("unsupported arch"); + } + + return false; +} + +/* + * On ppc64le, when a function references data, it does so indirectly, via the + * .toc section. So there are *two* levels of relas: + * + * 1) the original function rela, referring to the .toc section; and + * + * 2) the .toc section rela, referring to the data needed by the function. + * + * For example: + * + * Relocation section '.rela.text.netlink_release' at offset 0xcadf0 contains 44 entries: + * ... + * 0000000000000398 0000007300000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 138 + * 00000000000003a0 0000007300000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 138 + * + * Relocation section '.rela.toc' at offset 0xcc6b0 contains 46 entries: + * ... + * 0000000000000138 0000002a00000026 R_PPC64_ADDR64 0000000000000000 .text.deferred_put_nlk_sk + 8 + * + * The below function takes the "first level" rela as input, and, if it refers + * to .toc, returns the "second level" rela, which is the one that refers to + * the actual data symbol. + * + * In some rare cases, a .toc entry has constant data, and thus has no + * corresponding rela. In that case, NULL is returned. + */ +static struct rela *toc_rela(const struct rela *rela) +{ + if (rela->type != R_PPC64_TOC16_HA && + rela->type != R_PPC64_TOC16_LO_DS) + return (struct rela *)rela; + + /* Only constants in toc */ + if (!rela->sym->sec->rela) + return NULL; + + /* Will return NULL for .toc constant entries */ + return find_rela_by_offset(rela->sym->sec->rela, + (unsigned int)rela->addend); +} + +/* + * When compiling with -ffunction-sections and -fdata-sections, almost every + * symbol gets its own dedicated section. We call such symbols "bundled" + * symbols. They're indicated by "sym->sec->sym == sym". + */ +static void kpatch_bundle_symbols(struct kpatch_elf *kelf) +{ + struct symbol *sym; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (is_bundleable(sym)) { + if (sym->sym.st_value != 0 && + !is_gcc6_localentry_bundled_sym(kelf, sym)) { + ERROR("symbol %s at offset %lu within section %s, expected 0", + sym->name, sym->sym.st_value, + sym->sec->name); + } + + sym->sec->sym = sym; + } + } +} + +static struct symbol *kpatch_lookup_parent(struct kpatch_elf *kelf, + const char *symname, + const char *child_suffix) +{ + struct symbol *parent; + char *pname; + + pname = strndup(symname, child_suffix - symname); + if (!pname) + ERROR("strndup"); + + parent = find_symbol_by_name(&kelf->symbols, pname); + free(pname); + + return parent; +} + +/* + * During optimization gcc may move unlikely execution branches into *.cold + * subfunctions. Some functions can also be split into multiple *.part + * functions. + * kpatch_detect_child_functions detects such subfunctions and + * crossreferences them with their parent functions through parent/child + * pointers. + */ +static void kpatch_detect_child_functions(struct kpatch_elf *kelf) +{ + struct symbol *sym; + + list_for_each_entry(sym, &kelf->symbols, list) { + char *childstr; + + if (sym->type != STT_FUNC) + continue; + + childstr = strstr(sym->name, ".cold"); + if (childstr) { + sym->parent = kpatch_lookup_parent(kelf, sym->name, + childstr); + if (!sym->parent) + ERROR("failed to find parent function for %s", + sym->name); + } else { + childstr = strstr(sym->name, ".part."); + if (!childstr) + continue; + sym->parent = kpatch_lookup_parent(kelf, sym->name, + childstr); + } + + if (sym->parent) + list_add_tail(&sym->subfunction_node, &sym->parent->children); + } +} + +static bool is_dynamic_debug_symbol(struct symbol *sym) +{ + if (sym->type == STT_OBJECT && !strcmp(sym->sec->name, "__verbose")) + return true; + if (sym->type == STT_OBJECT && !strcmp(sym->sec->name, "__dyndbg")) + return true; + if (sym->type == STT_SECTION && !strcmp(sym->name, "__verbose")) + return true; + if (sym->type == STT_SECTION && !strcmp(sym->name, "__dyndbg")) + return true; + return false; +} + +static bool is_string_literal_section(struct section *sec) +{ + return !strncmp(sec->name, ".rodata.", 8) && strstr(sec->name, ".str"); +} + +/* + * This function detects whether the given symbol is a "special" static local + * variable (for lack of a better term). + * + * Special static local variables should never be correlated and should always + * be included if they are referenced by an included function. + */ +static bool is_special_static(struct symbol *sym) +{ + static char *var_names[] = { + "__key", + "__warned", + "__already_done.", + "__func__", + "__FUNCTION__", + "_rs", + "CSWTCH", + "_entry", + NULL, + }; + char **var_name; + + if (!sym) + return false; + + /* pr_debug() uses static local variables in the __verbose or __dyndbg section */ + if (is_dynamic_debug_symbol(sym)) + return true; + + if (sym->type == STT_SECTION) { + /* make sure section is bundled */ + if (!sym->sec->sym) + return false; + + /* use bundled object/function symbol for matching */ + sym = sym->sec->sym; + } + + if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL) + return false; + + if (!strcmp(sym->sec->name, ".data.once")) + return true; + + for (var_name = var_names; *var_name; var_name++) { + size_t var_name_len = strlen(*var_name); + char buf[256]; + + snprintf(buf, 256, ".%s.", *var_name); + + /* First look for gcc-style statics: '.' */ + if (!strncmp(sym->name, buf + 1, var_name_len + 1)) + return true; + + buf[var_name_len + 1] = '\0'; + /* Next clang-style statics: '.' */ + if (strstr(sym->name, buf)) + return true; + } + + return false; +} + +static bool has_digit_tail(char *tail) +{ + if (*tail != '.') + return false; + + while (isdigit(*++tail)) + ; + + if (!*tail) + return true; + + return false; +} + +/* + * This is like strcmp, but for gcc-mangled symbols. It skips the comparison + * of any substring which consists of '.' followed by any number of digits. + */ +static int kpatch_mangled_strcmp(char *s1, char *s2) +{ + /* + * ELF string sections aren't mangled, though they look that way. Just + * compare them normally. + */ + if (strstr(s1, ".str1.")) + return strcmp(s1, s2); + + while (*s1 == *s2) { + if (!*s1) + return 0; + if (*s1 == '.' && isdigit(s1[1])) { + if (!isdigit(s2[1])) + return 1; + while (isdigit(*++s1)) + ; + while (isdigit(*++s2)) + ; + } else { + s1++; + s2++; + } + } + + if ((!*s1 && has_digit_tail(s2)) || + (!*s2 && has_digit_tail(s1))) + return 0; + + return 1; +} + +static bool rela_equal(struct rela *rela1, struct rela *rela2) +{ + struct rela *rela_toc1, *rela_toc2; + unsigned long toc_data1 = 0, toc_data2 = 0; /* = 0 to prevent gcc warning */ + + if (rela1->type != rela2->type || + rela1->offset != rela2->offset) + return false; + + /* + * On x86, .altinstr_aux is used to store temporary code which allows + * static_cpu_has() to work before apply_alternatives() has run. This + * code is completely inert for modules, because apply_alternatives() + * runs during module init, before the module is fully formed. Any + * changed references to it (i.e. changed addend) can be ignored. As + * long as they're both references to .altinstr_aux, they can be + * considered equal, even if the addends differ. + */ + if (!strcmp(rela1->sym->name, ".altinstr_aux") && + !strcmp(rela2->sym->name, ".altinstr_aux")) + return true; + + /* + * With -mcmodel=large on ppc64le, GCC might generate entries in the .toc + * section for relocation symbol references. The .toc offsets may change + * between the original and patched .o, so comparing ".toc + offset" isn't + * right. Compare the .toc-based symbols by reading the corresponding relas + * from the .toc section. + */ + rela_toc1 = toc_rela(rela1); + if (!rela_toc1) { + /* + * .toc section entries are mostly place holder for relocation entries, specified + * in .rela.toc section. Sometimes, .toc section may have constants as entries. + * These constants are not reference to any symbols, but plain instructions mostly + * due to some arithmetics in the functions referring them. + * + * They are referred by the functions like normal .toc entries, these entries can + * not be resolved to any symbols. + * + * Disassembly of section .toc: + * + * 0000000000000000 <.toc>: + * ... + * 148: R_PPC64_ADDR64 .data.capacity_margin + * 150: 0b d7 a3 70 andi. r3,r5,55051 + * 154: 3d 0a d7 a3 lhz r30,2621(r23) + * 158: R_PPC64_ADDR64 sched_max_numa_distance + * + * Relocation section '.rela.toc' at offset 0xadac0 contains 160 entries: + * Offset Info Type Symbol's Value Symbol's Name + Addend + * ... + * 0000000000000148 0000009100000026 R_PPC64_ADDR64 0000000000000000 .data.capacity_margin + 0 + * 0000000000000158 000001a500000026 R_PPC64_ADDR64 0000000000000000 sched_max_numa_distance + 0 + * + * Relocation section '.rela.text.select_task_rq_fair' at offset 0x90e98 contains 37 entries: + * Offset Info Type Symbol's Value Symbol's Name + Addend + * ... + * 00000000000004a0 0000008800000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 148 + * 00000000000004ac 0000008800000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 148 + * 0000000000000514 0000008800000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 150 + * 000000000000051c 0000008800000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 150 + */ + memcpy(&toc_data1, rela1->sym->sec->data->d_buf + rela1->addend, sizeof(toc_data1)); + if (!toc_data1) + ERROR(".toc entry not found %s + %lx", rela1->sym->name, rela1->addend); + } + + rela_toc2 = toc_rela(rela2); + if (!rela_toc2) { + memcpy(&toc_data2, rela2->sym->sec->data->d_buf + rela2->addend, sizeof(toc_data2)); + if (!toc_data2) + ERROR(".toc entry not found %s + %lx", rela2->sym->name, rela2->addend); + } + + if (!rela_toc1 && !rela_toc2) + return toc_data1 == toc_data2; + + if (!rela_toc1 || !rela_toc2) + return false; + + if (rela_toc1->string) + return rela_toc2->string && !strcmp(rela_toc1->string, rela_toc2->string); + + if (rela_toc1->addend != rela_toc2->addend) + return false; + + return !kpatch_mangled_strcmp(rela_toc1->sym->name, rela_toc2->sym->name); +} + +static void kpatch_compare_correlated_rela_section(struct section *relasec) +{ + struct rela *rela1, *rela2 = NULL; + + /* + * On ppc64le, don't compare the .rela.toc section. The .toc and + * .rela.toc sections are included as standard elements. + */ + if (!strcmp(relasec->name, ".rela.toc")) { + relasec->status = SAME; + return; + } + + rela2 = list_entry(relasec->twin->relas.next, struct rela, list); + list_for_each_entry(rela1, &relasec->relas, list) { + if (rela_equal(rela1, rela2)) { + rela2 = list_entry(rela2->list.next, struct rela, list); + continue; + } + relasec->status = CHANGED; + return; + } + + relasec->status = SAME; +} + +static void kpatch_compare_correlated_nonrela_section(struct section *sec) +{ + struct section *sec1 = sec, *sec2 = sec->twin; + + if (sec1->sh.sh_type != SHT_NOBITS && + memcmp(sec1->data->d_buf, sec2->data->d_buf, sec1->data->d_size)) + sec->status = CHANGED; + else + sec->status = SAME; +} + +static void kpatch_compare_correlated_section(struct section *sec) +{ + struct section *sec1 = sec, *sec2 = sec->twin; + + /* Compare section headers (must match or fatal) */ + if (sec1->sh.sh_type != sec2->sh.sh_type || + sec1->sh.sh_flags != sec2->sh.sh_flags || + sec1->sh.sh_entsize != sec2->sh.sh_entsize || + (sec1->sh.sh_addralign != sec2->sh.sh_addralign && + !is_text_section(sec1))) + DIFF_FATAL("%s section header details differ from %s", sec1->name, sec2->name); + + /* Short circuit for mcount sections, we rebuild regardless */ + if (!strcmp(sec->name, ".rela__mcount_loc") || + !strcmp(sec->name, "__mcount_loc")) { + sec->status = SAME; + goto out; + } + + if (sec1->sh.sh_size != sec2->sh.sh_size || + sec1->data->d_size != sec2->data->d_size || + (sec1->rela && !sec2->rela) || + (sec2->rela && !sec1->rela)) { + sec->status = CHANGED; + goto out; + } + + if (is_rela_section(sec)) + kpatch_compare_correlated_rela_section(sec); + else + kpatch_compare_correlated_nonrela_section(sec); +out: + if (sec->status == CHANGED) + log_debug("section %s has changed\n", sec->name); +} + +/* + * This function is not comprehensive, i.e. it doesn't detect immediate loads + * to *all* registers. It only detects those which have been found in the wild + * to be involved in the load of a __LINE__ immediate. If we miss some, that's + * ok, we'll find them later when someone notices a function falsely being + * reported as changed ;-) + * + * Right now we're only checking immediate loads to the registers corresponding + * to function arguments 2 and 3 for each respective arch's calling convention. + * (Argument 1 is typically the printf format string). Eventually we might + * want to consider just checking *all* registers which could conceivably be + * used as function arguments. But in practice, arg2 and arg3 seem to be the + * main ones, so for now, take a more conservative approach at the risk of + * failing to detect some of the more obscure __LINE__-only changed functions. + */ +static bool insn_is_load_immediate(struct kpatch_elf *kelf, void *addr) +{ + unsigned char *insn = addr; + + switch(kelf->arch) { + + case X86_64: + /* arg2: mov $imm, %esi */ + if (insn[0] == 0xbe) + return true; + + /* arg3: mov $imm, %edx */ + if (insn[0] == 0xba) + return true; + + break; + + case PPC64: + /* + * ppc64le insns are LE-encoded: + * + * 0a 00 80 38 li r4,10 + * 47 14 a0 38 li r5,5191 + */ + + /* arg2: li r4, imm */ + if (insn[3] == 0x38 && insn[2] == 0x80) + return true; + + /* arg3: li r5, imm */ + if (insn[3] == 0x38 && insn[2] == 0xa0) + return true; + + break; + + case S390: + /* arg2: lghi %r3, imm */ + if (insn[0] == 0xa7 && insn[1] == 0x39) + return true; + + /* arg3: lghi %r4, imm */ + if (insn[0] == 0xa7 && insn[1] == 0x49) + return true; + + break; + + default: + ERROR("unsupported arch"); + } + + return false; +} + +/* + * Determine if a section has changed only due to a __LINE__ number change, + * e.g. a WARN() or might_sleep() macro's embedding of the line number into an + * instruction operand. + * + * Warning: Hackery lies herein. It's hopefully justified by the fact that + * this issue is very common. + * + * Example WARN(): + * + * 938: be 70 00 00 00 mov $0x70,%esi + * 93d: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + * 940: R_X86_64_32S .rodata.tcp_conn_request.str1.8+0x88 + * 944: c6 05 00 00 00 00 01 movb $0x1,0x0(%rip) # 94b + * 946: R_X86_64_PC32 .data.unlikely-0x1 + * 94b: e8 00 00 00 00 callq 950 + * 94c: R_X86_64_PC32 warn_slowpath_null-0x4 + * + * Example might_sleep: + * + * 50f: be f7 01 00 00 mov $0x1f7,%esi + * 514: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + * 517: R_X86_64_32S .rodata.do_select.str1.8+0x98 + * 51b: e8 00 00 00 00 callq 520 + * 51c: R_X86_64_PC32 ___might_sleep-0x4 + */ +static bool kpatch_line_macro_change_only(struct kpatch_elf *kelf, + struct section *sec) +{ + unsigned long offset, insn1_len, insn2_len; + void *data1, *data2, *insn1, *insn2; + struct rela *r, *rela; + bool found, found_any = false; + + if (sec->status != CHANGED || + is_rela_section(sec) || + !is_text_section(sec) || + sec->sh.sh_size != sec->twin->sh.sh_size || + !sec->rela || + sec->rela->status != SAME) + return false; + + data1 = sec->twin->data->d_buf; + data2 = sec->data->d_buf; + for (offset = 0; offset < sec->sh.sh_size; offset += insn1_len) { + + insn1 = data1 + offset; + insn2 = data2 + offset; + + insn1_len = insn_length(kelf, insn1); + insn2_len = insn_length(kelf, insn2); + + if (!insn1_len || !insn2_len) + ERROR("can't decode instruction in section %s at offset 0x%lx", + sec->name, offset); + + if (insn1_len != insn2_len) + return false; + + if (!memcmp(insn1, insn2, insn1_len)) + continue; + + /* + * Here we found a difference between two instructions of the + * same length. Only ignore the change if: + * + * a) the instructions match a known pattern of a '__LINE__' + * macro immediate value which was embedded in the + * instruction; and + * + * b) the instructions are followed by certain expected + * relocations. + */ + + if (!insn_is_load_immediate(kelf, insn1) || + !insn_is_load_immediate(kelf, insn2)) + return false; + + found = false; + list_for_each_entry(r, &sec->rela->relas, list) { + + if (r->offset < offset + insn1_len) + continue; + + rela = toc_rela(r); + if (!rela) + continue; + + if (rela->string) + continue; + + if (!strncmp(rela->sym->name, "__warned.", 9) || + !strncmp(rela->sym->name, "__already_done.", 15) || + !strncmp(rela->sym->name, "__func__.", 9)) + continue; + + if (!strncmp(rela->sym->name, "warn_slowpath_", 14) || + !strcmp(rela->sym->name, "__warn_printk") || + !strcmp(rela->sym->name, "__might_sleep") || + !strcmp(rela->sym->name, "___might_sleep") || + !strcmp(rela->sym->name, "__might_fault") || + !strcmp(rela->sym->name, "printk") || + !strcmp(rela->sym->name, "_printk") || + !strcmp(rela->sym->name, "lockdep_rcu_suspicious") || + !strcmp(rela->sym->name, "__btrfs_abort_transaction") || + !strcmp(rela->sym->name, "__btrfs_handle_fs_error") || + !strcmp(rela->sym->name, "__btrfs_panic")) { + found = true; + break; + } + return false; + } + if (!found) + return false; + + found_any = true; + } + + if (!found_any) + ERROR("no instruction changes detected for changed section %s", + sec->name); + + return true; +} + +/* + * Child functions with "*.cold" names don't have _fentry_ calls, but "*.part", + * often do. In the later case, it is not necessary to include the parent + * in the output object when the child function has changed. + */ +static bool kpatch_changed_child_needs_parent_profiling(struct symbol *sym) +{ + struct symbol *child; + + list_for_each_entry(child, &sym->children, subfunction_node) { + if (child->has_func_profiling) + continue; + if (child->sec->status == CHANGED || + kpatch_changed_child_needs_parent_profiling(child)) + return true; + } + + return false; +} + +static void kpatch_compare_sections(struct kpatch_elf *kelf) +{ + struct section *sec; + struct list_head *seclist = &kelf->sections; + + /* compare all sections */ + list_for_each_entry(sec, seclist, list) { + if (sec->twin) + kpatch_compare_correlated_section(sec); + else + sec->status = NEW; + } + + /* exclude WARN-only, might_sleep changes */ + list_for_each_entry(sec, seclist, list) { + if (kpatch_line_macro_change_only(kelf, sec)) { + log_debug("reverting macro / line number section %s status to SAME\n", + sec->name); + sec->status = SAME; + } + } + + /* sync symbol status */ + list_for_each_entry(sec, seclist, list) { + if (is_rela_section(sec)) { + if (sec->base->sym && sec->base->sym->status != CHANGED) + sec->base->sym->status = sec->status; + } else { + struct symbol *sym = sec->sym; + + if (sym && sym->status != CHANGED) + sym->status = sec->status; + + if (sym && sym->status == SAME && + kpatch_changed_child_needs_parent_profiling(sym)) + sym->status = CHANGED; + } + } +} + +static enum subsection kpatch_subsection_type(struct section *sec) +{ + if (!strncmp(sec->name, ".text.unlikely.", 15)) + return SUBSECTION_UNLIKELY; + + if (!strncmp(sec->name, ".text.hot.", 10)) + return SUBSECTION_HOT; + + return SUBSECTION_NORMAL; +} + +static bool kpatch_subsection_changed(struct section *sec1, struct section *sec2) +{ + return kpatch_subsection_type(sec1) != kpatch_subsection_type(sec2); +} + +static struct symbol *kpatch_get_correlated_parent(struct symbol *sym) +{ + while (sym->parent && !sym->parent->twin) + sym = sym->parent; + return sym->parent; +} + +static void kpatch_compare_correlated_symbol(struct symbol *sym) +{ + struct symbol *sym1 = sym, *sym2 = sym->twin; + + if (sym1->sym.st_info != sym2->sym.st_info || + (sym1->sec && !sym2->sec) || + (sym2->sec && !sym1->sec)) + DIFF_FATAL("symbol info mismatch: %s", sym1->name); + + /* + * If two symbols are correlated but their sections are not, then the + * symbol has changed sections. This is only allowed if the symbol is + * moving out of an ignored section, or moving between normal/hot/unlikely + * subsections. + */ + if (sym1->sec && sym2->sec && sym1->sec->twin != sym2->sec) { + if ((sym2->sec->twin && sym2->sec->twin->ignore) || + kpatch_subsection_changed(sym1->sec, sym2->sec)) + sym->status = CHANGED; + else + DIFF_FATAL("symbol changed sections: %s", sym1->name); + } + + if (sym1->type == STT_OBJECT && + sym1->sym.st_size != sym2->sym.st_size) + DIFF_FATAL("object size mismatch: %s", sym1->name); + + if (sym1->sym.st_shndx == SHN_UNDEF || + sym1->sym.st_shndx == SHN_ABS) + sym1->status = SAME; + + /* + * The status of LOCAL symbols is dependent on the status of their + * matching section and is set during section comparison. + */ +} + +static void kpatch_compare_symbols(struct list_head *symlist) +{ + struct symbol *sym; + + list_for_each_entry(sym, symlist, list) { + if (sym->twin) + kpatch_compare_correlated_symbol(sym); + else + sym->status = NEW; + + log_debug("symbol %s is %s\n", sym->name, status_str(sym->status)); + } +} + +#define CORRELATE_ELEMENT(_e1_, _e2_, kindstr) \ +do { \ + typeof(_e1_) e1 = (_e1_); \ + typeof(_e2_) e2 = (_e2_); \ + e1->twin = e2; \ + e2->twin = e1; \ + /* set initial status, might change */ \ + e1->status = e2->status = SAME; \ + if (strcmp(e1->name, e2->name)) { \ + /* Rename mangled element */ \ + log_debug("renaming %s %s to %s\n", \ + kindstr, e2->name, e1->name); \ + e2->name = strdup(e1->name); \ + if (!e2->name) \ + ERROR("strdup"); \ + } \ +} while (0) + +#define UNCORRELATE_ELEMENT(_elem_) \ +do { \ + typeof(_elem_) elem = (_elem_); \ + elem->twin->twin = NULL; \ + elem->twin = NULL; \ +} while (0) + +static void __kpatch_correlate_section(struct section *sec_orig, + struct section *sec_patched) +{ + CORRELATE_ELEMENT(sec_orig, sec_patched, "section"); +} + +static void kpatch_correlate_symbol(struct symbol *sym_orig, + struct symbol *sym_patched) +{ + CORRELATE_ELEMENT(sym_orig, sym_patched, "symbol"); + if (sym_orig->lookup_table_file_sym && !sym_patched->lookup_table_file_sym) + sym_patched->lookup_table_file_sym = sym_orig->lookup_table_file_sym; +} + +static void kpatch_correlate_static_local(struct symbol *sym_orig, + struct symbol *sym_patched) +{ + CORRELATE_ELEMENT(sym_orig, sym_patched, "static local"); +} + +static void kpatch_correlate_section(struct section *sec_orig, + struct section *sec_patched) +{ + __kpatch_correlate_section(sec_orig, sec_patched); + + if (is_rela_section(sec_orig)) { + __kpatch_correlate_section(sec_orig->base, sec_patched->base); + sec_orig = sec_orig->base; + sec_patched = sec_patched->base; + } else if (sec_orig->rela && sec_patched->rela) { + __kpatch_correlate_section(sec_orig->rela, sec_patched->rela); + } + + if (sec_orig->secsym) + kpatch_correlate_symbol(sec_orig->secsym, sec_patched->secsym); + if (sec_orig->sym) + kpatch_correlate_symbol(sec_orig->sym, sec_patched->sym); +} + +static void kpatch_correlate_sections(struct list_head *seclist_orig, + struct list_head *seclist_patched) +{ + struct section *sec_orig, *sec_patched; + + list_for_each_entry(sec_orig, seclist_orig, list) { + if (sec_orig->twin) + continue; + list_for_each_entry(sec_patched, seclist_patched, list) { + if (kpatch_mangled_strcmp(sec_orig->name, sec_patched->name) || + sec_patched->twin) + continue; + + if (is_special_static(is_rela_section(sec_orig) ? + sec_orig->base->secsym : + sec_orig->secsym)) + continue; + + /* + * Group sections must match exactly to be correlated. + * Changed group sections are currently not supported. + */ + if (sec_orig->sh.sh_type == SHT_GROUP) { + if (sec_orig->data->d_size != sec_patched->data->d_size) + continue; + if (memcmp(sec_orig->data->d_buf, sec_patched->data->d_buf, + sec_orig->data->d_size)) + continue; + } + + kpatch_correlate_section(sec_orig, sec_patched); + break; + } + } +} + +static void kpatch_correlate_symbols(struct list_head *symlist_orig, + struct list_head *symlist_patched) +{ + struct symbol *sym_orig, *sym_patched; + + list_for_each_entry(sym_orig, symlist_orig, list) { + if (sym_orig->twin) + continue; + list_for_each_entry(sym_patched, symlist_patched, list) { + if (kpatch_mangled_strcmp(sym_orig->name, sym_patched->name) || + sym_orig->type != sym_patched->type || sym_patched->twin) + continue; + + if (is_special_static(sym_orig)) + continue; + + /* + * The .LCx symbols point to string literals in + * '.rodata..str1.*' sections. They get included + * in kpatch_include_standard_elements(). + */ + if (sym_orig->type == STT_NOTYPE && + !strncmp(sym_orig->name, ".LC", 3)) + continue; + + /* group section symbols must have correlated sections */ + if (sym_orig->sec && + sym_orig->sec->sh.sh_type == SHT_GROUP && + sym_orig->sec->twin != sym_patched->sec) + continue; + + kpatch_correlate_symbol(sym_orig, sym_patched); + break; + } + } +} + +static void kpatch_compare_elf_headers(Elf *elf_orig, Elf *elf_patched) +{ + GElf_Ehdr ehdr_orig, ehdr_patched; + + if (!gelf_getehdr(elf_orig, &ehdr_orig)) + ERROR("gelf_getehdr"); + + if (!gelf_getehdr(elf_patched, &ehdr_patched)) + ERROR("gelf_getehdr"); + + if (memcmp(ehdr_orig.e_ident, ehdr_patched.e_ident, EI_NIDENT) || + ehdr_orig.e_type != ehdr_patched.e_type || + ehdr_orig.e_machine != ehdr_patched.e_machine || + ehdr_orig.e_version != ehdr_patched.e_version || + ehdr_orig.e_entry != ehdr_patched.e_entry || + ehdr_orig.e_phoff != ehdr_patched.e_phoff || + ehdr_orig.e_flags != ehdr_patched.e_flags || + ehdr_orig.e_ehsize != ehdr_patched.e_ehsize || + ehdr_orig.e_phentsize != ehdr_patched.e_phentsize || + ehdr_orig.e_shentsize != ehdr_patched.e_shentsize) + DIFF_FATAL("ELF headers differ"); +} + +static void kpatch_check_program_headers(Elf *elf) +{ + size_t ph_nr; + + if (elf_getphdrnum(elf, &ph_nr)) + ERROR("elf_getphdrnum"); + + if (ph_nr != 0) + DIFF_FATAL("ELF contains program header"); +} + +static void kpatch_mark_grouped_sections(struct kpatch_elf *kelf) +{ + struct section *groupsec, *sec; + unsigned int *data, *end; + + list_for_each_entry(groupsec, &kelf->sections, list) { + if (groupsec->sh.sh_type != SHT_GROUP) + continue; + data = groupsec->data->d_buf; + end = groupsec->data->d_buf + groupsec->data->d_size; + data++; /* skip first flag word (e.g. GRP_COMDAT) */ + while (data < end) { + sec = find_section_by_index(&kelf->sections, *data); + if (!sec) + ERROR("group section not found"); + sec->grouped = 1; + log_debug("marking section %s (%d) as grouped\n", + sec->name, sec->index); + data++; + } + } +} + +static char *kpatch_section_function_name(struct section *sec) +{ + if (is_rela_section(sec)) + sec = sec->base; + return sec->sym ? sec->sym->name : sec->name; +} + +static struct symbol *kpatch_find_uncorrelated_rela(struct section *relasec, + struct symbol *sym) +{ + struct rela *rela, *rela_toc; + + /* find the patched object's corresponding variable */ + list_for_each_entry(rela, &relasec->relas, list) { + struct symbol *patched_sym; + + rela_toc = toc_rela(rela); + if (!rela_toc) + continue; /* skip toc constants */ + + patched_sym = rela_toc->sym; + + if (patched_sym->twin) + continue; + + if (sym->type != patched_sym->type || + (sym->type == STT_OBJECT && + sym->sym.st_size != patched_sym->sym.st_size)) + continue; + + if (kpatch_mangled_strcmp(patched_sym->name, sym->name)) + continue; + + return patched_sym; + } + + return NULL; +} + +static struct symbol *kpatch_find_static_twin_in_children(struct symbol *parent, + struct symbol *sym) +{ + struct symbol *child; + + list_for_each_entry(child, &parent->children, subfunction_node) { + struct symbol *res; + + /* Only look in children whose rela section differ from the parent's */ + if (child->sec->rela == parent->sec->rela || !child->sec->rela) + continue; + + res = kpatch_find_uncorrelated_rela(child->sec->rela, sym); + /* Need to go deeper */ + if (!res) + res = kpatch_find_static_twin_in_children(child, sym); + if (res != NULL) + return res; + } + + return NULL; +} + +/* + * Given a static local variable symbol and a rela section which references it + * in the base object, find a corresponding usage of a similarly named symbol + * in the patched object. + */ +static struct symbol *kpatch_find_static_twin(struct section *relasec, + struct symbol *sym) +{ + struct symbol *res; + + if (!relasec->twin && relasec->base->sym) { + struct symbol *parent = NULL; + + /* + * The static twin might have been in a .part. symbol in the + * original object that got removed in the patched object. + */ + parent = kpatch_get_correlated_parent(relasec->base->sym); + if (parent) + relasec = parent->sec->rela; + + } + + if (!relasec->twin) + return NULL; + + res = kpatch_find_uncorrelated_rela(relasec->twin, sym); + if (res != NULL) + return res; + + /* Look if reference might have moved to child functions' sections */ + if (relasec->twin->base->sym) + return kpatch_find_static_twin_in_children(relasec->twin->base->sym, + sym); + + return NULL; +} + +static bool kpatch_is_normal_static_local(struct symbol *sym) +{ + if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL) + return false; + + if (!strncmp(sym->name, ".L", 2)) + return false; + + if (!strchr(sym->name, '.')) + return false; + + if (is_special_static(sym)) + return false; + + return true; +} + +static struct rela *kpatch_find_static_twin_ref(struct section *relasec, + struct symbol *sym) +{ + struct rela *rela; + + list_for_each_entry(rela, &relasec->relas, list) { + if (rela->sym == sym->twin) + return rela; + } + + /* Reference to static variable might have moved to child function section */ + if (relasec->base->sym) { + struct symbol *parent = relasec->base->sym; + struct symbol *child; + + list_for_each_entry(child, &parent->children, subfunction_node) { + /* Only look in children whose rela section differ from the parent's */ + if (child->sec->rela == parent->sec->rela || + !child->sec->rela) + continue; + + rela = kpatch_find_static_twin_ref(child->sec->rela, sym); + if (rela) + return rela; + } + } + + return NULL; +} + +/* + * gcc renames static local variables by appending a period and a number. For + * example, __foo could be renamed to __foo.31452. Unfortunately this number + * can arbitrarily change. Correlate them by comparing which functions + * reference them, and rename the patched symbols to match the base symbol + * names. + * + * Some surprising facts about static local variable symbols: + * + * - It's possible for multiple functions to use the same + * static local variable if the variable is defined in an + * inlined function. + * + * - It's also possible for multiple static local variables + * with the same name to be used in the same function if they + * have different scopes. (We have to assume that in such + * cases, the order in which they're referenced remains the + * same between the orig and patched objects, as there's no + * other way to distinguish them.) + * + * - Static locals are usually referenced by functions, but + * they can occasionally be referenced by data sections as + * well. + */ +static void kpatch_correlate_static_local_variables(struct kpatch_elf *orig, + struct kpatch_elf *patched) +{ + struct symbol *sym, *patched_sym; + struct section *relasec; + struct rela *rela; + int bundled, patched_bundled; + + /* + * First undo the correlations for all static locals. Two static + * locals can have the same numbered suffix in the orig and patched + * objects by coincidence. + */ + list_for_each_entry(sym, &orig->symbols, list) { + + if (!kpatch_is_normal_static_local(sym)) + continue; + + if (sym->twin) + UNCORRELATE_ELEMENT(sym); + + bundled = sym == sym->sec->sym; + if (bundled && sym->sec->twin) { + UNCORRELATE_ELEMENT(sym->sec); + + if (sym->sec->secsym) + UNCORRELATE_ELEMENT(sym->sec->secsym); + + if (sym->sec->rela) + UNCORRELATE_ELEMENT(sym->sec->rela); + } + } + + /* + * Do the correlations: for each section reference to a static local, + * look for a corresponding reference in the section's twin. + */ + list_for_each_entry(relasec, &orig->sections, list) { + + if (!is_rela_section(relasec) || + is_debug_section(relasec) || + !strcmp(relasec->name, ".rela.toc")) + continue; + + list_for_each_entry(rela, &relasec->relas, list) { + + if (!toc_rela(rela)) + continue; /* skip toc constants */ + sym = toc_rela(rela)->sym; + + if (!kpatch_is_normal_static_local(sym)) + continue; + + if (sym->twin) + continue; + + bundled = sym == sym->sec->sym; + if (bundled && sym->sec == relasec->base) { + /* + * A rare case where a static local data + * structure references itself. There's no + * reliable way to correlate this. Hopefully + * there's another reference to the symbol + * somewhere that can be used. + */ + log_debug("can't correlate static local %s's reference to itself\n", + sym->name); + continue; + } + + patched_sym = kpatch_find_static_twin(relasec, sym); + if (!patched_sym) + DIFF_FATAL("reference to static local variable %s in %s was removed", + sym->name, + kpatch_section_function_name(relasec)); + + patched_bundled = patched_sym == patched_sym->sec->sym; + if (bundled != patched_bundled) + ERROR("bundle mismatch for symbol %s", sym->name); + if (!bundled && sym->sec->twin != patched_sym->sec) + ERROR("sections %s and %s aren't correlated for symbol %s", + sym->sec->name, patched_sym->sec->name, sym->name); + + kpatch_correlate_static_local(sym, patched_sym); + + if (bundled) + kpatch_correlate_section(sym->sec, patched_sym->sec); + } + } + + /* + * Make sure that: + * + * 1. all the orig object's referenced static locals have been + * correlated; and + * + * 2. each reference to a static local in the orig object has a + * corresponding reference in the patched object (because a static + * local can be referenced by more than one section). + */ + list_for_each_entry(relasec, &orig->sections, list) { + + if (!is_rela_section(relasec) || + is_debug_section(relasec)) + continue; + + list_for_each_entry(rela, &relasec->relas, list) { + struct section *target_sec = relasec; + + sym = rela->sym; + if (!kpatch_is_normal_static_local(sym)) + continue; + + if (!relasec->twin && relasec->base->sym) { + struct symbol *parent = NULL; + + parent = kpatch_get_correlated_parent(relasec->base->sym); + if (parent) + target_sec = parent->sec->rela; + } + + if (!sym->twin || !target_sec->twin) + DIFF_FATAL("reference to static local variable %s in %s was removed", + sym->name, + kpatch_section_function_name(target_sec)); + + if (!kpatch_find_static_twin_ref(target_sec->twin, sym)) + DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it", + sym->name, sym->twin->name, + kpatch_section_function_name(target_sec->twin)); + } + } + + /* + * Now go through the patched object and look for any uncorrelated + * static locals to see if we need to print any warnings about new + * variables. + */ + list_for_each_entry(relasec, &patched->sections, list) { + + if (!is_rela_section(relasec) || + is_debug_section(relasec)) + continue; + + list_for_each_entry(rela, &relasec->relas, list) { + + sym = rela->sym; + if (!kpatch_is_normal_static_local(sym)) + continue; + + if (sym->twin) + continue; + + log_normal("WARNING: unable to correlate static local variable %s used by %s, assuming variable is new\n", + sym->name, + kpatch_section_function_name(relasec)); + return; + } + } +} + +static void kpatch_correlate_elfs(struct kpatch_elf *kelf_orig, + struct kpatch_elf *kelf_patched) +{ + kpatch_correlate_sections(&kelf_orig->sections, &kelf_patched->sections); + kpatch_correlate_symbols(&kelf_orig->symbols, &kelf_patched->symbols); +} + +static void kpatch_compare_correlated_elements(struct kpatch_elf *kelf) +{ + /* lists are already correlated at this point */ + kpatch_compare_sections(kelf); + kpatch_compare_symbols(&kelf->symbols); +} + +static bool is_callback_section(struct section *sec) { + + static char *callback_sections[] = { + ".kpatch.callbacks.pre_patch", + ".kpatch.callbacks.post_patch", + ".kpatch.callbacks.pre_unpatch", + ".kpatch.callbacks.post_unpatch", + ".rela.kpatch.callbacks.pre_patch", + ".rela.kpatch.callbacks.post_patch", + ".rela.kpatch.callbacks.pre_unpatch", + ".rela.kpatch.callbacks.post_unpatch", + NULL, + }; + char **callback_sec; + + for (callback_sec = callback_sections; *callback_sec; callback_sec++) + if (!strcmp(sec->name, *callback_sec)) + return true; + + return false; +} + +/* + * Mangle the relas a little. The compiler will sometimes use section symbols + * to reference local objects and functions rather than the object or function + * symbols themselves. We substitute the object/function symbols for the + * section symbol in this case so that the relas can be properly correlated and + * so that the existing object/function in vmlinux can be linked to. + */ +static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) +{ + struct section *relasec; + struct rela *rela; + struct symbol *sym; + long target_off; + bool found = false; + + log_debug("\n"); + + list_for_each_entry(relasec, &kelf->sections, list) { + if (!is_rela_section(relasec) || is_debug_section(relasec)) + continue; + + list_for_each_entry(rela, &relasec->relas, list) { + + if (rela->sym->type != STT_SECTION || !rela->sym->sec) + continue; + + /* + * These sections don't have symbols associated with + * them: + */ + if (!strcmp(rela->sym->name, ".toc") || + !strcmp(rela->sym->name, ".fixup") || + !strcmp(rela->sym->name, ".altinstr_replacement") || + !strcmp(rela->sym->name, ".altinstr_aux") || + !strcmp(rela->sym->name, ".text..refcount")) + continue; + + /* + * Replace references to bundled sections with their + * symbols. + */ + if (rela->sym->sec->sym) { + rela->sym = rela->sym->sec->sym; + + /* + * On ppc64le with GCC6+, even with + * -ffunction-sections, the function symbol + * starts 8 bytes past the beginning of the + * section, because the .TOC pointer is at the + * beginning, right before the code. So even + * though the symbol is bundled, we can't + * assume it's at offset 0 in the section. + */ + rela->addend -= rela->sym->sym.st_value; + + continue; + } + + target_off = rela_target_offset(kelf, relasec, rela); + + /* + * Attempt to replace references to unbundled sections + * with their symbols. + */ + list_for_each_entry(sym, &kelf->symbols, list) { + long start, end; + + if (sym->type == STT_SECTION || + sym->sec != rela->sym->sec) + continue; + + start = sym->sym.st_value; + end = sym->sym.st_value + sym->sym.st_size; + + if (is_text_section(relasec->base) && + !is_text_section(sym->sec) && + rela->type == R_X86_64_32S && + rela->addend == (long)sym->sec->sh.sh_size && + end == (long)sym->sec->sh.sh_size) { + + /* + * A special case where gcc needs a + * pointer to the address at the end of + * a data section. + * + * This is usually used with a compare + * instruction to determine when to end + * a loop. The code doesn't actually + * dereference the pointer so this is + * "normal" and we just replace the + * section reference with a reference + * to the last symbol in the section. + * + * Note that this only catches the + * issue when it happens at the end of + * a section. It can also happen in + * the middle of a section. In that + * case, the wrong symbol will be + * associated with the reference. But + * that's ok because: + * + * 1) This situation only occurs when + * gcc is trying to get the address + * of the symbol, not the contents + * of its data; and + * + * 2) Because kpatch doesn't allow data + * sections to change, + * &(var1+sizeof(var1)) will always + * be the same as &var2. + */ + } else if (target_off == start && target_off == end) { + + /* + * Allow replacement for references to + * empty symbols. + */ + + } else if (target_off < start || target_off >= end) + continue; + + log_debug("%s: replacing %s+%ld reference with %s+%ld\n", + relasec->name, + rela->sym->name, rela->addend, + sym->name, rela->addend - start); + found = true; + + rela->sym = sym; + rela->addend -= start; + break; + } + + if (!found && !is_string_literal_section(rela->sym->sec) && + strncmp(rela->sym->name, ".rodata", 7)) { + ERROR("%s+0x%x: can't find replacement symbol for %s+%ld reference", + relasec->base->name, rela->offset, rela->sym->name, rela->addend); + } + } + } + log_debug("\n"); +} + +static void kpatch_check_func_profiling_calls(struct kpatch_elf *kelf) +{ + struct symbol *sym; + int errs = 0; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status != CHANGED || + (sym->parent && sym->parent->status == CHANGED)) + continue; + if (!sym->twin->has_func_profiling) { + log_normal("function %s has no fentry/mcount call, unable to patch\n", + sym->name); + errs++; + } + } + + if (errs) + DIFF_FATAL("%d function(s) can not be patched", errs); +} + +static void kpatch_verify_patchability(struct kpatch_elf *kelf) +{ + struct section *sec; + int errs = 0; + + list_for_each_entry(sec, &kelf->sections, list) { + if (sec->status == CHANGED && !sec->include) { + log_normal("changed section %s not selected for inclusion\n", + sec->name); + errs++; + } + + if (sec->status != SAME && sec->grouped) { + log_normal("changed section %s is part of a section group\n", + sec->name); + errs++; + } + + if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) { + log_normal("new/changed group sections are not supported\n"); + errs++; + } + + /* + * ensure we aren't including .data.* or .bss.* + * (.data.unlikely and .data.once is ok b/c it only has __warned vars) + */ + if (sec->include && sec->status != NEW && + (!strncmp(sec->name, ".data", 5) || !strncmp(sec->name, ".bss", 4)) && + (strcmp(sec->name, ".data.unlikely") && strcmp(sec->name, ".data.once"))) { + log_normal("data section %s selected for inclusion\n", + sec->name); + errs++; + } + } + + if (errs) + DIFF_FATAL("%d unsupported section change(s)", errs); +} + +static void kpatch_include_symbol(struct symbol *sym); + +static void kpatch_include_section(struct section *sec) +{ + struct rela *rela; + + /* Include the section and its section symbol */ + if (sec->include) + return; + sec->include = 1; + if (sec->secsym) + sec->secsym->include = 1; + + /* + * Include the section's rela section and then recursively include the + * symbols needed by its relas. + */ + if (!sec->rela) + return; + sec->rela->include = 1; + list_for_each_entry(rela, &sec->rela->relas, list) + kpatch_include_symbol(rela->sym); +} + +static void kpatch_include_symbol(struct symbol *sym) +{ + /* + * This function is called recursively from kpatch_include_section(). + * Make sure we don't get into an endless loop. + */ + if (sym->include) + return; + + /* + * The symbol gets included even if its section isn't needed, as it + * might be needed: either permanently for a rela, or temporarily for + * the later creation of a dynrela. + */ + sym->include = 1; + + /* + * For a function/object symbol, if it has a section, we only need to + * include the section if it has changed. Otherwise the symbol will be + * used by relas/dynrelas to link to the real symbol externally. + * + * For section symbols, we always include the section because + * references to them can't otherwise be resolved externally. + */ + if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME)) + kpatch_include_section(sym->sec); +} + +static void kpatch_include_standard_elements(struct kpatch_elf *kelf) +{ + struct section *sec; + struct symbol *sym; + + list_for_each_entry(sec, &kelf->sections, list) { + /* + * Include the following sections even if they haven't changed. + * + * Notes about some of the more interesting sections: + * + * - With -fdata-sections, .rodata is only used for: + * + * switch jump tables; + * KASAN data (with KASAN enabled, which is rare); and + * an ugly hack in vmx_vcpu_run(). + * + * Those data are all local to the functions which use them. + * So it's safe to include .rodata. + * + * - On ppc64le, the .toc section is used for all data + * accesses. + * + * Note that if any of these sections have rela sections, they + * will also be included in their entirety. That may result in + * some extra (unused) dynrelas getting created, which should + * be harmless. + */ + if (!strcmp(sec->name, ".shstrtab") || + !strcmp(sec->name, ".strtab") || + !strcmp(sec->name, ".symtab") || + !strcmp(sec->name, ".toc") || + !strcmp(sec->name, ".rodata") || + is_string_literal_section(sec)) { + kpatch_include_section(sec); + } + } + + list_for_each_entry(sym, &kelf->symbols, list) + if (sym->sec && is_string_literal_section(sym->sec)) + sym->include = 1; + + /* include the NULL symbol */ + list_entry(kelf->symbols.next, struct symbol, list)->include = 1; +} + +static bool kpatch_include_callback_elements(struct kpatch_elf *kelf) +{ + struct section *sec; + struct symbol *sym; + struct rela *rela; + bool found = false; + + /* include load/unload sections */ + list_for_each_entry(sec, &kelf->sections, list) { + if (!is_callback_section(sec)) + continue; + + sec->include = 1; + found = true; + if (is_rela_section(sec)) { + /* include callback dependencies */ + rela = list_entry(sec->relas.next, struct rela, list); + sym = rela->sym; + log_normal("found callback: %s\n",sym->name); + kpatch_include_symbol(sym); + } else if (sec->secsym) { + sec->secsym->include = 1; + } + } + + /* Strip temporary structure symbols used by the callback macros. */ + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type == STT_OBJECT && sym->sec && + is_callback_section(sym->sec)) + sym->include = 0; + } + + return found; +} + +static void kpatch_include_force_elements(struct kpatch_elf *kelf) +{ + struct section *sec; + struct symbol *sym; + struct rela *rela; + + /* include force sections */ + list_for_each_entry(sec, &kelf->sections, list) { + if (!strcmp(sec->name, ".kpatch.force") || + !strcmp(sec->name, ".rela.kpatch.force")) { + sec->include = 1; + if (!is_rela_section(sec)) { + /* .kpatch.force */ + if (sec->secsym) + sec->secsym->include = 1; + continue; + } + /* .rela.kpatch.force */ + list_for_each_entry(rela, &sec->relas, list) + log_normal("function '%s' marked with KPATCH_FORCE_UNSAFE!\n", + rela->sym->name); + } + } + + /* strip temporary global kpatch_force_func_* symbols */ + list_for_each_entry(sym, &kelf->symbols, list) + if (!strncmp(sym->name, "__kpatch_force_func_", + strlen("__kpatch_force_func_"))) + sym->include = 0; +} + +static int kpatch_include_new_globals(struct kpatch_elf *kelf) +{ + struct symbol *sym; + int nr = 0; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->bind == STB_GLOBAL && sym->sec && + sym->status == NEW) { + kpatch_include_symbol(sym); + nr++; + } + } + + return nr; +} + +static int kpatch_include_changed_functions(struct kpatch_elf *kelf) +{ + struct symbol *sym; + int changed_nr = 0; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->status == CHANGED && + sym->type == STT_FUNC) { + changed_nr++; + kpatch_include_symbol(sym); + } + + if (sym->type == STT_FILE) + sym->include = 1; + } + + return changed_nr; +} + +static void kpatch_print_changes(struct kpatch_elf *kelf) +{ + struct symbol *sym; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (!sym->include || !sym->sec || sym->type != STT_FUNC || sym->parent) + continue; + if (sym->status == NEW) + log_normal("new function: %s\n", sym->name); + else if (sym->status == CHANGED) + log_normal("changed function: %s\n", sym->name); + } +} + +static void kpatch_migrate_symbols(struct list_head *src, + struct list_head *dst, + bool (*select)(struct symbol *)) +{ + struct symbol *sym, *safe; + + list_for_each_entry_safe(sym, safe, src, list) { + if (select && !select(sym)) + continue; + + list_del(&sym->list); + list_add_tail(&sym->list, dst); + } +} + +static void kpatch_migrate_included_elements(struct kpatch_elf *kelf, struct kpatch_elf **kelfout) +{ + struct section *sec, *safesec; + struct symbol *sym, *safesym; + struct kpatch_elf *out; + + /* allocate output kelf */ + out = malloc(sizeof(*out)); + if (!out) + ERROR("malloc"); + memset(out, 0, sizeof(*out)); + out->arch = kelf->arch; + INIT_LIST_HEAD(&out->sections); + INIT_LIST_HEAD(&out->symbols); + INIT_LIST_HEAD(&out->strings); + + /* migrate included sections from kelf to out */ + list_for_each_entry_safe(sec, safesec, &kelf->sections, list) { + if (!sec->include) + continue; + list_del(&sec->list); + list_add_tail(&sec->list, &out->sections); + sec->index = 0; + if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include) + /* break link to non-included section symbol */ + sec->secsym = NULL; + } + + /* migrate included symbols from kelf to out */ + list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) { + if (!sym->include) + continue; + list_del(&sym->list); + list_add_tail(&sym->list, &out->symbols); + sym->index = 0; + sym->strip = SYMBOL_DEFAULT; + if (sym->sec && !sym->sec->include) + /* break link to non-included section */ + sym->sec = NULL; + + } + + *kelfout = out; +} + +static void kpatch_reorder_symbols(struct kpatch_elf *kelf) +{ + LIST_HEAD(symbols); + + /* migrate NULL sym */ + kpatch_migrate_symbols(&kelf->symbols, &symbols, is_null_sym); + /* migrate LOCAL FILE sym */ + kpatch_migrate_symbols(&kelf->symbols, &symbols, is_file_sym); + /* migrate LOCAL FUNC syms */ + kpatch_migrate_symbols(&kelf->symbols, &symbols, is_local_func_sym); + /* migrate all other LOCAL syms */ + kpatch_migrate_symbols(&kelf->symbols, &symbols, is_local_sym); + /* migrate all other (GLOBAL) syms */ + kpatch_migrate_symbols(&kelf->symbols, &symbols, NULL); + + list_replace(&symbols, &kelf->symbols); +} + +static int bug_table_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("BUG_STRUCT_SIZE"); + if (!str) + ERROR("BUG_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int ex_table_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("EX_STRUCT_SIZE"); + if (!str) + ERROR("EX_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int jump_table_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("JUMP_STRUCT_SIZE"); + if (!str) + ERROR("JUMP_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int printk_index_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("PRINTK_INDEX_STRUCT_SIZE"); + if (!str) + ERROR("PRINTK_INDEX_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int parainstructions_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("PARA_STRUCT_SIZE"); + if (!str) + ERROR("PARA_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int altinstructions_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("ALT_STRUCT_SIZE"); + if (!str) + ERROR("ALT_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int smp_locks_group_size(struct kpatch_elf *kelf, int offset) +{ + return 4; +} + +static int static_call_sites_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("STATIC_CALL_STRUCT_SIZE"); + if (!str) + ERROR("STATIC_CALL_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int retpoline_sites_group_size(struct kpatch_elf *kelf, int offset) +{ + return 4; +} + +static int return_sites_group_size(struct kpatch_elf *kelf, int offset) +{ + return 4; +} + +static int fixup_entry_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("FIXUP_STRUCT_SIZE"); + if (!str) + ERROR("FIXUP_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int fixup_lwsync_group_size(struct kpatch_elf *kelf, int offset) +{ + return 8; +} + +static int fixup_barrier_nospec_group_size(struct kpatch_elf *kelf, int offset) +{ + return 8; +} + +/* + * .s390_indirect_jump, .s390_indirect_call, .s390_indirect_branches, + * .s390_return_reg, .s390_return_mem contains indirect branch locations. This + * is an array of 32 bit elements. These sections could be used during runtime + * to replace the expolines with the normal indirect jump. + */ +static int s390_expolines_group_size(struct kpatch_elf *kelf, int offset) +{ + return 4; +} + +/* + * The rela groups in the .fixup section vary in size. The beginning of each + * .fixup rela group is referenced by the __ex_table section. To find the size + * of a .fixup rela group, we have to traverse the __ex_table relas. + */ +static int fixup_group_size(struct kpatch_elf *kelf, int offset) +{ + struct section *relasec; + struct rela *rela; + int found; + + relasec = find_section_by_name(&kelf->sections, ".rela__ex_table"); + if (!relasec) + ERROR("missing .rela__ex_table section"); + + /* find beginning of this group */ + found = 0; + list_for_each_entry(rela, &relasec->relas, list) { + if (!strcmp(rela->sym->name, ".fixup") && + rela->addend == offset) { + found = 1; + break; + } + } + + if (!found) + ERROR("can't find .fixup rela group at offset %d\n", offset); + + /* find beginning of next group */ + found = 0; + list_for_each_entry_continue(rela, &relasec->relas, list) { + if (!strcmp(rela->sym->name, ".fixup") && + rela->addend > offset) { + found = 1; + break; + } + } + + if (!found) { + /* last group */ + struct section *fixupsec; + fixupsec = find_section_by_name(&kelf->sections, ".fixup"); + if (!fixupsec) + ERROR("missing .fixup section"); + return (int)(fixupsec->sh.sh_size - offset); + } + + return (int)(rela->addend - offset); +} + +static struct special_section special_sections[] = { + { + .name = "__bug_table", + .arch = X86_64 | PPC64 | S390, + .group_size = bug_table_group_size, + }, + { + .name = ".fixup", + .arch = X86_64 | PPC64 | S390, + .group_size = fixup_group_size, + }, + { + .name = "__ex_table", /* must come after .fixup */ + .arch = X86_64 | PPC64 | S390, + .group_size = ex_table_group_size, + }, + { + .name = "__jump_table", + .arch = X86_64 | PPC64 | S390, + .group_size = jump_table_group_size, + }, + { + .name = ".printk_index", + .arch = X86_64 | PPC64 | S390, + .group_size = printk_index_group_size, + }, + { + .name = ".smp_locks", + .arch = X86_64, + .group_size = smp_locks_group_size, + }, + { + .name = ".parainstructions", + .arch = X86_64, + .group_size = parainstructions_group_size, + }, + { + .name = ".altinstructions", + .arch = X86_64 | S390, + .group_size = altinstructions_group_size, + }, + { + .name = ".static_call_sites", + .arch = X86_64, + .group_size = static_call_sites_group_size, + }, + { + .name = ".retpoline_sites", + .arch = X86_64, + .group_size = retpoline_sites_group_size, + }, + { + .name = ".return_sites", + .arch = X86_64, + .group_size = return_sites_group_size, + }, + { + .name = "__ftr_fixup", + .arch = PPC64, + .group_size = fixup_entry_group_size, + }, + { + .name = "__mmu_ftr_fixup", + .arch = PPC64, + .group_size = fixup_entry_group_size, + }, + { + .name = "__fw_ftr_fixup", + .arch = PPC64, + .group_size = fixup_entry_group_size, + }, + { + .name = "__lwsync_fixup", + .arch = PPC64, + .group_size = fixup_lwsync_group_size, + }, + { + .name = "__barrier_nospec_fixup", + .arch = PPC64, + .group_size = fixup_barrier_nospec_group_size, + }, + { + .name = ".s390_return_mem", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_return_reg", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_call", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_branches", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_jump", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + {}, +}; + +static bool should_keep_jump_label(struct lookup_table *lookup, + struct section *relasec, + unsigned int group_offset, + unsigned int group_size, + int *jump_labels_found) +{ + struct rela *code = NULL, *key = NULL, *rela; + bool tracepoint = false, dynamic_debug = false; + struct lookup_result symbol; + int i = 0; + + /* + * Here we hard-code knowledge about the contents of the jump_entry + * struct. It has three fields: code, target, and key. Each field has + * a relocation associated with it. + */ + list_for_each_entry(rela, &relasec->relas, list) { + if (rela->offset >= group_offset && + rela->offset < group_offset + group_size) { + if (i == 0) + code = rela; + else if (i == 2) + key = rela; + i++; + } + } + + if (i != 3 || !key || !code) + ERROR("BUG: __jump_table has an unexpected format"); + + if (!strncmp(key->sym->name, "__tracepoint_", 13)) + tracepoint = true; + + if (is_dynamic_debug_symbol(key->sym)) + dynamic_debug = true; + + if (KLP_ARCH) { + /* + * On older kernels (with .klp.arch support), jump labels + * aren't supported at all. Error out when they occur in a + * replacement function, with the exception of tracepoints and + * dynamic debug printks. An inert tracepoint or printk is + * harmless enough, but a broken jump label can cause + * unexpected behavior. + */ + if (tracepoint || dynamic_debug) + return false; + + /* + * This will be upgraded to an error after all jump labels have + * been reported. + */ + log_normal("Found a jump label at %s()+0x%lx, using key %s. Jump labels aren't supported with this kernel. Use static_key_enabled() instead.\n", + code->sym->name, code->addend, key->sym->name); + (*jump_labels_found)++; + return false; + } + + /* + * On newer (5.8+) kernels, jump labels are supported in the case where + * the corresponding static key lives in vmlinux. That's because such + * kernels apply vmlinux-specific .klp.rela sections at the same time + * (in the klp module load) as normal relas, before jump label init. + * On the other hand, jump labels based on static keys which are + * defined in modules aren't supported, because late module patching + * can result in the klp relas getting applied *after* the klp module's + * jump label init. + */ + + if (lookup_symbol(lookup, key->sym, &symbol) && + strcmp(symbol.objname, "vmlinux")) { + + /* The static key lives in a module -- not supported */ + + /* Inert tracepoints and dynamic debug printks are harmless */ + if (tracepoint || dynamic_debug) + return false; + + /* + * This will be upgraded to an error after all jump labels have + * been reported. + */ + log_normal("Found a jump label at %s()+0x%lx, using key %s, which is defined in a module. Use static_key_enabled() instead.\n", + code->sym->name, code->addend, key->sym->name); + (*jump_labels_found)++; + return false; + } + + /* The static key lives in vmlinux or the patch module itself */ + + /* + * If the jump label key lives in the '__dyndbg' section, make sure + * the section gets included, because we don't use klp relocs for + * dynamic debug symbols. For an example of such a key, see + * DYNAMIC_DEBUG_BRANCH(). + */ + if (dynamic_debug) + kpatch_include_symbol(key->sym); + + return true; +} + +static bool should_keep_rela_group(struct lookup_table *lookup, + struct section *relasec, unsigned int offset, + unsigned int size, int *jump_labels_found) +{ + struct rela *rela; + bool found = false; + + /* check if any relas in the group reference any changed functions */ + list_for_each_entry(rela, &relasec->relas, list) { + if (rela->offset >= offset && + rela->offset < offset + size && + rela->sym->type == STT_FUNC && + rela->sym->sec->include) { + found = true; + log_debug("new/changed symbol %s found in special section %s\n", + rela->sym->name, relasec->name); + } + } + + if (!found) + return false; + + if (!strcmp(relasec->name, ".rela__jump_table")) + return should_keep_jump_label(lookup, relasec, offset, size, + jump_labels_found); + + return true; +} + +/* + * When updating .fixup, the corresponding addends in .ex_table need to be + * updated too. Stash the result in rela.r_addend so that the calculation in + * fixup_group_size() is not affected. + */ +static void kpatch_update_ex_table_addend(struct kpatch_elf *kelf, + struct special_section *special, + int src_offset, int dest_offset, + int group_size) +{ + struct rela *rela; + struct section *relasec; + + relasec = find_section_by_name(&kelf->sections, ".rela__ex_table"); + if (!relasec) + ERROR("missing .rela__ex_table section"); + + list_for_each_entry(rela, &relasec->relas, list) { + if (!strcmp(rela->sym->name, ".fixup") && + rela->addend >= src_offset && + rela->addend < src_offset + group_size) + rela->rela.r_addend = rela->addend - (src_offset - dest_offset); + } +} + +static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, + struct lookup_table *lookup, + struct special_section *special, + struct section *relasec) +{ + struct rela *rela, *safe; + char *src, *dest; + unsigned int group_size, src_offset, dest_offset; + int jump_labels_found = 0; + + LIST_HEAD(newrelas); + + src = relasec->base->data->d_buf; + /* alloc buffer for new base section */ + dest = malloc(relasec->base->sh.sh_size); + if (!dest) + ERROR("malloc"); + + /* Restore the stashed r_addend from kpatch_update_ex_table_addend. */ + if (!strcmp(special->name, "__ex_table")) { + list_for_each_entry(rela, &relasec->relas, list) { + if (!strcmp(rela->sym->name, ".fixup")) + rela->addend = rela->rela.r_addend; + } + } + + src_offset = 0; + dest_offset = 0; + for ( ; src_offset < relasec->base->sh.sh_size; src_offset += group_size) { + + group_size = special->group_size(kelf, src_offset); + + /* + * In some cases the struct has padding at the end to ensure + * that all structs after it are properly aligned. But the + * last struct in the section may not be padded. In that case, + * shrink the group_size such that it still (hopefully) + * contains the data but doesn't go past the end of the + * section. + */ + if (src_offset + group_size > relasec->base->sh.sh_size) + group_size = (unsigned int)(relasec->base->sh.sh_size - src_offset); + + if (!should_keep_rela_group(lookup, relasec, src_offset, group_size, + &jump_labels_found)) + continue; + + /* + * Copy all relas in the group. It's possible that the relas + * aren't sorted (e.g. .rela.fixup), so go through the entire + * rela list each time. + */ + list_for_each_entry_safe(rela, safe, &relasec->relas, list) { + if (rela->offset >= src_offset && + rela->offset < src_offset + group_size) { + /* copy rela entry */ + list_del(&rela->list); + list_add_tail(&rela->list, &newrelas); + + rela->offset -= src_offset - dest_offset; + rela->rela.r_offset = rela->offset; + + rela->sym->include = 1; + + if (!strcmp(special->name, ".fixup")) + kpatch_update_ex_table_addend(kelf, special, + src_offset, + dest_offset, + group_size); + } + } + + /* copy base section group */ + memcpy(dest + dest_offset, src + src_offset, group_size); + dest_offset += group_size; + } + + if (jump_labels_found) + ERROR("Found %d jump label(s) in the patched code. Jump labels aren't currently supported. Use static_key_enabled() instead.", + jump_labels_found); + + if (!dest_offset) { + /* no changed or global functions referenced */ + relasec->status = relasec->base->status = SAME; + relasec->include = relasec->base->include = 0; + free(dest); + return; + } + + /* overwrite with new relas list */ + list_replace(&newrelas, &relasec->relas); + + /* include both rela and base sections */ + relasec->include = 1; + relasec->base->include = 1; + /* include secsym so .kpatch.arch relas can point to section symbols */ + if (relasec->base->secsym) + relasec->base->secsym->include = 1; + + /* + * Update text section data buf and size. + * + * The rela section's data buf and size will be regenerated in + * kpatch_rebuild_rela_section_data(). + */ + relasec->base->data->d_buf = dest; + relasec->base->data->d_size = dest_offset; +} + +#define ORC_IP_PTR_SIZE 4 + +/* + * This function is similar to kpatch_regenerate_special_section(), but + * customized for the ORC-related sections. ORC is more special than the other + * special sections because each ORC entry is split into .orc_unwind (struct + * orc_entry) and .orc_unwind_ip. + */ +static void kpatch_regenerate_orc_sections(struct kpatch_elf *kelf) +{ + struct rela *rela, *safe; + char *src, *dest, *str; + unsigned int src_idx = 0, dest_idx = 0, orc_entry_size; + struct section *orc_sec, *ip_sec; + + + str = getenv("ORC_STRUCT_SIZE"); + if (!str) + return; + orc_entry_size = atoi(str); + + if (!orc_entry_size) + ERROR("bad ORC_STRUCT_SIZE"); + + LIST_HEAD(newrelas); + + orc_sec = find_section_by_name(&kelf->sections, ".orc_unwind"); + ip_sec = find_section_by_name(&kelf->sections, ".orc_unwind_ip"); + + if (!orc_sec || !ip_sec) + return; + + if (orc_sec->sh.sh_size % orc_entry_size != 0) + ERROR("bad .orc_unwind size"); + + if (ip_sec->sh.sh_size != + (orc_sec->sh.sh_size / orc_entry_size) * ORC_IP_PTR_SIZE) + ERROR(".orc_unwind/.orc_unwind_ip size mismatch"); + + src = orc_sec->data->d_buf; + dest = malloc(orc_sec->sh.sh_size); + if (!dest) + ERROR("malloc"); + + list_for_each_entry_safe(rela, safe, &ip_sec->rela->relas, list) { + + if (rela->sym->type != STT_FUNC || !rela->sym->sec->include) + goto next; + + /* copy orc entry */ + memcpy(dest + (dest_idx * orc_entry_size), + src + (src_idx * orc_entry_size), + orc_entry_size); + + /* move ip rela */ + list_del(&rela->list); + list_add_tail(&rela->list, &newrelas); + rela->offset = dest_idx * ORC_IP_PTR_SIZE; + rela->sym->include = 1; + + dest_idx++; +next: + src_idx++; + } + + if (!dest_idx) { + /* no changed or global functions referenced */ + orc_sec->status = ip_sec->status = ip_sec->rela->status = SAME; + orc_sec->include = ip_sec->include = ip_sec->rela->include = 0; + free(dest); + return; + } + + /* overwrite with new relas list */ + list_replace(&newrelas, &ip_sec->rela->relas); + + /* include the sections */ + orc_sec->include = ip_sec->include = ip_sec->rela->include = 1; + + /* + * Update data buf/size. + * + * The ip section can keep its old (zeroed data), though its size has + * possibly decreased. The ip rela section's data buf and size will be + * regenerated in kpatch_rebuild_rela_section_data(). + */ + orc_sec->data->d_buf = dest; + orc_sec->data->d_size = dest_idx * orc_entry_size; + ip_sec->data->d_size = dest_idx * ORC_IP_PTR_SIZE; +} + +static void kpatch_check_relocations(struct kpatch_elf *kelf) +{ + struct rela *rela; + struct section *relasec; + long sec_size; + long sec_off; + + list_for_each_entry(relasec, &kelf->sections, list) { + if (!is_rela_section(relasec)) + continue; + list_for_each_entry(rela, &relasec->relas, list) { + if (!rela->sym->sec) + continue; + + sec_size = rela->sym->sec->data->d_size; + sec_off = (long)rela->sym->sym.st_value + + rela_target_offset(kelf, relasec, rela); + + /* + * This check isn't perfect: we still allow relocations + * to the end of a section. There are real instances + * of that, including ORC entries, LOCKDEP=n + * zero-length '__key' passing, and the loop edge case + * described in kpatch_replace_sections_syms(). For + * now, just allow all such cases. + */ + if (sec_off < 0 || sec_off > sec_size) { + ERROR("%s+0x%x: out-of-range relocation %s+%lx", + relasec->base->name, rela->offset, + rela->sym->name, rela->addend); + } + } + } +} + +static void kpatch_include_debug_sections(struct kpatch_elf *kelf) +{ + struct section *sec; + struct rela *rela, *saferela; + + /* include all .debug_* sections */ + list_for_each_entry(sec, &kelf->sections, list) { + if (is_debug_section(sec)) { + sec->include = 1; + if (!is_rela_section(sec) && sec->secsym) + sec->secsym->include = 1; + } + } + + /* + * Go through the .rela.debug_ sections and strip entries + * referencing unchanged symbols + */ + list_for_each_entry(sec, &kelf->sections, list) { + if (!is_rela_section(sec) || !is_debug_section(sec)) + continue; + list_for_each_entry_safe(rela, saferela, &sec->relas, list) + if (!rela->sym->sec->include) + list_del(&rela->list); + } +} + +static void kpatch_mark_ignored_sections(struct kpatch_elf *kelf) +{ + struct section *sec, *strsec, *ignoresec; + struct rela *rela; + char *name; + + /* Ignore any discarded sections */ + list_for_each_entry(sec, &kelf->sections, list) { + if (!strncmp(sec->name, ".discard", 8) || + !strncmp(sec->name, ".rela.discard", 13) || + !strncmp(sec->name, ".llvm_addrsig", 13) || + !strncmp(sec->name, ".llvm.", 6)) + sec->ignore = 1; + } + + sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.sections"); + if (!sec) + return; + + list_for_each_entry(rela, &sec->rela->relas, list) { + strsec = rela->sym->sec; + strsec->status = CHANGED; + /* + * Include the string section here. This is because the + * KPATCH_IGNORE_SECTION() macro is passed a literal string + * by the patch author, resulting in a change to the string + * section. If we don't include it, then we will potentially + * get a "changed section not included" error in + * kpatch_verify_patchability() if no other function based change + * also changes the string section. We could try to exclude each + * literal string added to the section by KPATCH_IGNORE_SECTION() + * from the section data comparison, but this is a simpler way. + */ + strsec->include = 1; + if (strsec->secsym) + strsec->secsym->include = 1; + + name = strsec->data->d_buf + rela->addend; + ignoresec = find_section_by_name(&kelf->sections, name); + if (!ignoresec) + ERROR("KPATCH_IGNORE_SECTION: can't find %s", name); + log_normal("ignoring section: %s\n", name); + if (is_rela_section(ignoresec)) + ignoresec = ignoresec->base; + ignoresec->ignore = 1; + if (ignoresec->twin) + ignoresec->twin->ignore = 1; + } +} + +static void kpatch_mark_ignored_sections_same(struct kpatch_elf *kelf) +{ + struct section *sec; + struct symbol *sym; + + list_for_each_entry(sec, &kelf->sections, list) { + if (!sec->ignore) + continue; + sec->status = SAME; + if (!is_rela_section(sec)) { + if (sec->secsym) + sec->secsym->status = SAME; + if (sec->rela) + sec->rela->status = SAME; + } + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->sec != sec) + continue; + sym->status = SAME; + } + } + + /* strip temporary global __UNIQUE_ID_kpatch_ignore_section_* symbols */ + list_for_each_entry(sym, &kelf->symbols, list) + if (!strncmp(sym->name, "__UNIQUE_ID_kpatch_ignore_section_", + strlen("__UNIQUE_ID_kpatch_ignore_section_"))) + sym->status = SAME; +} + +static void kpatch_mark_ignored_children_same(struct symbol *sym) +{ + struct symbol *child; + + list_for_each_entry(child, &sym->children, subfunction_node) { + child->status = SAME; + kpatch_mark_ignored_children_same(child); + } +} + +static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf) +{ + struct section *sec; + struct symbol *sym; + struct rela *rela; + + sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.functions"); + if (!sec) + return; + + list_for_each_entry(rela, &sec->rela->relas, list) { + if (!rela->sym->sec) + ERROR("expected bundled symbol"); + if (rela->sym->type != STT_FUNC) + ERROR("expected function symbol"); + log_normal("ignoring function: %s\n", rela->sym->name); + if (rela->sym->status != CHANGED) + log_normal("NOTICE: no change detected in function %s, unnecessary KPATCH_IGNORE_FUNCTION()?\n", rela->sym->name); + rela->sym->status = SAME; + rela->sym->sec->status = SAME; + + kpatch_mark_ignored_children_same(rela->sym); + + if (rela->sym->sec->secsym) + rela->sym->sec->secsym->status = SAME; + if (rela->sym->sec->rela) + rela->sym->sec->rela->status = SAME; + } + + /* strip temporary global kpatch_ignore_func_* symbols */ + list_for_each_entry(sym, &kelf->symbols, list) + if (!strncmp(sym->name, "__kpatch_ignore_func_", + strlen("__kpatch_ignore_func_"))) + sym->status = SAME; +} + +static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *objname) +{ + struct special_section *special; + struct symbol *strsym; + struct section *sec, *karch_sec; + struct rela *rela; + int nr, index = 0; + + if (!KLP_ARCH) + return; + + nr = sizeof(special_sections) / sizeof(special_sections[0]); + karch_sec = create_section_pair(kelf, ".kpatch.arch", sizeof(struct kpatch_arch), nr); + + /* lookup strings symbol */ + strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings"); + if (!strsym) + ERROR("can't find .kpatch.strings symbol"); + + for (special = special_sections; special->name; special++) { + if ((special->arch & kelf->arch) == 0) + continue; + + if (strcmp(special->name, ".parainstructions") && + strcmp(special->name, ".altinstructions")) + continue; + + sec = find_section_by_name(&kelf->sections, special->name); + if (!sec) + continue; + + /* entries[index].sec */ + ALLOC_LINK(rela, &karch_sec->rela->relas); + rela->sym = sec->secsym; + rela->type = absolute_rela_type(kelf); + rela->addend = 0; + rela->offset = (unsigned int)(index * sizeof(struct kpatch_arch) + \ + offsetof(struct kpatch_arch, sec)); + + /* entries[index].objname */ + ALLOC_LINK(rela, &karch_sec->rela->relas); + rela->sym = strsym; + rela->type = absolute_rela_type(kelf); + rela->addend = offset_of_string(&kelf->strings, objname); + rela->offset = (unsigned int)(index * sizeof(struct kpatch_arch) + \ + offsetof(struct kpatch_arch, objname)); + + index++; + } + + karch_sec->data->d_size = index * sizeof(struct kpatch_arch); + karch_sec->sh.sh_size = karch_sec->data->d_size; +} + +static void kpatch_process_special_sections(struct kpatch_elf *kelf, + struct lookup_table *lookup) +{ + struct special_section *special; + struct section *sec; + struct symbol *sym; + struct rela *rela; + int altinstr = 0; + + for (special = special_sections; special->name; special++) { + if ((special->arch & kelf->arch) == 0) + continue; + + sec = find_section_by_name(&kelf->sections, special->name); + if (!sec || !sec->rela) + continue; + + kpatch_regenerate_special_section(kelf, lookup, special, sec->rela); + + if (!strcmp(special->name, ".altinstructions") && sec->include) + altinstr = 1; + } + + /* + * The following special sections don't have relas which reference + * non-included symbols, so their entire rela section can be included. + */ + list_for_each_entry(sec, &kelf->sections, list) { + if (strcmp(sec->name, ".altinstr_replacement")) + continue; + /* + * Only include .altinstr_replacement if .altinstructions + * is also included. + */ + if (!altinstr) + break; + + /* include base section */ + sec->include = 1; + + /* include all symbols in the section */ + list_for_each_entry(sym, &kelf->symbols, list) + if (sym->sec == sec) + sym->include = 1; + + /* include rela section */ + if (sec->rela) { + sec->rela->include = 1; + /* include all symbols referenced by relas */ + list_for_each_entry(rela, &sec->rela->relas, list) + kpatch_include_symbol(rela->sym); + } + } + + if (KLP_ARCH) { + /* + * The following special sections aren't supported with older + * kernels, so make sure we don't ever try to include them. + * Otherwise the kernel will see the jump table during module + * loading and get confused. Generally it should be safe to + * exclude them, it just means that you can't modify jump + * labels and enable tracepoints in a patched function. + */ + list_for_each_entry(sec, &kelf->sections, list) { + if (strcmp(sec->name, "__jump_table") && + strcmp(sec->name, "__tracepoints") && + strcmp(sec->name, "__tracepoints_ptrs") && + strcmp(sec->name, "__tracepoints_strings")) + continue; + + sec->status = SAME; + sec->include = 0; + if (sec->rela) { + sec->rela->status = SAME; + sec->rela->include = 0; + } + } + } + + kpatch_regenerate_orc_sections(kelf); +} + +static void kpatch_create_patches_sections(struct kpatch_elf *kelf, + struct lookup_table *table, + char *objname) +{ + int nr, index, objname_offset; + struct section *sec, *relasec; + struct symbol *sym, *strsym; + struct rela *rela; + struct lookup_result symbol; + struct kpatch_patch_func *funcs; + + /* count patched functions */ + nr = 0; + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status != CHANGED || + sym->parent) + continue; + nr++; + } + + /* create text/rela section pair */ + sec = create_section_pair(kelf, ".kpatch.funcs", sizeof(*funcs), nr); + relasec = sec->rela; + funcs = sec->data->d_buf; + + /* lookup strings symbol */ + strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings"); + if (!strsym) + ERROR("can't find .kpatch.strings symbol"); + + /* add objname to strings */ + objname_offset = offset_of_string(&kelf->strings, objname); + + /* populate sections */ + index = 0; + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status != CHANGED || + sym->parent) + continue; + + if (!lookup_symbol(table, sym, &symbol)) + ERROR("can't find symbol '%s' in symbol table", sym->name); + + if (sym->bind == STB_LOCAL && symbol.global) + ERROR("can't find local symbol '%s' in symbol table", sym->name); + + log_debug("lookup for %s: obj=%s sympos=%lu size=%lu", + sym->name, symbol.objname, symbol.sympos, + symbol.size); + + /* + * Convert global symbols to local so other objects in the + * patch module (like the patch callback object's init code) + * won't link to this function and call it before its + * relocations have been applied. + */ + sym->bind = STB_LOCAL; + sym->sym.st_info = (unsigned char) + GELF_ST_INFO(sym->bind, sym->type); + + /* add entry in text section */ + funcs[index].old_addr = symbol.addr; + funcs[index].old_size = symbol.size; + funcs[index].new_size = sym->sym.st_size; + funcs[index].sympos = symbol.sympos; + + /* + * Add a relocation that will populate the + * funcs[index].new_addr field at module load time. + */ + ALLOC_LINK(rela, &relasec->relas); + rela->sym = sym; + rela->type = absolute_rela_type(kelf); + rela->addend = 0; + rela->offset = (unsigned int)(index * sizeof(*funcs)); + + /* + * Add a relocation that will populate the funcs[index].name + * field. + */ + ALLOC_LINK(rela, &relasec->relas); + rela->sym = strsym; + rela->type = absolute_rela_type(kelf); + rela->addend = offset_of_string(&kelf->strings, sym->name); + rela->offset = (unsigned int)(index * sizeof(*funcs) + + offsetof(struct kpatch_patch_func, name)); + + /* + * Add a relocation that will populate the funcs[index].objname + * field. + */ + ALLOC_LINK(rela, &relasec->relas); + rela->sym = strsym; + rela->type = absolute_rela_type(kelf); + rela->addend = objname_offset; + rela->offset = (unsigned int)(index * sizeof(*funcs) + + offsetof(struct kpatch_patch_func,objname)); + + index++; + } + + /* sanity check, index should equal nr */ + if (index != nr) + ERROR("size mismatch in funcs sections"); + +} + +static bool kpatch_is_core_module_symbol(char *name) +{ + return (!strcmp(name, "kpatch_shadow_alloc") || + !strcmp(name, "kpatch_shadow_free") || + !strcmp(name, "kpatch_shadow_get")); +} + +static bool is_expoline(struct kpatch_elf *kelf, char *name) +{ + return kelf->arch == S390 && !strncmp(name, "__s390_indirect_jump_r", 22); +} + +static int function_ptr_rela(const struct rela *rela) +{ + const struct rela *rela_toc = toc_rela(rela); + + return (rela_toc && rela_toc->sym->type == STT_FUNC && + !rela_toc->sym->parent && + rela_toc->addend == (int)rela_toc->sym->sym.st_value && + (rela->type == R_X86_64_32S || + rela->type == R_PPC64_TOC16_HA || + rela->type == R_PPC64_TOC16_LO_DS)); +} + +static bool need_dynrela(struct kpatch_elf *kelf, struct lookup_table *table, + struct section *relasec, const struct rela *rela) +{ + struct lookup_result symbol; + + if (is_debug_section(relasec)) + return false; + + /* + * These references are treated specially by the module loader and + * should never be converted to dynrelas. + */ + if (rela->type == R_PPC64_REL16_HA || rela->type == R_PPC64_REL16_LO || + rela->type == R_PPC64_ENTRY) + return false; + + /* v5.13+ kernels use relative jump labels */ + if (rela->type == R_PPC64_REL64 && strcmp(relasec->name, ".rela__jump_table")) + return false; + + /* + * On powerpc, the function prologue generated by GCC 6 has the + * sequence: + * + * .globl my_func + * .type my_func, @function + * .quad .TOC.-my_func + * my_func: + * .reloc ., R_PPC64_ENTRY ; optional + * ld r2,-8(r12) + * add r2,r2,r12 + * .localentry my_func, .-my_func + * + * The R_PPC64_ENTRY is optional and its symbol might have an empty + * name. Leave it as a normal rela. + */ + if (rela->type == R_PPC64_ENTRY) + return false; + + /* + * Allow references to core module symbols to remain as normal + * relas. They should be exported. + */ + if (kpatch_is_core_module_symbol(rela->sym->name)) + return false; + + /* + * Allow references to s390 expolines to remain as normal relas. They + * will be generated in the module by the kernel module link. + */ + if (is_expoline(kelf, rela->sym->name)) + return false; + + if (rela->sym->sec) { + /* + * Internal symbols usually don't need dynrelas, because they + * live in the patch module and can be relocated normally. + * + * There's one exception: function pointers. + * + * If the rela references a function pointer, we convert it to + * a dynrela, so that the function pointer will refer to the + * original function rather than the patched function. This + * can prevent crashes in cases where the function pointer is + * called asynchronously after the patch module has been + * unloaded. + */ + if (!function_ptr_rela(rela)) + return false; + + /* + * Function pointers which refer to _nested_ functions are a + * special case. They are not supposed to be visible outside + * of the function that defines them. Their names may differ + * in the original and the patched kernels which makes it + * difficult to use dynrelas. Fortunately, nested functions + * are rare and are unlikely to be used as asynchronous + * callbacks, so the patched code can refer to them directly. + * It seems, one can only distinguish such functions by their + * names containing a dot. Other kinds of functions with such + * names (e.g. optimized copies of functions) are unlikely to + * be used as callbacks. + * + * Function pointers to *new* functions don't have this issue, + * just use a normal rela for them. + */ + return toc_rela(rela)->sym->status != NEW && + !strchr(toc_rela(rela)->sym->name, '.'); + } + + if (!lookup_symbol(table, rela->sym, &symbol)) { + /* + * Assume the symbol lives in another .o in the patch module. + * A normal rela should work. + */ + return false; + } + + if (rela->sym->bind == STB_LOCAL) { + + if (symbol.global) + ERROR("can't find local symbol '%s' in symbol table", + rela->sym->name); + + /* + * The symbol is (formerly) local. Use a dynrela to access the + * original version of the symbol in the patched object. + */ + return true; + } + + if (symbol.exported) { + + if (is_gcc6_localentry_bundled_sym(kelf, rela->sym)) { + /* + * On powerpc, the symbol is global and exported, but + * it was also in the changed object file. In this + * case the rela refers to the 'localentry' point, so a + * normal rela wouldn't work. Force a dynrela so it + * can be handled correctly by the livepatch relocation + * code. + */ + return true; + } + + if (!strcmp(symbol.objname, "vmlinux")) { + /* + * The symbol is exported by vmlinux. Use a normal + * rela. + */ + return false; + } + + /* + * The symbol is exported by the to-be-patched module, or by + * another module which the patched module depends on. Use a + * dynrela because of late module loading: the patch module may + * be loaded before the to-be-patched (or other) module. + */ + return true; + } + + if (symbol.global) { + /* + * The symbol is global in the to-be-patched object, but not + * exported. Use a dynrela to work around the fact that it's + * an unexported sybmbol. + */ + return true; + } + + /* + * The symbol is global and not exported, but it's not in the parent + * object. The only explanation is that it's defined in another object + * in the patch module. A normal rela should resolve it. + */ + return false; +} + +/* + * kpatch_create_intermediate_sections() + * + * The primary purpose of this function is to convert some relas (also known as + * relocations) to dynrelas (also known as dynamic relocations or livepatch + * relocations or klp relas). + * + * If the patched code refers to a symbol, for example, if it calls a function + * or stores a pointer to a function somewhere or accesses some global data, + * the address of that symbol must be resolved somehow before the patch is + * applied. + * + * If the symbol lives outside the patch module, and if it's not exported by + * vmlinux (e.g., with EXPORT_SYMBOL) then the rela needs to be converted to a + * dynrela so the livepatch code can resolve it at runtime. + */ +static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, + struct lookup_table *table, + char *objname, + char *pmod_name) +{ + int nr, index; + struct section *relasec, *ksym_sec, *krela_sec; + struct rela *rela, *rela2, *safe; + struct symbol *strsym, *ksym_sec_sym; + struct kpatch_symbol *ksyms; + struct kpatch_relocation *krelas; + struct lookup_result symbol; + bool special; + bool vmlinux = !strcmp(objname, "vmlinux"); + struct special_section *s; + + /* count rela entries that need to be dynamic */ + nr = 0; + list_for_each_entry(relasec, &kelf->sections, list) { + if (!is_rela_section(relasec)) + continue; + if (!strcmp(relasec->name, ".rela.kpatch.funcs")) + continue; + list_for_each_entry(rela, &relasec->relas, list) { + + /* upper bound on number of kpatch relas and symbols */ + nr++; + + /* + * We set 'need_dynrela' here in the first pass because + * the .toc section's 'need_dynrela' values are + * dependent on all the other sections. Otherwise, if + * we did this analysis in the second pass, we'd have + * to convert .toc dynrelas at the very end. + * + * Specifically, this is needed for the powerpc + * internal symbol function pointer check which is done + * via .toc indirection in need_dynrela(). + */ + if (need_dynrela(kelf, table, relasec, rela)) + toc_rela(rela)->need_dynrela = 1; + } + } + + /* create .kpatch.relocations text/rela section pair */ + krela_sec = create_section_pair(kelf, ".kpatch.relocations", sizeof(*krelas), nr); + krelas = krela_sec->data->d_buf; + + /* create .kpatch.symbols text/rela section pair */ + ksym_sec = create_section_pair(kelf, ".kpatch.symbols", sizeof(*ksyms), nr); + ksyms = ksym_sec->data->d_buf; + + /* create .kpatch.symbols section symbol (to set rela->sym later) */ + ALLOC_LINK(ksym_sec_sym, &kelf->symbols); + ksym_sec_sym->sec = ksym_sec; + ksym_sec_sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); + ksym_sec_sym->type = STT_SECTION; + ksym_sec_sym->bind = STB_LOCAL; + ksym_sec_sym->name = ".kpatch.symbols"; + + /* lookup strings symbol */ + strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings"); + if (!strsym) + ERROR("can't find .kpatch.strings symbol"); + + /* populate sections */ + index = 0; + list_for_each_entry(relasec, &kelf->sections, list) { + if (!is_rela_section(relasec)) + continue; + if (!strcmp(relasec->name, ".rela.kpatch.funcs") || + !strcmp(relasec->name, ".rela.kpatch.relocations") || + !strcmp(relasec->name, ".rela.kpatch.symbols")) + continue; + + special = false; + for (s = special_sections; s->name; s++) { + if ((s->arch & kelf->arch) == 0) + continue; + + if (!strcmp(relasec->base->name, s->name)) + special = true; + } + + list_for_each_entry_safe(rela, safe, &relasec->relas, list) { + if (!rela->need_dynrela) { + rela->sym->strip = SYMBOL_USED; + continue; + } + + /* + * Starting with Linux 5.8, .klp.arch sections are no + * longer supported: now that vmlinux relocations are + * written early, before paravirt and alternative + * module init, .klp.arch is technically not needed. + * + * For sanity we just need to make sure that there are + * no .klp.rela.{module}.{section} sections for special + * sections. Otherwise there might be ordering issues, + * if the .klp.relas are applied after the module + * special section init code (e.g., apply_paravirt) + * runs due to late module patching. + */ + if (!KLP_ARCH && !vmlinux && special) + ERROR("unsupported dynrela reference to symbol '%s' in module-specific special section '%s'", + rela->sym->name, relasec->base->name); + + if (!lookup_symbol(table, rela->sym, &symbol)) + ERROR("can't find symbol '%s' in symbol table", + rela->sym->name); + + log_debug("lookup for %s: obj=%s sympos=%lu", + rela->sym->name, symbol.objname, + symbol.sympos); + + /* Fill in ksyms[index] */ + if (vmlinux) + ksyms[index].src = symbol.addr; + else + /* for modules, src is discovered at runtime */ + ksyms[index].src = 0; + ksyms[index].sympos = symbol.sympos; + ksyms[index].type = rela->sym->type; + ksyms[index].bind = rela->sym->bind; + + /* add rela to fill in ksyms[index].name field */ + ALLOC_LINK(rela2, &ksym_sec->rela->relas); + rela2->sym = strsym; + rela2->type = absolute_rela_type(kelf); + rela2->addend = offset_of_string(&kelf->strings, rela->sym->name); + rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \ + offsetof(struct kpatch_symbol, name)); + + /* add rela to fill in ksyms[index].objname field */ + ALLOC_LINK(rela2, &ksym_sec->rela->relas); + rela2->sym = strsym; + rela2->type = absolute_rela_type(kelf); + rela2->addend = offset_of_string(&kelf->strings, symbol.objname); + rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \ + offsetof(struct kpatch_symbol, objname)); + + /* Fill in krelas[index] */ + if (is_gcc6_localentry_bundled_sym(kelf, rela->sym) && + rela->addend == (int)rela->sym->sym.st_value) + rela->addend -= rela->sym->sym.st_value; + krelas[index].addend = rela->addend; + krelas[index].type = rela->type; + krelas[index].external = !vmlinux && symbol.exported; + + /* add rela to fill in krelas[index].dest field */ + ALLOC_LINK(rela2, &krela_sec->rela->relas); + if (!relasec->base->secsym) { + struct symbol *sym; + + /* + * Newer toolchains are stingy with their + * section symbols, create one if it doesn't + * exist already. + */ + ALLOC_LINK(sym, &kelf->symbols); + sym->sec = relasec->base; + sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); + sym->type = STT_SECTION; + sym->bind = STB_LOCAL; + sym->name = relasec->base->name; + relasec->base->secsym = sym; + } + rela2->sym = relasec->base->secsym; + rela2->type = absolute_rela_type(kelf); + rela2->addend = rela->offset; + rela2->offset = (unsigned int)(index * sizeof(*krelas) + \ + offsetof(struct kpatch_relocation, dest)); + + /* add rela to fill in krelas[index].objname field */ + ALLOC_LINK(rela2, &krela_sec->rela->relas); + rela2->sym = strsym; + rela2->type = absolute_rela_type(kelf); + rela2->addend = offset_of_string(&kelf->strings, objname); + rela2->offset = (unsigned int)(index * sizeof(*krelas) + \ + offsetof(struct kpatch_relocation, objname)); + + /* add rela to fill in krelas[index].ksym field */ + ALLOC_LINK(rela2, &krela_sec->rela->relas); + rela2->sym = ksym_sec_sym; + rela2->type = absolute_rela_type(kelf); + rela2->addend = (unsigned int)(index * sizeof(*ksyms)); + rela2->offset = (unsigned int)(index * sizeof(*krelas) + \ + offsetof(struct kpatch_relocation, ksym)); + + /* + * Mark the referred to symbol for removal but + * only if it is not from this object file. + * The symbols from this object file may be needed + * later (for example, they may have relocations + * of their own which should be processed). + */ + if (!rela->sym->sec && rela->sym->strip != SYMBOL_USED) + rela->sym->strip = SYMBOL_STRIP; + list_del(&rela->list); + free(rela); + + index++; + } + } + + /* set size to actual number of ksyms/krelas */ + ksym_sec->data->d_size = index * sizeof(struct kpatch_symbol); + ksym_sec->sh.sh_size = ksym_sec->data->d_size; + + krela_sec->data->d_size = index * sizeof(struct kpatch_relocation); + krela_sec->sh.sh_size = krela_sec->data->d_size; +} + +static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char *objname) +{ + struct section *sec; + struct rela *rela; + struct symbol *strsym; + int objname_offset; + + struct callback { char *name; int offset; }; + static struct callback callbacks[] = { + { .name = ".rela.kpatch.callbacks.pre_patch", + .offset = offsetof(struct kpatch_pre_patch_callback, objname) }, + { .name = ".rela.kpatch.callbacks.post_patch", + .offset = offsetof(struct kpatch_post_patch_callback, objname) }, + { .name = ".rela.kpatch.callbacks.pre_unpatch", + .offset = offsetof(struct kpatch_pre_unpatch_callback, objname) }, + { .name = ".rela.kpatch.callbacks.post_unpatch", + .offset = offsetof(struct kpatch_post_patch_callback, objname) }, + { .name = NULL, .offset = 0 }, + }; + struct callback *callbackp; + + /* lookup strings symbol */ + strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings"); + if (!strsym) + ERROR("can't find .kpatch.strings symbol"); + + /* add objname to strings */ + objname_offset = offset_of_string(&kelf->strings, objname); + + list_for_each_entry(sec, &kelf->sections, list) { + for (callbackp = callbacks; callbackp->name; callbackp++) { + if (!strcmp(callbackp->name, sec->name)) { + ALLOC_LINK(rela, &sec->relas); + rela->sym = strsym; + rela->type = absolute_rela_type(kelf); + rela->addend = objname_offset; + rela->offset = callbackp->offset; + break; + } + } + } +} + +/* + * This function basically reimplements the functionality of the Linux + * recordmcount script, so that patched functions can be recognized by ftrace. + * + * TODO: Eventually we can modify recordmount so that it recognizes our bundled + * sections as valid and does this work for us. + */ +static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) +{ + int nr, index; + struct section *sec, *relasec; + struct symbol *sym; + struct rela *rela, *mcount_rela; + void **funcs; + unsigned long insn_offset = 0; + + nr = 0; + list_for_each_entry(sym, &kelf->symbols, list) + if (sym->type == STT_FUNC && sym->status != SAME && + sym->has_func_profiling) + nr++; + + /* create text/rela section pair */ + sec = create_section_pair(kelf, "__mcount_loc", sizeof(void*), nr); + relasec = sec->rela; + + /* populate sections */ + index = 0; + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status == SAME) + continue; + + if (!sym->has_func_profiling) { + log_debug("function %s has no fentry/mcount call, no mcount record is needed\n", + sym->name); + continue; + } + + switch(kelf->arch) { + case PPC64: { + bool found = false; + + list_for_each_entry(rela, &sym->sec->rela->relas, list) + if (!strcmp(rela->sym->name, "_mcount")) { + found = true; + break; + } + + if (!found) + ERROR("%s: unexpected missing call to _mcount()", __func__); + + insn_offset = rela->offset; + break; + } + case X86_64: { + unsigned char *insn; + void *newdata; + + rela = list_first_entry(&sym->sec->rela->relas, struct rela, list); + + /* + * For "call fentry", the relocation points to 1 byte past the + * beginning of the instruction. + */ + insn_offset = rela->offset - 1; + + /* + * R_X86_64_NONE is only generated by older versions of + * kernel/gcc which use the mcount script. There's a + * NOP instead of a call to fentry. + */ + if (rela->type != R_X86_64_NONE) + break; + + /* Make a writable copy of the text section data */ + newdata = malloc(sym->sec->data->d_size); + if (!newdata) + ERROR("malloc"); + memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size); + sym->sec->data->d_buf = newdata; + insn = newdata; + + /* + * Replace the NOP with a call to fentry. The fentry + * rela symbol is already there, just need to change + * the relocation type accordingly. + */ + insn = sym->sec->data->d_buf; + if (insn[0] != 0xf) + ERROR("%s: unexpected instruction at the start of the function", sym->name); + insn[0] = 0xe8; + insn[1] = 0; + insn[2] = 0; + insn[3] = 0; + insn[4] = 0; + + rela->type = R_X86_64_PC32; + break; + } + case S390: { + insn_offset = sym->sym.st_value; + break; + } + default: + ERROR("unsupported arch"); + } + + /* + * 'rela' points to the mcount/fentry call. + * + * Create a .rela__mcount_loc entry which also points to it. + */ + ALLOC_LINK(mcount_rela, &relasec->relas); + mcount_rela->sym = sym; + mcount_rela->type = absolute_rela_type(kelf); + mcount_rela->addend = insn_offset - sym->sym.st_value; + mcount_rela->offset = (unsigned int) (index * sizeof(*funcs)); + + index++; + } + + /* sanity check, index should equal nr */ + if (index != nr) + ERROR("size mismatch in funcs sections"); +} + +/* + * This function strips out symbols that were referenced by changed rela + * sections, but the rela entries that referenced them were converted to + * dynrelas and are no longer needed. + */ +static void kpatch_strip_unneeded_syms(struct kpatch_elf *kelf, + struct lookup_table *table) +{ + struct symbol *sym, *safe; + + list_for_each_entry_safe(sym, safe, &kelf->symbols, list) { + if (sym->strip == SYMBOL_STRIP) { + list_del(&sym->list); + free(sym); + } + } +} + +static void kpatch_create_strings_elements(struct kpatch_elf *kelf) +{ + struct section *sec; + struct symbol *sym; + + /* create .kpatch.strings */ + + /* allocate section resources */ + ALLOC_LINK(sec, &kelf->sections); + sec->name = ".kpatch.strings"; + + /* set data */ + sec->data = malloc(sizeof(*sec->data)); + if (!sec->data) + ERROR("malloc"); + sec->data->d_type = ELF_T_BYTE; + + /* set section header */ + sec->sh.sh_type = SHT_PROGBITS; + sec->sh.sh_entsize = 1; + sec->sh.sh_addralign = 1; + sec->sh.sh_flags = SHF_ALLOC; + + /* create .kpatch.strings section symbol (reuse sym variable) */ + + ALLOC_LINK(sym, &kelf->symbols); + sym->sec = sec; + sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); + sym->type = STT_SECTION; + sym->bind = STB_LOCAL; + sym->name = ".kpatch.strings"; +} + +static void kpatch_build_strings_section_data(struct kpatch_elf *kelf) +{ + struct string *string; + struct section *sec; + size_t size; + char *strtab; + + sec = find_section_by_name(&kelf->sections, ".kpatch.strings"); + if (!sec) + ERROR("can't find .kpatch.strings"); + + /* determine size */ + size = 0; + list_for_each_entry(string, &kelf->strings, list) + size += strlen(string->name) + 1; + + /* allocate section resources */ + strtab = malloc(size); + if (!strtab) + ERROR("malloc"); + sec->data->d_buf = strtab; + sec->data->d_size = size; + + /* populate strings section data */ + list_for_each_entry(string, &kelf->strings, list) { + strcpy(strtab, string->name); + strtab += strlen(string->name) + 1; + } +} + +/* + * Don't allow sibling calls from patched functions on ppc64le. Before doing a + * sibling call, the patched function restores the stack to its caller's stack. + * The kernel-generated stub then writes the patch module's r2 (toc) value to + * the caller's stack, corrupting it, eventually causing a panic after it + * returns to the caller and the caller tries to use the livepatch module's toc + * value. + * + * In theory we could instead a) generate a custom stub, or b) modify the + * kernel livepatch_handler code to save/restore the stack r2 value, but this + * is easier for now. + */ +static void kpatch_no_sibling_calls_ppc64le(struct kpatch_elf *kelf) +{ + struct symbol *sym; + unsigned char *insn; + unsigned int offset; + + if (kelf->arch != PPC64) + return; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status != CHANGED) + continue; + + for (offset = 0; offset < sym->sec->data->d_size; offset += 4) { + + insn = sym->sec->data->d_buf + offset; + + /* + * The instruction 0x48000000 can be assumed to be a + * sibling call: + * + * Bits 0-5 (opcode) == 0x9: unconditional branch + * Bit 30 (absolute) == 0: relative address + * Bit 31 (link) == 0: doesn't set LR (not a call) + * + * Bits 6-29 (branch address) == zero, which means + * it's either a branch to self (infinite loop), or + * there's a REL24 relocation for the address which + * will be written by the linker or the kernel. + */ + if (insn[3] != 0x48 || insn[2] != 0x00 || + insn[1] != 0x00 || insn[0] != 0x00) + continue; + + /* Make sure it's not a branch-to-self: */ + if (!find_rela_by_offset(sym->sec->rela, offset)) + continue; + + ERROR("Found an unsupported sibling call at %s()+0x%lx. Add __attribute__((optimize(\"-fno-optimize-sibling-calls\"))) to %s() definition.", + sym->name, sym->sym.st_value + offset, sym->name); + } + } +} + +/* Check which functions have fentry/mcount calls; save this info for later use. */ +static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) +{ + struct symbol *sym; + struct rela *rela; + unsigned char *insn; + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || !sym->sec || !sym->sec->rela) + continue; + + switch(kelf->arch) { + case PPC64: + list_for_each_entry(rela, &sym->sec->rela->relas, list) { + if (!strcmp(rela->sym->name, "_mcount")) { + sym->has_func_profiling = 1; + break; + } + } + break; + case X86_64: + rela = list_first_entry(&sym->sec->rela->relas, struct rela, + list); + if ((rela->type != R_X86_64_NONE && + rela->type != R_X86_64_PC32 && + rela->type != R_X86_64_PLT32) || + strcmp(rela->sym->name, "__fentry__")) + continue; + + sym->has_func_profiling = 1; + break; + case S390: + /* Check for compiler generated fentry nop - jgnop 0 */ + insn = sym->sec->data->d_buf; + if (insn[0] == 0xc0 && insn[1] == 0x04 && + insn[2] == 0x00 && insn[3] == 0x00 && + insn[4] == 0x00 && insn[5] == 0x00) + sym->has_func_profiling = 1; + break; + default: + ERROR("unsupported arch"); + } + } +} + +struct arguments { + char *args[7]; + bool debug, klp_arch; +}; + +static char args_doc[] = "original.o patched.o parent-name parent-symtab Module.symvers patch-module-name output.o"; + +static struct argp_option options[] = { + {"debug", 'd', NULL, 0, "Show debug output" }, + {"klp-arch", 'a', NULL, 0, "Kernel supports .klp.arch section" }, + { NULL } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; + + switch (key) + { + case 'd': + arguments->debug = 1; + break; + case 'a': + arguments->klp_arch = 1; + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 7) + /* Too many arguments. */ + argp_usage (state); + arguments->args[state->arg_num] = arg; + break; + case ARGP_KEY_END: + if (state->arg_num < 7) + /* Not enough arguments. */ + argp_usage (state); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, NULL }; + +int main(int argc, char *argv[]) +{ + struct kpatch_elf *kelf_orig, *kelf_patched, *kelf_out; + struct arguments arguments; + int num_changed, callbacks_exist, new_globals_exist; + struct lookup_table *lookup; + struct section *relasec, *symtab; + char *orig_obj, *patched_obj, *parent_name; + char *parent_symtab, *mod_symvers, *patch_name, *output_obj; + + memset(&arguments, 0, sizeof(arguments)); + argp_parse (&argp, argc, argv, 0, NULL, &arguments); + if (arguments.debug) + loglevel = DEBUG; + if (arguments.klp_arch) + KLP_ARCH = true; + + elf_version(EV_CURRENT); + + orig_obj = arguments.args[0]; + patched_obj = arguments.args[1]; + parent_name = arguments.args[2]; + parent_symtab = arguments.args[3]; + mod_symvers = arguments.args[4]; + patch_name = arguments.args[5]; + output_obj = arguments.args[6]; + + childobj = basename(orig_obj); + + kelf_orig = kpatch_elf_open(orig_obj); + kelf_patched = kpatch_elf_open(patched_obj); + kpatch_find_func_profiling_calls(kelf_orig); + kpatch_find_func_profiling_calls(kelf_patched); + + kpatch_compare_elf_headers(kelf_orig->elf, kelf_patched->elf); + kpatch_check_program_headers(kelf_orig->elf); + kpatch_check_program_headers(kelf_patched->elf); + + kpatch_bundle_symbols(kelf_orig); + kpatch_bundle_symbols(kelf_patched); + + kpatch_detect_child_functions(kelf_orig); + kpatch_detect_child_functions(kelf_patched); + + lookup = lookup_open(parent_symtab, parent_name, mod_symvers, kelf_orig); + + kpatch_mark_grouped_sections(kelf_patched); + kpatch_replace_sections_syms(kelf_orig); + kpatch_replace_sections_syms(kelf_patched); + + kpatch_correlate_elfs(kelf_orig, kelf_patched); + kpatch_correlate_static_local_variables(kelf_orig, kelf_patched); + + /* + * After this point, we don't care about kelf_orig anymore. + * We access its sections via the twin pointers in the + * section, symbol, and rela lists of kelf_patched. + */ + kpatch_mark_ignored_sections(kelf_patched); + kpatch_compare_correlated_elements(kelf_patched); + kpatch_mark_ignored_functions_same(kelf_patched); + kpatch_mark_ignored_sections_same(kelf_patched); + kpatch_check_func_profiling_calls(kelf_patched); + kpatch_elf_teardown(kelf_orig); + kpatch_elf_free(kelf_orig); + + kpatch_include_standard_elements(kelf_patched); + num_changed = kpatch_include_changed_functions(kelf_patched); + callbacks_exist = kpatch_include_callback_elements(kelf_patched); + kpatch_include_force_elements(kelf_patched); + new_globals_exist = kpatch_include_new_globals(kelf_patched); + kpatch_include_debug_sections(kelf_patched); + + kpatch_process_special_sections(kelf_patched, lookup); + + kpatch_print_changes(kelf_patched); + kpatch_dump_kelf(kelf_patched); + + kpatch_verify_patchability(kelf_patched); + + if (!num_changed && !new_globals_exist) { + if (callbacks_exist) + log_debug("no changed functions were found, but callbacks exist\n"); + else { + log_debug("no changed functions were found\n"); + return EXIT_STATUS_NO_CHANGE; + } + } + + /* this is destructive to kelf_patched */ + kpatch_migrate_included_elements(kelf_patched, &kelf_out); + + /* + * Teardown kelf_patched since we shouldn't access sections or symbols + * through it anymore. Don't free however, since our section and symbol + * name fields still point to strings in the Elf object owned by + * kpatch_patched. + */ + kpatch_elf_teardown(kelf_patched); + + kpatch_no_sibling_calls_ppc64le(kelf_out); + + /* create strings, patches, and dynrelas sections */ + kpatch_create_strings_elements(kelf_out); + kpatch_create_patches_sections(kelf_out, lookup, parent_name); + kpatch_create_intermediate_sections(kelf_out, lookup, parent_name, patch_name); + kpatch_create_kpatch_arch_section(kelf_out, parent_name); + kpatch_create_callbacks_objname_rela(kelf_out, parent_name); + kpatch_build_strings_section_data(kelf_out); + + kpatch_create_mcount_sections(kelf_out); + + /* + * At this point, the set of output sections and symbols is + * finalized. Reorder the symbols into linker-compliant + * order and index all the symbols and sections. After the + * indexes have been established, update index data + * throughout the structure. + */ + kpatch_reorder_symbols(kelf_out); + kpatch_strip_unneeded_syms(kelf_out, lookup); + kpatch_reindex_elements(kelf_out); + + /* + * Update rela section headers and rebuild the rela section data + * buffers from the relas lists. + */ + symtab = find_section_by_name(&kelf_out->sections, ".symtab"); + if (!symtab) + ERROR("missing .symtab section"); + + list_for_each_entry(relasec, &kelf_out->sections, list) { + if (!is_rela_section(relasec)) + continue; + relasec->sh.sh_link = symtab->index; + relasec->sh.sh_info = relasec->base->index; + kpatch_rebuild_rela_section_data(relasec); + } + kpatch_check_relocations(kelf_out); + + kpatch_create_shstrtab(kelf_out); + kpatch_create_strtab(kelf_out); + kpatch_create_symtab(kelf_out); + kpatch_dump_kelf(kelf_out); + kpatch_write_output_elf(kelf_out, kelf_patched->elf, output_obj, 0664); + + lookup_close(lookup); + kpatch_elf_free(kelf_patched); + kpatch_elf_teardown(kelf_out); + kpatch_elf_free(kelf_out); + + return EXIT_STATUS_SUCCESS; +} diff --git a/kpatch-build/create-diff-object.d b/kpatch-build/create-diff-object.d new file mode 100644 index 0000000..133eae6 --- /dev/null +++ b/kpatch-build/create-diff-object.d @@ -0,0 +1,16 @@ +create-diff-object.o: create-diff-object.c list.h lookup.h kpatch-elf.h \ + log.h kpatch.h ../kmod/patch/kpatch-patch.h kpatch-intermediate.h + +list.h: + +lookup.h: + +kpatch-elf.h: + +log.h: + +kpatch.h: + +../kmod/patch/kpatch-patch.h: + +kpatch-intermediate.h: diff --git a/kpatch-build/create-diff-object.o b/kpatch-build/create-diff-object.o new file mode 100644 index 0000000000000000000000000000000000000000..1ca8547fe616aa6f80cdf33a6e864bdcb7082ccc GIT binary patch literal 164904 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 Z%j~4v-^;Mh(uXUZSBJ8xuKm;W{{UhsZQ}p{ literal 0 HcmV?d00001 diff --git a/kpatch-build/create-klp-module b/kpatch-build/create-klp-module new file mode 100755 index 0000000000000000000000000000000000000000..e8e3d2f23627c2adad1123e0e483d53379a3f3db GIT binary patch literal 105952 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 cV08Ot{Gl96L$;1);6c^M$;-fo~MNZE%fhg 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 +#include +#include +#include + +#include "log.h" +#include "kpatch-elf.h" +#include "kpatch-intermediate.h" + +/* For log.h */ +char *childobj; +enum loglevel loglevel = NORMAL; + +/* + * Add a symbol from .kpatch.symbols to the symbol table + * + * If a symbol matching the .kpatch.symbols entry already + * exists, return it. + */ +static struct symbol *find_or_add_ksym_to_symbols(struct kpatch_elf *kelf, + struct section *ksymsec, + char *strings, int offset) +{ + struct kpatch_symbol *ksyms, *ksym; + struct symbol *sym; + struct rela *rela; + char *objname, *name; + char pos[32], buf[256]; + unsigned int index; + + ksyms = ksymsec->data->d_buf; + index = (unsigned int)(offset / sizeof(*ksyms)); + ksym = &ksyms[index]; + + /* Get name of ksym */ + rela = find_rela_by_offset(ksymsec->rela, + (unsigned int)(offset + offsetof(struct kpatch_symbol, name))); + if (!rela) + ERROR("name of ksym not found?"); + + name = strings + rela->addend; + + /* Get objname of ksym */ + rela = find_rela_by_offset(ksymsec->rela, + (unsigned int)(offset + offsetof(struct kpatch_symbol, objname))); + if (!rela) + ERROR("objname of ksym not found?"); + + objname = strings + rela->addend; + + snprintf(pos, 32, "%lu", ksym->sympos); + /* .klp.sym.objname.name,pos */ + snprintf(buf, 256, KLP_SYM_PREFIX "%s.%s,%s", objname, name, pos); + + /* Look for an already allocated symbol */ + list_for_each_entry(sym, &kelf->symbols, list) { + if (!strcmp(buf, sym->name)) + return sym; + } + + ALLOC_LINK(sym, NULL); + sym->name = strdup(buf); + if (!sym->name) + ERROR("strdup"); + sym->type = ksym->type; + sym->bind = ksym->bind; + /* + * Note that st_name will be set in kpatch_create_strtab(), + * and sym->index is set in kpatch_reindex_elements() + */ + sym->sym.st_shndx = SHN_LIVEPATCH; + sym->sym.st_info = (unsigned char)GELF_ST_INFO(sym->bind, sym->type); + /* + * Figure out where to put the new symbol: + * a) locals need to be grouped together, before globals + * b) globals can be tacked into the end of the list + */ + if (is_local_sym(sym)) { + struct list_head *head; + struct symbol *s; + + head = &kelf->symbols; + list_for_each_entry(s, &kelf->symbols, list) { + if (!is_local_sym(s)) + break; + head = &s->list; + } + list_add_tail(&sym->list, head); + } else { + list_add_tail(&sym->list, &kelf->symbols); + } + + return sym; +} + +/* + * Create a klp rela section given the base section and objname + * + * If a klp rela section matching the base section and objname + * already exists, return it. + */ +static struct section *find_or_add_klp_relasec(struct kpatch_elf *kelf, + struct section *base, + char *objname) +{ + struct section *relasec; + char buf[256]; + + /* .klp.rela.objname.secname */ + snprintf(buf, 256, KLP_RELASEC_PREFIX "%s.%s", objname, base->name); + + list_for_each_entry(relasec, &kelf->sections, list) { + if (!strcmp(relasec->name, buf)) + return relasec; + } + + ALLOC_LINK(relasec, &kelf->sections); + relasec->name = strdup(buf); + if (!relasec->name) + ERROR("strdup"); + relasec->base = base; + + INIT_LIST_HEAD(&relasec->relas); + + relasec->data = malloc(sizeof(*relasec->data)); + if (!relasec->data) + ERROR("malloc"); + relasec->data->d_type = ELF_T_RELA; + + /* sh_info and sh_link are set when rebuilding rela sections */ + relasec->sh.sh_type = SHT_RELA; + relasec->sh.sh_entsize = sizeof(GElf_Rela); + relasec->sh.sh_addralign = 8; + relasec->sh.sh_flags = SHF_RELA_LIVEPATCH | SHF_INFO_LINK | SHF_ALLOC; + + return relasec; +} + +/* + * Create klp relocation sections and klp symbols from .kpatch.relocations + * and .kpatch.symbols sections + * + * For every entry in .kpatch.relocations: + * 1) Allocate a symbol for the corresponding .kpatch.symbols entry if + * it doesn't already exist (find_or_add_ksym_to_symbols()) + * This is the symbol that the relocation points to (rela->sym) + * 2) Allocate a rela, and add it to the corresponding .klp.rela. section. If + * the matching .klp.rela. section (given the base section and objname) + * doesn't exist yet, create it (find_or_add_klp_relasec()) + */ +static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section *krelasec, + struct section *ksymsec, char *strings) +{ + struct section *klp_relasec; + struct kpatch_relocation *krelas; + struct symbol *sym, *dest; + struct rela *rela; + char *objname; + unsigned int nr, index, offset, dest_off; + + krelas = krelasec->data->d_buf; + nr = (unsigned int)(krelasec->data->d_size / sizeof(*krelas)); + + for (index = 0; index < nr; index++) { + offset = (unsigned int)(index * sizeof(*krelas)); + + /* Get the rela dest sym + offset */ + rela = find_rela_by_offset(krelasec->rela, + offset + offsetof(struct kpatch_relocation, dest)); + if (!rela) + ERROR("find_rela_by_offset"); + + dest = rela->sym; + dest_off = (unsigned int)rela->addend; + + /* Get the name of the object the dest belongs to */ + rela = find_rela_by_offset(krelasec->rela, + (unsigned int)(offset + offsetof(struct kpatch_relocation, objname))); + if (!rela) + ERROR("find_rela_by_offset"); + + objname = strings + rela->addend; + + /* Get the .kpatch.symbol entry for the rela src */ + rela = find_rela_by_offset(krelasec->rela, + (unsigned int)(offset + offsetof(struct kpatch_relocation, ksym))); + if (!rela) + ERROR("find_rela_by_offset"); + + /* Create (or find) a klp symbol from the rela src entry */ + sym = find_or_add_ksym_to_symbols(kelf, ksymsec, strings, + (unsigned int)rela->addend); + if (!sym) + ERROR("error finding or adding ksym to symtab"); + + /* Create (or find) the .klp.rela. section for the dest sec and object */ + klp_relasec = find_or_add_klp_relasec(kelf, dest->sec, objname); + if (!klp_relasec) + ERROR("error finding or adding klp relasec"); + + /* Add the klp rela to the .klp.rela. section */ + ALLOC_LINK(rela, &klp_relasec->relas); + rela->offset = (unsigned int)(dest->sym.st_value + dest_off); + rela->type = krelas[index].type; + rela->sym = sym; + rela->addend = krelas[index].addend; + } +} + +/* + * Create .klp.arch. sections by iterating through the .kpatch.arch section + * + * A .kpatch.arch section is just an array of kpatch_arch structs: + * + * struct kpatch_arch { + * unsigned long sec; + * char *objname; + * }; + * + * There are two relas associated with each kpatch arch entry, one that points + * to the section of interest (.parainstructions or .altinstructions), and one + * rela points to the name of the object the section belongs to in + * .kpatch.strings. This gives us the necessary information to create .klp.arch + * sections, which use the '.klp.arch.objname.secname' name format. + */ +static void create_klp_arch_sections(struct kpatch_elf *kelf, char *strings) +{ + struct section *karch, *sec, *base = NULL; + struct rela *rela, *rela2; + char *secname, *objname = NULL; + char buf[256]; + unsigned int nr, index, offset; + size_t new_size, old_size; + + karch = find_section_by_name(&kelf->sections, ".kpatch.arch"); + if (!karch) + return; + + nr = (unsigned int)(karch->data->d_size / sizeof(struct kpatch_arch)); + + for (index = 0; index < nr; index++) { + offset = (unsigned int)(index * sizeof(struct kpatch_arch)); + + /* Get the base section (.parainstructions or .altinstructions) */ + rela = find_rela_by_offset(karch->rela, + offset + offsetof(struct kpatch_arch, sec)); + if (!rela) + ERROR("find_rela_by_offset"); + + base = rela->sym->sec; + if (!base) + ERROR("base sec of kpatch_arch entry not found"); + + /* Get the name of the object the base section belongs to */ + rela = find_rela_by_offset(karch->rela, + (unsigned int)(offset + offsetof(struct kpatch_arch, objname))); + if (!rela) + ERROR("find_rela_by_offset"); + + objname = strings + rela->addend; + + /* Example: .klp.arch.vmlinux..parainstructions */ + snprintf(buf, 256, "%s%s.%s", KLP_ARCH_PREFIX, objname, base->name); + + /* Check if the .klp.arch. section already exists */ + sec = find_section_by_name(&kelf->sections, buf); + if (!sec) { + secname = strdup(buf); + if (!secname) + ERROR("strdup"); + + /* Start with a new section with size 0 first */ + sec = create_section_pair(kelf, secname, 1, 0); + } + + /* + * Merge .klp.arch. sections if necessary + * + * Example: + * If there are multiple .parainstructions sections for vmlinux + * (this can happen when, using the --unique option for ld, + * we've linked together multiple .o's with .parainstructions + * sections for the same object), they will be merged under a + * single .klp.arch.vmlinux..parainstructions section + */ + old_size = sec->data->d_size; + + /* + * Due to a quirk in how .parainstructions gets linked, the + * section size doesn't encompass the last 4 bytes of the last + * entry. Align the old size properly before merging. + */ + if (!strcmp(base->name, ".parainstructions")) { + char *str; + static int align_mask = 0; + + if (!align_mask) { + str = getenv("PARA_STRUCT_SIZE"); + if (!str) + ERROR("PARA_STRUCT_SIZE not set"); + + align_mask = atoi(str) - 1; + } + + old_size = (old_size + align_mask) & ~align_mask; + } + + new_size = old_size + base->data->d_size; + sec->data->d_buf = realloc(sec->data->d_buf, new_size); + if (!sec->data->d_buf) + ERROR("realloc"); + sec->data->d_size = new_size; + sec->sh.sh_size = sec->data->d_size; + memcpy(sec->data->d_buf + old_size, + base->data->d_buf, base->data->d_size); + + list_for_each_entry(rela, &base->rela->relas, list) { + ALLOC_LINK(rela2, &sec->rela->relas); + rela2->sym = rela->sym; + rela2->type = rela->type; + rela2->addend = rela->addend; + rela2->offset = (unsigned int)(old_size + rela->offset); + } + } +} + +/* + * We can't keep these sections since the module loader will apply them before + * the patch module gets a chance to load (that's why we copied these sections + * into .klp.arch. sections. Hence we remove them here. + */ +static void remove_arch_sections(struct kpatch_elf *kelf) +{ + size_t i; + char *arch_sections[] = { + ".parainstructions", + ".rela.parainstructions", + ".altinstructions", + ".rela.altinstructions" + }; + + for (i = 0; i < sizeof(arch_sections)/sizeof(arch_sections[0]); i++) + kpatch_remove_and_free_section(kelf, arch_sections[i]); + +} + +static void remove_intermediate_sections(struct kpatch_elf *kelf) +{ + size_t i; + char *intermediate_sections[] = { + ".kpatch.symbols", + ".rela.kpatch.symbols", + ".kpatch.relocations", + ".rela.kpatch.relocations", + ".kpatch.arch", + ".rela.kpatch.arch" + }; + + for (i = 0; i < sizeof(intermediate_sections)/sizeof(intermediate_sections[0]); i++) + kpatch_remove_and_free_section(kelf, intermediate_sections[i]); +} + +struct arguments { + char *args[2]; + int debug; + int no_klp_arch; +}; + +static char args_doc[] = "input.ko output.ko"; + +static struct argp_option options[] = { + {"debug", 'd', 0, 0, "Show debug output" }, + {"no-klp-arch-sections", 'n', 0, 0, "Do not output .klp.arch.* sections" }, + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; + + switch (key) + { + case 'd': + arguments->debug = 1; + break; + case 'n': + arguments->no_klp_arch = 1; + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 2) + /* Too many arguments. */ + argp_usage (state); + arguments->args[state->arg_num] = arg; + break; + case ARGP_KEY_END: + if (state->arg_num < 2) + /* Not enough arguments. */ + argp_usage (state); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, 0 }; + +int main(int argc, char *argv[]) +{ + struct kpatch_elf *kelf; + struct section *symtab, *relasec; + struct section *ksymsec, *krelasec, *strsec; + struct arguments arguments; + char *strings; + unsigned int ksyms_nr, krelas_nr; + + memset(&arguments, 0, sizeof(arguments)); + argp_parse (&argp, argc, argv, 0, 0, &arguments); + if (arguments.debug) + loglevel = DEBUG; + + elf_version(EV_CURRENT); + + childobj = basename(arguments.args[0]); + + kelf = kpatch_elf_open(arguments.args[0]); + + /* + * Sanity checks: + * - Make sure all the required sections exist + * - Make sure that the number of entries in + * .kpatch.{symbols,relocations} match + */ + strsec = find_section_by_name(&kelf->sections, ".kpatch.strings"); + if (!strsec) + ERROR("missing .kpatch.strings"); + strings = strsec->data->d_buf; + + ksymsec = find_section_by_name(&kelf->sections, ".kpatch.symbols"); + if (!ksymsec) + ERROR("missing .kpatch.symbols section"); + ksyms_nr = (unsigned int)(ksymsec->data->d_size / sizeof(struct kpatch_symbol)); + + krelasec = find_section_by_name(&kelf->sections, ".kpatch.relocations"); + if (!krelasec) + ERROR("missing .kpatch.relocations section"); + krelas_nr = (unsigned int)(krelasec->data->d_size / sizeof(struct kpatch_relocation)); + + if (krelas_nr != ksyms_nr) + ERROR("number of krelas and ksyms do not match"); + + /* + * Create klp rela sections and klp symbols from + * .kpatch.{relocations,symbols} sections + */ + create_klp_relasecs_and_syms(kelf, krelasec, ksymsec, strings); + + /* + * If --no-klp-arch-sections wasn't set, additionally + * create .klp.arch. sections + */ + if (!arguments.no_klp_arch) { + create_klp_arch_sections(kelf, strings); + remove_arch_sections(kelf); + } + + remove_intermediate_sections(kelf); + kpatch_reindex_elements(kelf); + + /* Rebuild rela sections, new klp rela sections will be rebuilt too. */ + symtab = find_section_by_name(&kelf->sections, ".symtab"); + if (!symtab) + ERROR("missing .symtab section"); + + list_for_each_entry(relasec, &kelf->sections, list) { + if (!is_rela_section(relasec)) + continue; + relasec->sh.sh_link = symtab->index; + relasec->sh.sh_info = relasec->base->index; + kpatch_rebuild_rela_section_data(relasec); + } + + kpatch_create_shstrtab(kelf); + kpatch_create_strtab(kelf); + kpatch_create_symtab(kelf); + + kpatch_write_output_elf(kelf, kelf->elf, arguments.args[1], 0664); + kpatch_elf_teardown(kelf); + kpatch_elf_free(kelf); + + return 0; +} diff --git a/kpatch-build/create-klp-module.d b/kpatch-build/create-klp-module.d new file mode 100644 index 0000000..2a278fe --- /dev/null +++ b/kpatch-build/create-klp-module.d @@ -0,0 +1,12 @@ +create-klp-module.o: create-klp-module.c log.h kpatch.h kpatch-elf.h \ + list.h kpatch-intermediate.h + +log.h: + +kpatch.h: + +kpatch-elf.h: + +list.h: + +kpatch-intermediate.h: diff --git a/kpatch-build/create-klp-module.o b/kpatch-build/create-klp-module.o new file mode 100644 index 0000000000000000000000000000000000000000..93f8a6c87b121df98d4211e732a3fc8edeaf2e21 GIT binary patch literal 35704 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 literal 0 HcmV?d00001 diff --git a/kpatch-build/create-kpatch-module.c b/kpatch-build/create-kpatch-module.c new file mode 100644 index 0000000..2884f93 --- /dev/null +++ b/kpatch-build/create-kpatch-module.c @@ -0,0 +1,265 @@ +/* + * create-kpatch-module.c + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "log.h" +#include "kpatch-elf.h" +#include "kpatch-intermediate.h" +#include "kpatch-patch.h" + +/* For log.h */ +char *childobj; +enum loglevel loglevel = NORMAL; + +/* + * Create .kpatch.dynrelas from .kpatch.relocations and .kpatch.symbols sections + * + * Iterate through .kpatch.relocations and fill in the corresponding dynrela + * entry using information from .kpatch.relocations and .kpatch.symbols + */ +static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section *krelasec, + struct section *ksymsec, struct section *strsec) +{ + struct kpatch_patch_dynrela *dynrelas; + struct kpatch_relocation *krelas; + struct kpatch_symbol *ksym, *ksyms; + struct section *dynsec; + struct symbol *sym; + struct rela *rela; + unsigned int index, nr, offset, dest_offset, objname_offset, name_offset; + unsigned int type; + long addend; + char *target_name; + + ksyms = ksymsec->data->d_buf; + krelas = krelasec->data->d_buf; + nr = (unsigned int)(krelasec->data->d_size / sizeof(*krelas)); + + dynsec = create_section_pair(kelf, ".kpatch.dynrelas", sizeof(*dynrelas), nr); + dynrelas = dynsec->data->d_buf; + + for (index = 0; index < nr; index++) { + offset = index * (unsigned int)sizeof(*krelas); + + /* + * To fill in each dynrela entry, find dest location, + * objname offset, ksym, and symbol name offset + */ + + /* Get dest location */ + rela = find_rela_by_offset(krelasec->rela, + offset + offsetof(struct kpatch_relocation, dest)); + if (!rela) + ERROR("find_rela_by_offset"); + sym = rela->sym; + dest_offset = (unsigned int)rela->addend; + + /* Get objname offset */ + rela = find_rela_by_offset(krelasec->rela, + (unsigned int)(offset + offsetof(struct kpatch_relocation, objname))); + if (!rela) + ERROR("find_rela_by_offset"); + objname_offset = (unsigned int)rela->addend; + + /* Get ksym (.kpatch.symbols entry) and symbol name offset */ + rela = find_rela_by_offset(krelasec->rela, + (unsigned int)(offset + offsetof(struct kpatch_relocation, ksym))); + if (!rela) + ERROR("find_rela_by_offset"); + ksym = ksyms + (rela->addend / sizeof(*ksyms)); + + offset = (unsigned int )(index * sizeof(*ksyms)); + rela = find_rela_by_offset(ksymsec->rela, + (unsigned int)(offset + offsetof(struct kpatch_symbol, name))); + if (!rela) + ERROR("find_rela_by_offset"); + name_offset = (unsigned int)rela->addend; + + /* Fill in dynrela entry */ + type = krelas[index].type; + addend = krelas[index].addend; + if (type == R_X86_64_64 && (addend > INT_MAX || addend <= INT_MIN)) { + target_name = (char *)strsec->data->d_buf + name_offset; + ERROR("got R_X86_64_64 dynrela for '%s' with addend too large or too small for an int: %lx", + target_name, addend); + } + + dynrelas[index].src = ksym->src; + dynrelas[index].addend = addend; + dynrelas[index].type = type; + dynrelas[index].external = krelas[index].external; + dynrelas[index].sympos = ksym->sympos; + + /* dest */ + ALLOC_LINK(rela, &dynsec->rela->relas); + rela->sym = sym; + rela->type = R_X86_64_64; + rela->addend = dest_offset; + rela->offset = (unsigned int)(index * sizeof(*dynrelas)); + + /* name */ + ALLOC_LINK(rela, &dynsec->rela->relas); + rela->sym = strsec->secsym; + rela->type = R_X86_64_64; + rela->addend = name_offset; + rela->offset = (unsigned int)(index * sizeof(*dynrelas) + \ + offsetof(struct kpatch_patch_dynrela, name)); + + /* objname */ + ALLOC_LINK(rela, &dynsec->rela->relas); + rela->sym = strsec->secsym; + rela->type = R_X86_64_64; + rela->addend = objname_offset; + rela->offset = (unsigned int)(index * sizeof(*dynrelas) + \ + offsetof(struct kpatch_patch_dynrela, objname)); + } +} + +static void remove_intermediate_sections(struct kpatch_elf *kelf) +{ + size_t i; + char *intermediate_sections[] = { + ".kpatch.symbols", + ".rela.kpatch.symbols", + ".kpatch.relocations", + ".rela.kpatch.relocations", + ".kpatch.arch", + ".rela.kpatch.arch" + }; + + for (i = 0; i < sizeof(intermediate_sections)/sizeof(intermediate_sections[0]); i++) + kpatch_remove_and_free_section(kelf, intermediate_sections[i]); +} + +struct arguments { + char *args[2]; + int debug; +}; + +static char args_doc[] = "input.o output.o"; + +static struct argp_option options[] = { + {"debug", 'd', 0, 0, "Show debug output" }, + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; + + switch (key) + { + case 'd': + arguments->debug = 1; + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 2) + /* Too many arguments. */ + argp_usage (state); + arguments->args[state->arg_num] = arg; + break; + case ARGP_KEY_END: + if (state->arg_num < 2) + /* Not enough arguments. */ + argp_usage (state); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, 0 }; + +int main(int argc, char *argv[]) +{ + struct kpatch_elf *kelf; + struct section *symtab, *sec; + struct section *ksymsec, *krelasec, *strsec; + struct arguments arguments; + unsigned int ksyms_nr, krelas_nr; + + arguments.debug = 0; + argp_parse (&argp, argc, argv, 0, 0, &arguments); + if (arguments.debug) + loglevel = DEBUG; + + elf_version(EV_CURRENT); + + childobj = basename(arguments.args[0]); + + kelf = kpatch_elf_open(arguments.args[0]); + + /* + * Sanity checks: + * - Make sure all the required sections exist + * - Make sure that the number of entries in + * .kpatch.{symbols,relocations} match + */ + strsec = find_section_by_name(&kelf->sections, ".kpatch.strings"); + if (!strsec) + ERROR("missing .kpatch.strings"); + + ksymsec = find_section_by_name(&kelf->sections, ".kpatch.symbols"); + if (!ksymsec) + ERROR("missing .kpatch.symbols section"); + ksyms_nr = (unsigned int)(ksymsec->data->d_size / sizeof(struct kpatch_symbol)); + + krelasec = find_section_by_name(&kelf->sections, ".kpatch.relocations"); + if (!krelasec) + ERROR("missing .kpatch.relocations section"); + krelas_nr = (unsigned int)(krelasec->data->d_size / sizeof(struct kpatch_relocation)); + + if (krelas_nr != ksyms_nr) + ERROR("number of krelas and ksyms do not match"); + + /* Create dynrelas from .kpatch.{relocations,symbols} sections */ + create_dynamic_rela_sections(kelf, krelasec, ksymsec, strsec); + remove_intermediate_sections(kelf); + + kpatch_reindex_elements(kelf); + + symtab = find_section_by_name(&kelf->sections, ".symtab"); + if (!symtab) + ERROR("missing .symtab section"); + + list_for_each_entry(sec, &kelf->sections, list) { + if (!is_rela_section(sec)) + continue; + sec->sh.sh_link = symtab->index; + sec->sh.sh_info = sec->base->index; + kpatch_rebuild_rela_section_data(sec); + } + + kpatch_create_shstrtab(kelf); + kpatch_create_strtab(kelf); + kpatch_create_symtab(kelf); + + kpatch_write_output_elf(kelf, kelf->elf, arguments.args[1], 0664); + kpatch_elf_teardown(kelf); + kpatch_elf_free(kelf); + + return 0; +} diff --git a/kpatch-build/create-kpatch-module.d b/kpatch-build/create-kpatch-module.d new file mode 100644 index 0000000..30127b1 --- /dev/null +++ b/kpatch-build/create-kpatch-module.d @@ -0,0 +1,14 @@ +create-kpatch-module.o: create-kpatch-module.c log.h kpatch.h \ + kpatch-elf.h list.h kpatch-intermediate.h ../kmod/patch/kpatch-patch.h + +log.h: + +kpatch.h: + +kpatch-elf.h: + +list.h: + +kpatch-intermediate.h: + +../kmod/patch/kpatch-patch.h: diff --git a/kpatch-build/create-kpatch-module.o b/kpatch-build/create-kpatch-module.o new file mode 100644 index 0000000000000000000000000000000000000000..3dd8613a3099a77765bc7170d515838268e4cce0 GIT binary patch literal 26888 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 6000 +#include "gcc-plugin.h" +#else +#include "plugin.h" +#endif +#include "plugin-version.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "line-map.h" +#include "input.h" +#include "tree.h" + +#include "tree-inline.h" +#include "version.h" +#include "rtl.h" +#include "tm_p.h" +#include "flags.h" +#include "hard-reg-set.h" +#include "output.h" +#include "except.h" +#include "function.h" +#include "toplev.h" +#if BUILDING_GCC_VERSION >= 5000 +#include "expr.h" +#endif +#include "basic-block.h" +#include "intl.h" +#include "ggc.h" +#include "timevar.h" + +#if BUILDING_GCC_VERSION < 10000 +#include "params.h" +#endif + +#if BUILDING_GCC_VERSION <= 4009 +#include "pointer-set.h" +#else +#include "hash-map.h" +#endif + +#if BUILDING_GCC_VERSION >= 7000 +#include "memmodel.h" +#endif +#include "emit-rtl.h" +#include "debug.h" +#include "target.h" +#include "langhooks.h" +#include "cfgloop.h" +#include "cgraph.h" +#include "opts.h" + +#if BUILDING_GCC_VERSION == 4005 +#include +#endif + +#if BUILDING_GCC_VERSION >= 4007 +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#endif + +#if BUILDING_GCC_VERSION >= 4006 +/* + * The c-family headers were moved into a subdirectory in GCC version + * 4.7, but most plugin-building users of GCC 4.6 are using the Debian + * or Ubuntu package, which has an out-of-tree patch to move this to the + * same location as found in 4.7 and later: + * https://sources.debian.net/src/gcc-4.6/4.6.3-14/debian/patches/pr45078.diff/ + */ +#include "c-family/c-common.h" +#else +#include "c-common.h" +#endif + +#if BUILDING_GCC_VERSION <= 4008 +#include "tree-flow.h" +#else +#include "tree-cfgcleanup.h" +#include "tree-ssa-operands.h" +#include "tree-into-ssa.h" +#endif + +#if BUILDING_GCC_VERSION >= 4008 +#include "is-a.h" +#endif + +#include "diagnostic.h" +#include "tree-dump.h" +#include "tree-pass.h" +#if BUILDING_GCC_VERSION >= 4009 +#include "pass_manager.h" +#endif +#include "predict.h" +#include "ipa-utils.h" + +#if BUILDING_GCC_VERSION >= 8000 +#include "stringpool.h" +#endif + +#if BUILDING_GCC_VERSION >= 4009 +#include "attribs.h" +#include "varasm.h" +#include "stor-layout.h" +#include "internal-fn.h" +#include "gimple-expr.h" +#include "gimple-fold.h" +#include "context.h" +#include "tree-ssa-alias.h" +#include "tree-ssa.h" +#include "stringpool.h" +#if BUILDING_GCC_VERSION >= 7000 +#include "tree-vrp.h" +#endif +#include "tree-ssanames.h" +#include "print-tree.h" +#include "tree-eh.h" +#include "stmt.h" +#include "gimplify.h" +#endif + +#include "gimple.h" + +#if BUILDING_GCC_VERSION >= 4009 +#include "tree-ssa-operands.h" +#include "tree-phinodes.h" +#include "tree-cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include "ssa-iterators.h" +#endif + +#if BUILDING_GCC_VERSION >= 5000 +#include "builtins.h" +#endif + +/* missing from basic_block.h... */ +void debug_dominance_info(enum cdi_direction dir); +void debug_dominance_tree(enum cdi_direction dir, basic_block root); + +#if BUILDING_GCC_VERSION == 4006 +void debug_gimple_stmt(gimple); +void debug_gimple_seq(gimple_seq); +void print_gimple_seq(FILE *, gimple_seq, int, int); +void print_gimple_stmt(FILE *, gimple, int, int); +void print_gimple_expr(FILE *, gimple, int, int); +void dump_gimple_stmt(pretty_printer *, gimple, int, int); +#endif + +#define __unused __attribute__((__unused__)) +#define __visible __attribute__((visibility("default"))) + +#define DECL_NAME_POINTER(node) IDENTIFIER_POINTER(DECL_NAME(node)) +#define DECL_NAME_LENGTH(node) IDENTIFIER_LENGTH(DECL_NAME(node)) +#define TYPE_NAME_POINTER(node) IDENTIFIER_POINTER(TYPE_NAME(node)) +#define TYPE_NAME_LENGTH(node) IDENTIFIER_LENGTH(TYPE_NAME(node)) + +/* should come from c-tree.h if only it were installed for gcc 4.5... */ +#define C_TYPE_FIELDS_READONLY(TYPE) TREE_LANG_FLAG_1(TYPE) + +static inline tree build_const_char_string(int len, const char *str) +{ + tree cstr, elem, index, type; + + cstr = build_string(len, str); + elem = build_type_variant(char_type_node, 1, 0); + index = build_index_type(size_int(len - 1)); + type = build_array_type(elem, index); + TREE_TYPE(cstr) = type; + TREE_CONSTANT(cstr) = 1; + TREE_READONLY(cstr) = 1; + TREE_STATIC(cstr) = 1; + return cstr; +} + +#define PASS_INFO(NAME, REF, ID, POS) \ +struct register_pass_info NAME##_pass_info = { \ + .pass = make_##NAME##_pass(), \ + .reference_pass_name = REF, \ + .ref_pass_instance_number = ID, \ + .pos_op = POS, \ +} + +#if BUILDING_GCC_VERSION == 4005 +#define FOR_EACH_LOCAL_DECL(FUN, I, D) \ + for (tree vars = (FUN)->local_decls, (I) = 0; \ + vars && ((D) = TREE_VALUE(vars)); \ + vars = TREE_CHAIN(vars), (I)++) +#define DECL_CHAIN(NODE) (TREE_CHAIN(DECL_MINIMAL_CHECK(NODE))) +#define FOR_EACH_VEC_ELT(T, V, I, P) \ + for (I = 0; VEC_iterate(T, (V), (I), (P)); ++(I)) +#define TODO_rebuild_cgraph_edges 0 +#define SCOPE_FILE_SCOPE_P(EXP) (!(EXP)) + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +typedef struct varpool_node *varpool_node_ptr; + +static inline bool gimple_call_builtin_p(gimple stmt, enum built_in_function code) +{ + tree fndecl; + + if (!is_gimple_call(stmt)) + return false; + fndecl = gimple_call_fndecl(stmt); + if (!fndecl || DECL_BUILT_IN_CLASS(fndecl) != BUILT_IN_NORMAL) + return false; + return DECL_FUNCTION_CODE(fndecl) == code; +} + +static inline bool is_simple_builtin(tree decl) +{ + if (decl && DECL_BUILT_IN_CLASS(decl) != BUILT_IN_NORMAL) + return false; + + switch (DECL_FUNCTION_CODE(decl)) { + /* Builtins that expand to constants. */ + case BUILT_IN_CONSTANT_P: + case BUILT_IN_EXPECT: + case BUILT_IN_OBJECT_SIZE: + case BUILT_IN_UNREACHABLE: + /* Simple register moves or loads from stack. */ + case BUILT_IN_RETURN_ADDRESS: + case BUILT_IN_EXTRACT_RETURN_ADDR: + case BUILT_IN_FROB_RETURN_ADDR: + case BUILT_IN_RETURN: + case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: + case BUILT_IN_FRAME_ADDRESS: + case BUILT_IN_VA_END: + case BUILT_IN_STACK_SAVE: + case BUILT_IN_STACK_RESTORE: + /* Exception state returns or moves registers around. */ + case BUILT_IN_EH_FILTER: + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_COPY_VALUES: + return true; + + default: + return false; + } +} + +static inline void add_local_decl(struct function *fun, tree d) +{ + gcc_assert(TREE_CODE(d) == VAR_DECL); + fun->local_decls = tree_cons(NULL_TREE, d, fun->local_decls); +} +#endif + +#if BUILDING_GCC_VERSION <= 4006 +#define ANY_RETURN_P(rtx) (GET_CODE(rtx) == RETURN) +#define C_DECL_REGISTER(EXP) DECL_LANG_FLAG_4(EXP) +#define EDGE_PRESERVE 0ULL +#define HOST_WIDE_INT_PRINT_HEX_PURE "%" HOST_WIDE_INT_PRINT "x" +#define flag_fat_lto_objects true + +#define get_random_seed(noinit) ({ \ + unsigned HOST_WIDE_INT seed; \ + sscanf(get_random_seed(noinit), "%" HOST_WIDE_INT_PRINT "x", &seed); \ + seed * seed; }) + +#define int_const_binop(code, arg1, arg2) \ + int_const_binop((code), (arg1), (arg2), 0) + +static inline bool gimple_clobber_p(gimple s __unused) +{ + return false; +} + +static inline bool gimple_asm_clobbers_memory_p(const_gimple stmt) +{ + unsigned i; + + for (i = 0; i < gimple_asm_nclobbers(stmt); i++) { + tree op = gimple_asm_clobber_op(stmt, i); + + if (!strcmp(TREE_STRING_POINTER(TREE_VALUE(op)), "memory")) + return true; + } + + return false; +} + +static inline tree builtin_decl_implicit(enum built_in_function fncode) +{ + return implicit_built_in_decls[fncode]; +} + +static inline int ipa_reverse_postorder(struct cgraph_node **order) +{ + return cgraph_postorder(order); +} + +static inline struct cgraph_node *cgraph_create_node(tree decl) +{ + return cgraph_node(decl); +} + +static inline struct cgraph_node *cgraph_get_create_node(tree decl) +{ + struct cgraph_node *node = cgraph_get_node(decl); + + return node ? node : cgraph_node(decl); +} + +static inline bool cgraph_function_with_gimple_body_p(struct cgraph_node *node) +{ + return node->analyzed && !node->thunk.thunk_p && !node->alias; +} + +static inline struct cgraph_node *cgraph_first_function_with_gimple_body(void) +{ + struct cgraph_node *node; + + for (node = cgraph_nodes; node; node = node->next) + if (cgraph_function_with_gimple_body_p(node)) + return node; + return NULL; +} + +static inline struct cgraph_node *cgraph_next_function_with_gimple_body(struct cgraph_node *node) +{ + for (node = node->next; node; node = node->next) + if (cgraph_function_with_gimple_body_p(node)) + return node; + return NULL; +} + +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable) +{ + cgraph_node_ptr alias; + + if (callback(node, data)) + return true; + + for (alias = node->same_body; alias; alias = alias->next) { + if (include_overwritable || cgraph_function_body_availability(alias) > AVAIL_OVERWRITABLE) + if (cgraph_for_node_and_aliases(alias, callback, data, include_overwritable)) + return true; + } + + return false; +} + +#define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \ + for ((node) = cgraph_first_function_with_gimple_body(); (node); \ + (node) = cgraph_next_function_with_gimple_body(node)) + +static inline void varpool_add_new_variable(tree decl) +{ + varpool_finalize_decl(decl); +} +#endif + +#if BUILDING_GCC_VERSION <= 4007 +#define FOR_EACH_FUNCTION(node) \ + for (node = cgraph_nodes; node; node = node->next) +#define FOR_EACH_VARIABLE(node) \ + for (node = varpool_nodes; node; node = node->next) +#define PROP_loops 0 +#define NODE_SYMBOL(node) (node) +#define NODE_DECL(node) (node)->decl +#define INSN_LOCATION(INSN) RTL_LOCATION(INSN) +#define vNULL NULL + +static inline int bb_loop_depth(const_basic_block bb) +{ + return bb->loop_father ? loop_depth(bb->loop_father) : 0; +} + +static inline bool gimple_store_p(gimple gs) +{ + tree lhs = gimple_get_lhs(gs); + + return lhs && !is_gimple_reg(lhs); +} + +static inline void gimple_init_singleton(gimple g __unused) +{ +} +#endif + +#if BUILDING_GCC_VERSION == 4007 || BUILDING_GCC_VERSION == 4008 +static inline struct cgraph_node *cgraph_alias_target(struct cgraph_node *n) +{ + return cgraph_alias_aliased_node(n); +} +#endif + +#if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION <= 4009 +#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \ + cgraph_create_edge((caller), (callee), (call_stmt), (count), (freq)) +#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \ + cgraph_create_edge_including_clones((caller), (callee), (old_call_stmt), (call_stmt), (count), (freq), (reason)) +#endif + +#if BUILDING_GCC_VERSION <= 4008 +#define ENTRY_BLOCK_PTR_FOR_FN(FN) ENTRY_BLOCK_PTR_FOR_FUNCTION(FN) +#define EXIT_BLOCK_PTR_FOR_FN(FN) EXIT_BLOCK_PTR_FOR_FUNCTION(FN) +#define basic_block_info_for_fn(FN) ((FN)->cfg->x_basic_block_info) +#define n_basic_blocks_for_fn(FN) ((FN)->cfg->x_n_basic_blocks) +#define n_edges_for_fn(FN) ((FN)->cfg->x_n_edges) +#define last_basic_block_for_fn(FN) ((FN)->cfg->x_last_basic_block) +#define label_to_block_map_for_fn(FN) ((FN)->cfg->x_label_to_block_map) +#define profile_status_for_fn(FN) ((FN)->cfg->x_profile_status) +#define BASIC_BLOCK_FOR_FN(FN, N) BASIC_BLOCK_FOR_FUNCTION((FN), (N)) +#define NODE_IMPLICIT_ALIAS(node) (node)->same_body_alias +#define VAR_P(NODE) (TREE_CODE(NODE) == VAR_DECL) + +static inline bool tree_fits_shwi_p(const_tree t) +{ + if (t == NULL_TREE || TREE_CODE(t) != INTEGER_CST) + return false; + + if (TREE_INT_CST_HIGH(t) == 0 && (HOST_WIDE_INT)TREE_INT_CST_LOW(t) >= 0) + return true; + + if (TREE_INT_CST_HIGH(t) == -1 && (HOST_WIDE_INT)TREE_INT_CST_LOW(t) < 0 && !TYPE_UNSIGNED(TREE_TYPE(t))) + return true; + + return false; +} + +static inline bool tree_fits_uhwi_p(const_tree t) +{ + if (t == NULL_TREE || TREE_CODE(t) != INTEGER_CST) + return false; + + return TREE_INT_CST_HIGH(t) == 0; +} + +static inline HOST_WIDE_INT tree_to_shwi(const_tree t) +{ + gcc_assert(tree_fits_shwi_p(t)); + return TREE_INT_CST_LOW(t); +} + +static inline unsigned HOST_WIDE_INT tree_to_uhwi(const_tree t) +{ + gcc_assert(tree_fits_uhwi_p(t)); + return TREE_INT_CST_LOW(t); +} + +static inline const char *get_tree_code_name(enum tree_code code) +{ + gcc_assert(code < MAX_TREE_CODES); + return tree_code_name[code]; +} + +#define ipa_remove_stmt_references(cnode, stmt) + +typedef union gimple_statement_d gasm; +typedef union gimple_statement_d gassign; +typedef union gimple_statement_d gcall; +typedef union gimple_statement_d gcond; +typedef union gimple_statement_d gdebug; +typedef union gimple_statement_d ggoto; +typedef union gimple_statement_d gphi; +typedef union gimple_statement_d greturn; + +static inline gasm *as_a_gasm(gimple stmt) +{ + return stmt; +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return stmt; +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return stmt; +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return stmt; +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return stmt; +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return stmt; +} + +static inline gcond *as_a_gcond(gimple stmt) +{ + return stmt; +} + +static inline const gcond *as_a_const_gcond(const_gimple stmt) +{ + return stmt; +} + +static inline gdebug *as_a_gdebug(gimple stmt) +{ + return stmt; +} + +static inline const gdebug *as_a_const_gdebug(const_gimple stmt) +{ + return stmt; +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return stmt; +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return stmt; +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return stmt; +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return stmt; +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return stmt; +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return stmt; +} +#endif + +#if BUILDING_GCC_VERSION == 4008 +#define NODE_SYMBOL(node) (&(node)->symbol) +#define NODE_DECL(node) (node)->symbol.decl +#endif + +#if BUILDING_GCC_VERSION >= 4008 +#define add_referenced_var(var) +#define mark_sym_for_renaming(var) +#define varpool_mark_needed_node(node) +#define create_var_ann(var) +#define TODO_dump_func 0 +#define TODO_dump_cgraph 0 +#endif + +#if BUILDING_GCC_VERSION <= 4009 +#define TODO_verify_il 0 +#define AVAIL_INTERPOSABLE AVAIL_OVERWRITABLE + +#define section_name_prefix LTO_SECTION_NAME_PREFIX +#define fatal_error(loc, gmsgid, ...) fatal_error((gmsgid), __VA_ARGS__) + +rtx emit_move_insn(rtx x, rtx y); + +typedef struct rtx_def rtx_insn; + +static inline const char *get_decl_section_name(const_tree decl) +{ + if (DECL_SECTION_NAME(decl) == NULL_TREE) + return NULL; + + return TREE_STRING_POINTER(DECL_SECTION_NAME(decl)); +} + +static inline void set_decl_section_name(tree node, const char *value) +{ + if (value) + DECL_SECTION_NAME(node) = build_string(strlen(value) + 1, value); + else + DECL_SECTION_NAME(node) = NULL; +} +#endif + +#if BUILDING_GCC_VERSION == 4009 +typedef struct gimple_statement_asm gasm; +typedef struct gimple_statement_base gassign; +typedef struct gimple_statement_call gcall; +typedef struct gimple_statement_base gcond; +typedef struct gimple_statement_base gdebug; +typedef struct gimple_statement_base ggoto; +typedef struct gimple_statement_phi gphi; +typedef struct gimple_statement_base greturn; + +static inline gasm *as_a_gasm(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return stmt; +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return stmt; +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gcond *as_a_gcond(gimple stmt) +{ + return stmt; +} + +static inline const gcond *as_a_const_gcond(const_gimple stmt) +{ + return stmt; +} + +static inline gdebug *as_a_gdebug(gimple stmt) +{ + return stmt; +} + +static inline const gdebug *as_a_const_gdebug(const_gimple stmt) +{ + return stmt; +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return stmt; +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return stmt; +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return stmt; +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return stmt; +} +#endif + +#if BUILDING_GCC_VERSION >= 4009 +#define TODO_ggc_collect 0 +#define NODE_SYMBOL(node) (node) +#define NODE_DECL(node) (node)->decl +#define cgraph_node_name(node) (node)->name() +#define NODE_IMPLICIT_ALIAS(node) (node)->cpp_implicit_alias + +static inline opt_pass *get_pass_for_id(int id) +{ + return g->get_passes()->get_pass_for_id(id); +} +#endif + +#if BUILDING_GCC_VERSION >= 5000 && BUILDING_GCC_VERSION < 6000 +/* gimple related */ +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_ASSIGN; +} +#endif + +#if BUILDING_GCC_VERSION >= 5000 +#define TODO_verify_ssa TODO_verify_il +#define TODO_verify_flow TODO_verify_il +#define TODO_verify_stmts TODO_verify_il +#define TODO_verify_rtl_sharing TODO_verify_il + +#define INSN_DELETED_P(insn) (insn)->deleted() + +static inline const char *get_decl_section_name(const_tree decl) +{ + return DECL_SECTION_NAME(decl); +} + +/* symtab/cgraph related */ +#define debug_cgraph_node(node) (node)->debug() +#define cgraph_get_node(decl) cgraph_node::get(decl) +#define cgraph_get_create_node(decl) cgraph_node::get_create(decl) +#define cgraph_create_node(decl) cgraph_node::create(decl) +#define cgraph_n_nodes symtab->cgraph_count +#define cgraph_max_uid symtab->cgraph_max_uid +#define varpool_get_node(decl) varpool_node::get(decl) +#define dump_varpool_node(file, node) (node)->dump(file) + +#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \ + (caller)->create_edge((callee), (call_stmt), (count), (freq)) +#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \ + (caller)->create_edge_including_clones((callee), (old_call_stmt), (call_stmt), (count), (freq), (reason)) + +typedef struct cgraph_node *cgraph_node_ptr; +typedef struct cgraph_edge *cgraph_edge_p; +typedef struct varpool_node *varpool_node_ptr; + +static inline void change_decl_assembler_name(tree decl, tree name) +{ + symtab->change_decl_assembler_name(decl, name); +} + +static inline void varpool_finalize_decl(tree decl) +{ + varpool_node::finalize_decl(decl); +} + +static inline void varpool_add_new_variable(tree decl) +{ + varpool_node::add(decl); +} + +static inline unsigned int rebuild_cgraph_edges(void) +{ + return cgraph_edge::rebuild_edges(); +} + +static inline cgraph_node_ptr cgraph_function_node(cgraph_node_ptr node, enum availability *availability) +{ + return node->function_symbol(availability); +} + +static inline cgraph_node_ptr cgraph_function_or_thunk_node(cgraph_node_ptr node, enum availability *availability = NULL) +{ + return node->ultimate_alias_target(availability); +} + +static inline bool cgraph_only_called_directly_p(cgraph_node_ptr node) +{ + return node->only_called_directly_p(); +} + +static inline enum availability cgraph_function_body_availability(cgraph_node_ptr node) +{ + return node->get_availability(); +} + +static inline cgraph_node_ptr cgraph_alias_target(cgraph_node_ptr node) +{ + return node->get_alias_target(); +} + +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable) +{ + return node->call_for_symbol_thunks_and_aliases(callback, data, include_overwritable); +} + +static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data) +{ + return symtab->add_cgraph_insertion_hook(hook, data); +} + +static inline void cgraph_remove_function_insertion_hook(struct cgraph_node_hook_list *entry) +{ + symtab->remove_cgraph_insertion_hook(entry); +} + +static inline struct cgraph_node_hook_list *cgraph_add_node_removal_hook(cgraph_node_hook hook, void *data) +{ + return symtab->add_cgraph_removal_hook(hook, data); +} + +static inline void cgraph_remove_node_removal_hook(struct cgraph_node_hook_list *entry) +{ + symtab->remove_cgraph_removal_hook(entry); +} + +static inline struct cgraph_2node_hook_list *cgraph_add_node_duplication_hook(cgraph_2node_hook hook, void *data) +{ + return symtab->add_cgraph_duplication_hook(hook, data); +} + +static inline void cgraph_remove_node_duplication_hook(struct cgraph_2node_hook_list *entry) +{ + symtab->remove_cgraph_duplication_hook(entry); +} + +static inline void cgraph_call_node_duplication_hooks(cgraph_node_ptr node, cgraph_node_ptr node2) +{ + symtab->call_cgraph_duplication_hooks(node, node2); +} + +static inline void cgraph_call_edge_duplication_hooks(cgraph_edge *cs1, cgraph_edge *cs2) +{ + symtab->call_edge_duplication_hooks(cs1, cs2); +} + +#if BUILDING_GCC_VERSION >= 6000 +typedef gimple *gimple_ptr; +typedef const gimple *const_gimple_ptr; +#define gimple gimple_ptr +#define const_gimple const_gimple_ptr +#undef CONST_CAST_GIMPLE +#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X)) +#endif + +/* gimple related */ +static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL) +{ + return gimple_build_assign(lhs, subcode, op1, op2 PASS_MEM_STAT); +} + +#if BUILDING_GCC_VERSION < 10000 +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_GOTO; +} + +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_RETURN; +} +#endif + +static inline gasm *as_a_gasm(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return as_a(stmt); +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return as_a(stmt); +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return as_a(stmt); +} + +/* IPA/LTO related */ +#define ipa_ref_list_referring_iterate(L, I, P) \ + (L)->referring.iterate((I), &(P)) +#define ipa_ref_list_reference_iterate(L, I, P) \ + (L)->reference.iterate((I), &(P)) + +static inline cgraph_node_ptr ipa_ref_referring_node(struct ipa_ref *ref) +{ + return dyn_cast(ref->referring); +} + +static inline void ipa_remove_stmt_references(symtab_node *referring_node, gimple stmt) +{ + referring_node->remove_stmt_references(stmt); +} +#endif + +#if BUILDING_GCC_VERSION < 6000 +#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ + get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, pvolatilep, keep_aligning) +#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET(VOIDmode, (ARG0), (ARG1)) +#endif + +#if BUILDING_GCC_VERSION >= 6000 +#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET((ARG0), (ARG1)) +#endif + +#ifdef __cplusplus +static inline void debug_tree(const_tree t) +{ + debug_tree(CONST_CAST_TREE(t)); +} + +static inline void debug_gimple_stmt(const_gimple s) +{ + debug_gimple_stmt(CONST_CAST_GIMPLE(s)); +} +#else +#define debug_tree(t) debug_tree(CONST_CAST_TREE(t)) +#define debug_gimple_stmt(s) debug_gimple_stmt(CONST_CAST_GIMPLE(s)) +#endif + +#if BUILDING_GCC_VERSION >= 7000 +#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ + get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep) +#endif + +#if BUILDING_GCC_VERSION < 7000 +#define SET_DECL_ALIGN(decl, align) DECL_ALIGN(decl) = (align) +#define SET_DECL_MODE(decl, mode) DECL_MODE(decl) = (mode) +#endif + +#endif diff --git a/kpatch-build/gcc-plugins/gcc-generate-rtl-pass.h b/kpatch-build/gcc-plugins/gcc-generate-rtl-pass.h new file mode 100644 index 0000000..d69cd80 --- /dev/null +++ b/kpatch-build/gcc-plugins/gcc-generate-rtl-pass.h @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Generator for RTL pass related boilerplate code/data + * + * Supports gcc 4.5-6 + * + * Usage: + * + * 1. before inclusion define PASS_NAME + * 2. before inclusion define NO_* for unimplemented callbacks + * NO_GATE + * NO_EXECUTE + * 3. before inclusion define PROPERTIES_* and TODO_FLAGS_* to override + * the default 0 values + * 4. for convenience, all the above will be undefined after inclusion! + * 5. the only exported name is make_PASS_NAME_pass() to register with gcc + */ + +#ifndef PASS_NAME +#error at least PASS_NAME must be defined +#else +#define __GCC_PLUGIN_STRINGIFY(n) #n +#define _GCC_PLUGIN_STRINGIFY(n) __GCC_PLUGIN_STRINGIFY(n) +#define _GCC_PLUGIN_CONCAT2(x, y) x ## y +#define _GCC_PLUGIN_CONCAT3(x, y, z) x ## y ## z + +#define __PASS_NAME_PASS_DATA(n) _GCC_PLUGIN_CONCAT2(n, _pass_data) +#define _PASS_NAME_PASS_DATA __PASS_NAME_PASS_DATA(PASS_NAME) + +#define __PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT2(n, _pass) +#define _PASS_NAME_PASS __PASS_NAME_PASS(PASS_NAME) + +#define _PASS_NAME_NAME _GCC_PLUGIN_STRINGIFY(PASS_NAME) + +#define __MAKE_PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT3(make_, n, _pass) +#define _MAKE_PASS_NAME_PASS __MAKE_PASS_NAME_PASS(PASS_NAME) + +#ifdef NO_GATE +#define _GATE NULL +#define _HAS_GATE false +#else +#define __GATE(n) _GCC_PLUGIN_CONCAT2(n, _gate) +#define _GATE __GATE(PASS_NAME) +#define _HAS_GATE true +#endif + +#ifdef NO_EXECUTE +#define _EXECUTE NULL +#define _HAS_EXECUTE false +#else +#define __EXECUTE(n) _GCC_PLUGIN_CONCAT2(n, _execute) +#define _EXECUTE __EXECUTE(PASS_NAME) +#define _HAS_EXECUTE true +#endif + +#ifndef PROPERTIES_REQUIRED +#define PROPERTIES_REQUIRED 0 +#endif + +#ifndef PROPERTIES_PROVIDED +#define PROPERTIES_PROVIDED 0 +#endif + +#ifndef PROPERTIES_DESTROYED +#define PROPERTIES_DESTROYED 0 +#endif + +#ifndef TODO_FLAGS_START +#define TODO_FLAGS_START 0 +#endif + +#ifndef TODO_FLAGS_FINISH +#define TODO_FLAGS_FINISH 0 +#endif + +#if BUILDING_GCC_VERSION >= 4009 +namespace { +static const pass_data _PASS_NAME_PASS_DATA = { +#else +static struct rtl_opt_pass _PASS_NAME_PASS = { + .pass = { +#endif + .type = RTL_PASS, + .name = _PASS_NAME_NAME, +#if BUILDING_GCC_VERSION >= 4008 + .optinfo_flags = OPTGROUP_NONE, +#endif +#if BUILDING_GCC_VERSION >= 5000 +#elif BUILDING_GCC_VERSION == 4009 + .has_gate = _HAS_GATE, + .has_execute = _HAS_EXECUTE, +#else + .gate = _GATE, + .execute = _EXECUTE, + .sub = NULL, + .next = NULL, + .static_pass_number = 0, +#endif + .tv_id = TV_NONE, + .properties_required = PROPERTIES_REQUIRED, + .properties_provided = PROPERTIES_PROVIDED, + .properties_destroyed = PROPERTIES_DESTROYED, + .todo_flags_start = TODO_FLAGS_START, + .todo_flags_finish = TODO_FLAGS_FINISH, +#if BUILDING_GCC_VERSION < 4009 + } +#endif +}; + +#if BUILDING_GCC_VERSION >= 4009 +class _PASS_NAME_PASS : public rtl_opt_pass { +public: + _PASS_NAME_PASS() : rtl_opt_pass(_PASS_NAME_PASS_DATA, g) {} + +#ifndef NO_GATE +#if BUILDING_GCC_VERSION >= 5000 + virtual bool gate(function *) { return _GATE(); } +#else + virtual bool gate(void) { return _GATE(); } +#endif +#endif + + virtual opt_pass *clone() { return new _PASS_NAME_PASS(); } + +#ifndef NO_EXECUTE +#if BUILDING_GCC_VERSION >= 5000 + virtual unsigned int execute(function *) { return _EXECUTE(); } +#else + virtual unsigned int execute(void) { return _EXECUTE(); } +#endif +#endif +}; +} + +opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return new _PASS_NAME_PASS(); +} +#else +struct opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return &_PASS_NAME_PASS.pass; +} +#endif + +/* clean up user provided defines */ +#undef PASS_NAME +#undef NO_GATE +#undef NO_EXECUTE + +#undef PROPERTIES_DESTROYED +#undef PROPERTIES_PROVIDED +#undef PROPERTIES_REQUIRED +#undef TODO_FLAGS_FINISH +#undef TODO_FLAGS_START + +/* clean up generated defines */ +#undef _EXECUTE +#undef __EXECUTE +#undef _GATE +#undef __GATE +#undef _GCC_PLUGIN_CONCAT2 +#undef _GCC_PLUGIN_CONCAT3 +#undef _GCC_PLUGIN_STRINGIFY +#undef __GCC_PLUGIN_STRINGIFY +#undef _HAS_EXECUTE +#undef _HAS_GATE +#undef _MAKE_PASS_NAME_PASS +#undef __MAKE_PASS_NAME_PASS +#undef _PASS_NAME_NAME +#undef _PASS_NAME_PASS +#undef __PASS_NAME_PASS +#undef _PASS_NAME_PASS_DATA +#undef __PASS_NAME_PASS_DATA + +#endif /* PASS_NAME */ diff --git a/kpatch-build/gcc-plugins/ppc64le-plugin.c b/kpatch-build/gcc-plugins/ppc64le-plugin.c new file mode 100644 index 0000000..ba4a01e --- /dev/null +++ b/kpatch-build/gcc-plugins/ppc64le-plugin.c @@ -0,0 +1,97 @@ +#include +#include "gcc-common.h" + +#define PLUGIN_NAME "ppc64le-plugin" + +#if BUILDING_GCC_VERSION < 10000 +#define CALL_LOCAL "*call_local_aixdi" +#define CALL_NONLOCAL "*call_nonlocal_aixdi" +#define CALL_VALUE_LOCAL "*call_value_local_aixdi" +#define CALL_VALUE_NONLOCAL "*call_value_nonlocal_aixdi" +#else +#define CALL_LOCAL "*call_localdi" +#define CALL_NONLOCAL "*call_nonlocal_aixdi" +#define CALL_VALUE_LOCAL "*call_value_localdi" +#define CALL_VALUE_NONLOCAL "*call_value_nonlocal_aixdi" +#endif + +int plugin_is_GPL_compatible; + +struct plugin_info plugin_info = { + .version = "1", + .help = PLUGIN_NAME ": insert nops after local calls\n", +}; + +static unsigned int ppc64le_plugin_execute(void) +{ + rtx_insn *insn; + int code; + const char *name; + static int nonlocal_code = -1, local_code = -1, + value_nonlocal_code = -1, value_local_code = -1; + static bool initialized = false; + + if (initialized) + goto found; + + /* Find the rs6000.md code numbers for local and non-local calls */ + initialized = true; + for (code = 0; code < 1000; code++) { + name = get_insn_name(code); + if (!name) + continue; + + if (!strcmp(name , CALL_LOCAL)) + local_code = code; + else if (!strcmp(name , CALL_NONLOCAL)) + nonlocal_code = code; + else if (!strcmp(name, CALL_VALUE_LOCAL)) + value_local_code = code; + else if (!strcmp(name, CALL_VALUE_NONLOCAL)) + value_nonlocal_code = code; + + if (nonlocal_code != -1 && local_code != -1 && + value_nonlocal_code != -1 && value_local_code != -1) + goto found; + } + +found: + if (nonlocal_code == -1 || local_code == -1 || + value_nonlocal_code == -1 || value_local_code == -1) { + error("%s: cannot find call instruction codes", PLUGIN_NAME); + } + + /* Convert local calls to non-local */ + for (insn = get_insns(); insn; insn = NEXT_INSN(insn)) { + if (GET_CODE(insn) == CALL_INSN) { + if (INSN_CODE(insn) == local_code) + INSN_CODE(insn) = nonlocal_code; + else if (INSN_CODE(insn) == value_local_code) + INSN_CODE(insn) = value_nonlocal_code; + } + } + + return 0; +} + +#define PASS_NAME ppc64le_plugin +#define NO_GATE +#include "gcc-generate-rtl-pass.h" + +int plugin_init(struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + const char * const plugin_name = plugin_info->base_name; + + PASS_INFO(ppc64le_plugin, "vregs", 1, PASS_POS_INSERT_AFTER); + + if (!plugin_default_version_check(version, &gcc_version)) + error(1, 0, PLUGIN_NAME ": incompatible gcc/plugin versions"); + + register_callback(plugin_name, PLUGIN_INFO, NULL, &plugin_info); + register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, + &ppc64le_plugin_pass_info); + + return 0; +} + diff --git a/kpatch-build/insn/asm/inat.h b/kpatch-build/insn/asm/inat.h new file mode 100644 index 0000000..74a2e31 --- /dev/null +++ b/kpatch-build/insn/asm/inat.h @@ -0,0 +1,221 @@ +#ifndef _ASM_X86_INAT_H +#define _ASM_X86_INAT_H +/* + * x86 instruction attributes + * + * Written by Masami Hiramatsu + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#include + +/* + * Internal bits. Don't use bitmasks directly, because these bits are + * unstable. You should use checking functions. + */ + +#define INAT_OPCODE_TABLE_SIZE 256 +#define INAT_GROUP_TABLE_SIZE 8 + +/* Legacy last prefixes */ +#define INAT_PFX_OPNDSZ 1 /* 0x66 */ /* LPFX1 */ +#define INAT_PFX_REPE 2 /* 0xF3 */ /* LPFX2 */ +#define INAT_PFX_REPNE 3 /* 0xF2 */ /* LPFX3 */ +/* Other Legacy prefixes */ +#define INAT_PFX_LOCK 4 /* 0xF0 */ +#define INAT_PFX_CS 5 /* 0x2E */ +#define INAT_PFX_DS 6 /* 0x3E */ +#define INAT_PFX_ES 7 /* 0x26 */ +#define INAT_PFX_FS 8 /* 0x64 */ +#define INAT_PFX_GS 9 /* 0x65 */ +#define INAT_PFX_SS 10 /* 0x36 */ +#define INAT_PFX_ADDRSZ 11 /* 0x67 */ +/* x86-64 REX prefix */ +#define INAT_PFX_REX 12 /* 0x4X */ +/* AVX VEX prefixes */ +#define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */ +#define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */ + +#define INAT_LSTPFX_MAX 3 +#define INAT_LGCPFX_MAX 11 + +/* Immediate size */ +#define INAT_IMM_BYTE 1 +#define INAT_IMM_WORD 2 +#define INAT_IMM_DWORD 3 +#define INAT_IMM_QWORD 4 +#define INAT_IMM_PTR 5 +#define INAT_IMM_VWORD32 6 +#define INAT_IMM_VWORD 7 + +/* Legacy prefix */ +#define INAT_PFX_OFFS 0 +#define INAT_PFX_BITS 4 +#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1) +#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS) +/* Escape opcodes */ +#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS) +#define INAT_ESC_BITS 2 +#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1) +#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS) +/* Group opcodes (1-16) */ +#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS) +#define INAT_GRP_BITS 5 +#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1) +#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS) +/* Immediates */ +#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS) +#define INAT_IMM_BITS 3 +#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS) +/* Flags */ +#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS) +#define INAT_MODRM (1 << (INAT_FLAG_OFFS)) +#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1)) +#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2)) +#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3)) +#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4)) +#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5)) +#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6)) +/* Attribute making macros for attribute tables */ +#define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) +#define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) +#define INAT_MAKE_GROUP(grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM) +#define INAT_MAKE_IMM(imm) (imm << INAT_IMM_OFFS) + +/* Attribute search APIs */ +extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode); +extern int inat_get_last_prefix_id(insn_byte_t last_pfx); +extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, + int lpfx_id, + insn_attr_t esc_attr); +extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm, + int lpfx_id, + insn_attr_t esc_attr); +extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, + insn_byte_t vex_m, + insn_byte_t vex_pp); + +/* Attribute checking functions */ +static inline int inat_is_legacy_prefix(insn_attr_t attr) +{ + attr &= INAT_PFX_MASK; + return attr && attr <= INAT_LGCPFX_MAX; +} + +static inline int inat_is_address_size_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ; +} + +static inline int inat_is_operand_size_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ; +} + +static inline int inat_is_rex_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_REX; +} + +static inline int inat_last_prefix_id(insn_attr_t attr) +{ + if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX) + return 0; + else + return attr & INAT_PFX_MASK; +} + +static inline int inat_is_vex_prefix(insn_attr_t attr) +{ + attr &= INAT_PFX_MASK; + return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3; +} + +static inline int inat_is_vex3_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3; +} + +static inline int inat_is_escape(insn_attr_t attr) +{ + return attr & INAT_ESC_MASK; +} + +static inline int inat_escape_id(insn_attr_t attr) +{ + return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS; +} + +static inline int inat_is_group(insn_attr_t attr) +{ + return attr & INAT_GRP_MASK; +} + +static inline int inat_group_id(insn_attr_t attr) +{ + return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS; +} + +static inline int inat_group_common_attribute(insn_attr_t attr) +{ + return attr & ~INAT_GRP_MASK; +} + +static inline int inat_has_immediate(insn_attr_t attr) +{ + return attr & INAT_IMM_MASK; +} + +static inline int inat_immediate_size(insn_attr_t attr) +{ + return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS; +} + +static inline int inat_has_modrm(insn_attr_t attr) +{ + return attr & INAT_MODRM; +} + +static inline int inat_is_force64(insn_attr_t attr) +{ + return attr & INAT_FORCE64; +} + +static inline int inat_has_second_immediate(insn_attr_t attr) +{ + return attr & INAT_SCNDIMM; +} + +static inline int inat_has_moffset(insn_attr_t attr) +{ + return attr & INAT_MOFFSET; +} + +static inline int inat_has_variant(insn_attr_t attr) +{ + return attr & INAT_VARIANT; +} + +static inline int inat_accept_vex(insn_attr_t attr) +{ + return attr & INAT_VEXOK; +} + +static inline int inat_must_vex(insn_attr_t attr) +{ + return attr & INAT_VEXONLY; +} +#endif diff --git a/kpatch-build/insn/asm/inat_types.h b/kpatch-build/insn/asm/inat_types.h new file mode 100644 index 0000000..cb3c20c --- /dev/null +++ b/kpatch-build/insn/asm/inat_types.h @@ -0,0 +1,29 @@ +#ifndef _ASM_X86_INAT_TYPES_H +#define _ASM_X86_INAT_TYPES_H +/* + * x86 instruction attributes + * + * Written by Masami Hiramatsu + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* Instruction attributes */ +typedef unsigned int insn_attr_t; +typedef unsigned char insn_byte_t; +typedef signed int insn_value_t; + +#endif diff --git a/kpatch-build/insn/asm/insn.h b/kpatch-build/insn/asm/insn.h new file mode 100644 index 0000000..48eb30a --- /dev/null +++ b/kpatch-build/insn/asm/insn.h @@ -0,0 +1,199 @@ +#ifndef _ASM_X86_INSN_H +#define _ASM_X86_INSN_H +/* + * x86 instruction analysis + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2009 + */ + +/* insn_attr_t is defined in inat.h */ +#include + +struct insn_field { + union { + insn_value_t value; + insn_byte_t bytes[4]; + }; + /* !0 if we've run insn_get_xxx() for this field */ + unsigned char got; + unsigned char nbytes; +}; + +struct insn { + struct insn_field prefixes; /* + * Prefixes + * prefixes.bytes[3]: last prefix + */ + struct insn_field rex_prefix; /* REX prefix */ + struct insn_field vex_prefix; /* VEX prefix */ + struct insn_field opcode; /* + * opcode.bytes[0]: opcode1 + * opcode.bytes[1]: opcode2 + * opcode.bytes[2]: opcode3 + */ + struct insn_field modrm; + struct insn_field sib; + struct insn_field displacement; + union { + struct insn_field immediate; + struct insn_field moffset1; /* for 64bit MOV */ + struct insn_field immediate1; /* for 64bit imm or off16/32 */ + }; + union { + struct insn_field moffset2; /* for 64bit MOV */ + struct insn_field immediate2; /* for 64bit imm or seg16 */ + }; + + insn_attr_t attr; + unsigned char opnd_bytes; + unsigned char addr_bytes; + unsigned char length; + unsigned char x86_64; + + const insn_byte_t *kaddr; /* kernel address of insn to analyze */ + const insn_byte_t *next_byte; +}; + +#define MAX_INSN_SIZE 16 + +#define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6) +#define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3) +#define X86_MODRM_RM(modrm) ((modrm) & 0x07) + +#define X86_SIB_SCALE(sib) (((sib) & 0xc0) >> 6) +#define X86_SIB_INDEX(sib) (((sib) & 0x38) >> 3) +#define X86_SIB_BASE(sib) ((sib) & 0x07) + +#define X86_REX_W(rex) ((rex) & 8) +#define X86_REX_R(rex) ((rex) & 4) +#define X86_REX_X(rex) ((rex) & 2) +#define X86_REX_B(rex) ((rex) & 1) + +/* VEX bit flags */ +#define X86_VEX_W(vex) ((vex) & 0x80) /* VEX3 Byte2 */ +#define X86_VEX_R(vex) ((vex) & 0x80) /* VEX2/3 Byte1 */ +#define X86_VEX_X(vex) ((vex) & 0x40) /* VEX3 Byte1 */ +#define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */ +#define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */ +/* VEX bit fields */ +#define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */ +#define X86_VEX2_M 1 /* VEX2.M always 1 */ +#define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */ +#define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */ +#define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */ + +extern void insn_init(struct insn *insn, const void *kaddr, int x86_64); +extern void insn_get_prefixes(struct insn *insn); +extern void insn_get_opcode(struct insn *insn); +extern void insn_get_modrm(struct insn *insn); +extern void insn_get_sib(struct insn *insn); +extern void insn_get_displacement(struct insn *insn); +extern void insn_get_immediate(struct insn *insn); +extern void insn_get_length(struct insn *insn); + +/* Attribute will be determined after getting ModRM (for opcode groups) */ +static inline void insn_get_attribute(struct insn *insn) +{ + insn_get_modrm(insn); +} + +/* Instruction uses RIP-relative addressing */ +extern int insn_rip_relative(struct insn *insn); + +/* Init insn for kernel text */ +static inline void kernel_insn_init(struct insn *insn, const void *kaddr) +{ +#ifdef CONFIG_X86_64 + insn_init(insn, kaddr, 1); +#else /* CONFIG_X86_32 */ + insn_init(insn, kaddr, 0); +#endif +} + +static inline int insn_is_avx(struct insn *insn) +{ + if (!insn->prefixes.got) + insn_get_prefixes(insn); + return (insn->vex_prefix.value != 0); +} + +/* Ensure this instruction is decoded completely */ +static inline int insn_complete(struct insn *insn) +{ + return insn->opcode.got && insn->modrm.got && insn->sib.got && + insn->displacement.got && insn->immediate.got; +} + +static inline insn_byte_t insn_vex_m_bits(struct insn *insn) +{ + if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ + return X86_VEX2_M; + else + return X86_VEX3_M(insn->vex_prefix.bytes[1]); +} + +static inline insn_byte_t insn_vex_p_bits(struct insn *insn) +{ + if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ + return X86_VEX_P(insn->vex_prefix.bytes[1]); + else + return X86_VEX_P(insn->vex_prefix.bytes[2]); +} + +/* Get the last prefix id from last prefix or VEX prefix */ +static inline int insn_last_prefix_id(struct insn *insn) +{ + if (insn_is_avx(insn)) + return insn_vex_p_bits(insn); /* VEX_p is a SIMD prefix id */ + + if (insn->prefixes.bytes[3]) + return inat_get_last_prefix_id(insn->prefixes.bytes[3]); + + return 0; +} + +/* Offset of each field from kaddr */ +static inline int insn_offset_rex_prefix(struct insn *insn) +{ + return insn->prefixes.nbytes; +} +static inline int insn_offset_vex_prefix(struct insn *insn) +{ + return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes; +} +static inline int insn_offset_opcode(struct insn *insn) +{ + return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes; +} +static inline int insn_offset_modrm(struct insn *insn) +{ + return insn_offset_opcode(insn) + insn->opcode.nbytes; +} +static inline int insn_offset_sib(struct insn *insn) +{ + return insn_offset_modrm(insn) + insn->modrm.nbytes; +} +static inline int insn_offset_displacement(struct insn *insn) +{ + return insn_offset_sib(insn) + insn->sib.nbytes; +} +static inline int insn_offset_immediate(struct insn *insn) +{ + return insn_offset_displacement(insn) + insn->displacement.nbytes; +} + +#endif /* _ASM_X86_INSN_H */ diff --git a/kpatch-build/insn/inat-tables.c b/kpatch-build/insn/inat-tables.c new file mode 100644 index 0000000..10de26c --- /dev/null +++ b/kpatch-build/insn/inat-tables.c @@ -0,0 +1,1146 @@ +/* x86 opcode map generated from x86-opcode-map.txt */ +/* Do not change this code. */ + +/* Table: one byte opcode */ +const insn_attr_t inat_primary_table[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MODRM, + [0x01] = INAT_MODRM, + [0x02] = INAT_MODRM, + [0x03] = INAT_MODRM, + [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x05] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x08] = INAT_MODRM, + [0x09] = INAT_MODRM, + [0x0a] = INAT_MODRM, + [0x0b] = INAT_MODRM, + [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x0d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x0f] = INAT_MAKE_ESCAPE(1), + [0x10] = INAT_MODRM, + [0x11] = INAT_MODRM, + [0x12] = INAT_MODRM, + [0x13] = INAT_MODRM, + [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x15] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x18] = INAT_MODRM, + [0x19] = INAT_MODRM, + [0x1a] = INAT_MODRM, + [0x1b] = INAT_MODRM, + [0x1c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x1d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x20] = INAT_MODRM, + [0x21] = INAT_MODRM, + [0x22] = INAT_MODRM, + [0x23] = INAT_MODRM, + [0x24] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x25] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x26] = INAT_MAKE_PREFIX(INAT_PFX_ES), + [0x28] = INAT_MODRM, + [0x29] = INAT_MODRM, + [0x2a] = INAT_MODRM, + [0x2b] = INAT_MODRM, + [0x2c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x2d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x2e] = INAT_MAKE_PREFIX(INAT_PFX_CS), + [0x30] = INAT_MODRM, + [0x31] = INAT_MODRM, + [0x32] = INAT_MODRM, + [0x33] = INAT_MODRM, + [0x34] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x35] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x36] = INAT_MAKE_PREFIX(INAT_PFX_SS), + [0x38] = INAT_MODRM, + [0x39] = INAT_MODRM, + [0x3a] = INAT_MODRM, + [0x3b] = INAT_MODRM, + [0x3c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x3d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x3e] = INAT_MAKE_PREFIX(INAT_PFX_DS), + [0x40] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x41] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x42] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x43] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x44] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x45] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x46] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x47] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x48] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x49] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4a] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4b] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4c] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4d] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4e] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4f] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x50] = INAT_FORCE64, + [0x51] = INAT_FORCE64, + [0x52] = INAT_FORCE64, + [0x53] = INAT_FORCE64, + [0x54] = INAT_FORCE64, + [0x55] = INAT_FORCE64, + [0x56] = INAT_FORCE64, + [0x57] = INAT_FORCE64, + [0x58] = INAT_FORCE64, + [0x59] = INAT_FORCE64, + [0x5a] = INAT_FORCE64, + [0x5b] = INAT_FORCE64, + [0x5c] = INAT_FORCE64, + [0x5d] = INAT_FORCE64, + [0x5e] = INAT_FORCE64, + [0x5f] = INAT_FORCE64, + [0x62] = INAT_MODRM, + [0x63] = INAT_MODRM | INAT_MODRM, + [0x64] = INAT_MAKE_PREFIX(INAT_PFX_FS), + [0x65] = INAT_MAKE_PREFIX(INAT_PFX_GS), + [0x66] = INAT_MAKE_PREFIX(INAT_PFX_OPNDSZ), + [0x67] = INAT_MAKE_PREFIX(INAT_PFX_ADDRSZ), + [0x68] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x69] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x6a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0x6b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x71] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x72] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x73] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x74] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x75] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x76] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x77] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x78] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x79] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7a] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7b] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7d] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7e] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7f] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x80] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x82] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x83] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x84] = INAT_MODRM, + [0x85] = INAT_MODRM, + [0x86] = INAT_MODRM, + [0x87] = INAT_MODRM, + [0x88] = INAT_MODRM, + [0x89] = INAT_MODRM, + [0x8a] = INAT_MODRM, + [0x8b] = INAT_MODRM, + [0x8c] = INAT_MODRM, + [0x8d] = INAT_MODRM, + [0x8e] = INAT_MODRM, + [0x8f] = INAT_MAKE_GROUP(2) | INAT_MODRM | INAT_FORCE64, + [0x9a] = INAT_MAKE_IMM(INAT_IMM_PTR), + [0x9c] = INAT_FORCE64, + [0x9d] = INAT_FORCE64, + [0xa0] = INAT_MOFFSET, + [0xa1] = INAT_MOFFSET, + [0xa2] = INAT_MOFFSET, + [0xa3] = INAT_MOFFSET, + [0xa8] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xa9] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0xb0] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb1] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb2] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb3] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb6] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb7] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb8] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xb9] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xba] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbb] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbc] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbd] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbe] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbf] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xc0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3), + [0xc1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3), + [0xc2] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_FORCE64, + [0xc4] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX3), + [0xc5] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX2), + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(4), + [0xc7] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(5), + [0xc8] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_SCNDIMM, + [0xc9] = INAT_FORCE64, + [0xca] = INAT_MAKE_IMM(INAT_IMM_WORD), + [0xcd] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd0] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd1] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd2] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd3] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd8] = INAT_MODRM, + [0xd9] = INAT_MODRM, + [0xda] = INAT_MODRM, + [0xdb] = INAT_MODRM, + [0xdc] = INAT_MODRM, + [0xdd] = INAT_MODRM, + [0xde] = INAT_MODRM, + [0xdf] = INAT_MODRM, + [0xe0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe6] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe7] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe8] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0xe9] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0xea] = INAT_MAKE_IMM(INAT_IMM_PTR), + [0xeb] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xf0] = INAT_MAKE_PREFIX(INAT_PFX_LOCK), + [0xf2] = INAT_MAKE_PREFIX(INAT_PFX_REPNE) | INAT_MAKE_PREFIX(INAT_PFX_REPNE), + [0xf3] = INAT_MAKE_PREFIX(INAT_PFX_REPE) | INAT_MAKE_PREFIX(INAT_PFX_REPE), + [0xf6] = INAT_MODRM | INAT_MAKE_GROUP(6), + [0xf7] = INAT_MODRM | INAT_MAKE_GROUP(7), + [0xfe] = INAT_MAKE_GROUP(8), + [0xff] = INAT_MAKE_GROUP(9), +}; + +/* Table: 2-byte opcode (0x0f) */ +const insn_attr_t inat_escape_table_1[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MAKE_GROUP(10), + [0x01] = INAT_MAKE_GROUP(11), + [0x02] = INAT_MODRM, + [0x03] = INAT_MODRM, + [0x0d] = INAT_MAKE_GROUP(12), + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x10] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x11] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x12] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x13] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x14] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x15] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x16] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x17] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x18] = INAT_MAKE_GROUP(13), + [0x1a] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM, + [0x1b] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM, + [0x1f] = INAT_MODRM, + [0x20] = INAT_MODRM, + [0x21] = INAT_MODRM, + [0x22] = INAT_MODRM, + [0x23] = INAT_MODRM, + [0x28] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x29] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2a] = INAT_MODRM | INAT_VARIANT, + [0x2b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2c] = INAT_MODRM | INAT_VARIANT, + [0x2d] = INAT_MODRM | INAT_VARIANT, + [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x38] = INAT_MAKE_ESCAPE(2), + [0x3a] = INAT_MAKE_ESCAPE(3), + [0x40] = INAT_MODRM, + [0x41] = INAT_MODRM, + [0x42] = INAT_MODRM, + [0x43] = INAT_MODRM, + [0x44] = INAT_MODRM, + [0x45] = INAT_MODRM, + [0x46] = INAT_MODRM, + [0x47] = INAT_MODRM, + [0x48] = INAT_MODRM, + [0x49] = INAT_MODRM, + [0x4a] = INAT_MODRM, + [0x4b] = INAT_MODRM, + [0x4c] = INAT_MODRM, + [0x4d] = INAT_MODRM, + [0x4e] = INAT_MODRM, + [0x4f] = INAT_MODRM, + [0x50] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x51] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x52] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x53] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x54] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x55] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x56] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x57] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5c] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5d] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x60] = INAT_MODRM | INAT_VARIANT, + [0x61] = INAT_MODRM | INAT_VARIANT, + [0x62] = INAT_MODRM | INAT_VARIANT, + [0x63] = INAT_MODRM | INAT_VARIANT, + [0x64] = INAT_MODRM | INAT_VARIANT, + [0x65] = INAT_MODRM | INAT_VARIANT, + [0x66] = INAT_MODRM | INAT_VARIANT, + [0x67] = INAT_MODRM | INAT_VARIANT, + [0x68] = INAT_MODRM | INAT_VARIANT, + [0x69] = INAT_MODRM | INAT_VARIANT, + [0x6a] = INAT_MODRM | INAT_VARIANT, + [0x6b] = INAT_MODRM | INAT_VARIANT, + [0x6c] = INAT_VARIANT, + [0x6d] = INAT_VARIANT, + [0x6e] = INAT_MODRM | INAT_VARIANT, + [0x6f] = INAT_MODRM | INAT_VARIANT, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x71] = INAT_MAKE_GROUP(14), + [0x72] = INAT_MAKE_GROUP(15), + [0x73] = INAT_MAKE_GROUP(16), + [0x74] = INAT_MODRM | INAT_VARIANT, + [0x75] = INAT_MODRM | INAT_VARIANT, + [0x76] = INAT_MODRM | INAT_VARIANT, + [0x77] = INAT_VEXOK | INAT_VEXOK, + [0x78] = INAT_MODRM, + [0x79] = INAT_MODRM, + [0x7c] = INAT_VARIANT, + [0x7d] = INAT_VARIANT, + [0x7e] = INAT_MODRM | INAT_VARIANT, + [0x7f] = INAT_MODRM | INAT_VARIANT, + [0x80] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x82] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x83] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x84] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x85] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x86] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x87] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x88] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x89] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8a] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8b] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8c] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8d] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8e] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8f] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x90] = INAT_MODRM, + [0x91] = INAT_MODRM, + [0x92] = INAT_MODRM, + [0x93] = INAT_MODRM, + [0x94] = INAT_MODRM, + [0x95] = INAT_MODRM, + [0x96] = INAT_MODRM, + [0x97] = INAT_MODRM, + [0x98] = INAT_MODRM, + [0x99] = INAT_MODRM, + [0x9a] = INAT_MODRM, + [0x9b] = INAT_MODRM, + [0x9c] = INAT_MODRM, + [0x9d] = INAT_MODRM, + [0x9e] = INAT_MODRM, + [0x9f] = INAT_MODRM, + [0xa0] = INAT_FORCE64, + [0xa1] = INAT_FORCE64, + [0xa3] = INAT_MODRM, + [0xa4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0xa5] = INAT_MODRM, + [0xa6] = INAT_MAKE_GROUP(17), + [0xa7] = INAT_MAKE_GROUP(18), + [0xa8] = INAT_FORCE64, + [0xa9] = INAT_FORCE64, + [0xab] = INAT_MODRM, + [0xac] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0xad] = INAT_MODRM, + [0xae] = INAT_MAKE_GROUP(19), + [0xaf] = INAT_MODRM, + [0xb0] = INAT_MODRM, + [0xb1] = INAT_MODRM, + [0xb2] = INAT_MODRM, + [0xb3] = INAT_MODRM, + [0xb4] = INAT_MODRM, + [0xb5] = INAT_MODRM, + [0xb6] = INAT_MODRM, + [0xb7] = INAT_MODRM, + [0xb8] = INAT_VARIANT, + [0xb9] = INAT_MAKE_GROUP(20), + [0xba] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(21), + [0xbb] = INAT_MODRM, + [0xbc] = INAT_MODRM | INAT_VARIANT, + [0xbd] = INAT_MODRM | INAT_VARIANT, + [0xbe] = INAT_MODRM, + [0xbf] = INAT_MODRM, + [0xc0] = INAT_MODRM, + [0xc1] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0xc3] = INAT_MODRM, + [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0xc7] = INAT_MAKE_GROUP(22), + [0xd0] = INAT_VARIANT, + [0xd1] = INAT_MODRM | INAT_VARIANT, + [0xd2] = INAT_MODRM | INAT_VARIANT, + [0xd3] = INAT_MODRM | INAT_VARIANT, + [0xd4] = INAT_MODRM | INAT_VARIANT, + [0xd5] = INAT_MODRM | INAT_VARIANT, + [0xd6] = INAT_VARIANT, + [0xd7] = INAT_MODRM | INAT_VARIANT, + [0xd8] = INAT_MODRM | INAT_VARIANT, + [0xd9] = INAT_MODRM | INAT_VARIANT, + [0xda] = INAT_MODRM | INAT_VARIANT, + [0xdb] = INAT_MODRM | INAT_VARIANT, + [0xdc] = INAT_MODRM | INAT_VARIANT, + [0xdd] = INAT_MODRM | INAT_VARIANT, + [0xde] = INAT_MODRM | INAT_VARIANT, + [0xdf] = INAT_MODRM | INAT_VARIANT, + [0xe0] = INAT_MODRM | INAT_VARIANT, + [0xe1] = INAT_MODRM | INAT_VARIANT, + [0xe2] = INAT_MODRM | INAT_VARIANT, + [0xe3] = INAT_MODRM | INAT_VARIANT, + [0xe4] = INAT_MODRM | INAT_VARIANT, + [0xe5] = INAT_MODRM | INAT_VARIANT, + [0xe6] = INAT_VARIANT, + [0xe7] = INAT_MODRM | INAT_VARIANT, + [0xe8] = INAT_MODRM | INAT_VARIANT, + [0xe9] = INAT_MODRM | INAT_VARIANT, + [0xea] = INAT_MODRM | INAT_VARIANT, + [0xeb] = INAT_MODRM | INAT_VARIANT, + [0xec] = INAT_MODRM | INAT_VARIANT, + [0xed] = INAT_MODRM | INAT_VARIANT, + [0xee] = INAT_MODRM | INAT_VARIANT, + [0xef] = INAT_MODRM | INAT_VARIANT, + [0xf0] = INAT_VARIANT, + [0xf1] = INAT_MODRM | INAT_VARIANT, + [0xf2] = INAT_MODRM | INAT_VARIANT, + [0xf3] = INAT_MODRM | INAT_VARIANT, + [0xf4] = INAT_MODRM | INAT_VARIANT, + [0xf5] = INAT_MODRM | INAT_VARIANT, + [0xf6] = INAT_MODRM | INAT_VARIANT, + [0xf7] = INAT_MODRM | INAT_VARIANT, + [0xf8] = INAT_MODRM | INAT_VARIANT, + [0xf9] = INAT_MODRM | INAT_VARIANT, + [0xfa] = INAT_MODRM | INAT_VARIANT, + [0xfb] = INAT_MODRM | INAT_VARIANT, + [0xfc] = INAT_MODRM | INAT_VARIANT, + [0xfd] = INAT_MODRM | INAT_VARIANT, + [0xfe] = INAT_MODRM | INAT_VARIANT, +}; +const insn_attr_t inat_escape_table_1_1[INAT_OPCODE_TABLE_SIZE] = { + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x13] = INAT_MODRM | INAT_VEXOK, + [0x14] = INAT_MODRM | INAT_VEXOK, + [0x15] = INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MODRM | INAT_VEXOK, + [0x17] = INAT_MODRM | INAT_VEXOK, + [0x28] = INAT_MODRM | INAT_VEXOK, + [0x29] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM, + [0x2b] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM, + [0x2d] = INAT_MODRM, + [0x2e] = INAT_MODRM | INAT_VEXOK, + [0x2f] = INAT_MODRM | INAT_VEXOK, + [0x50] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x54] = INAT_MODRM | INAT_VEXOK, + [0x55] = INAT_MODRM | INAT_VEXOK, + [0x56] = INAT_MODRM | INAT_VEXOK, + [0x57] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5b] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x60] = INAT_MODRM | INAT_VEXOK, + [0x61] = INAT_MODRM | INAT_VEXOK, + [0x62] = INAT_MODRM | INAT_VEXOK, + [0x63] = INAT_MODRM | INAT_VEXOK, + [0x64] = INAT_MODRM | INAT_VEXOK, + [0x65] = INAT_MODRM | INAT_VEXOK, + [0x66] = INAT_MODRM | INAT_VEXOK, + [0x67] = INAT_MODRM | INAT_VEXOK, + [0x68] = INAT_MODRM | INAT_VEXOK, + [0x69] = INAT_MODRM | INAT_VEXOK, + [0x6a] = INAT_MODRM | INAT_VEXOK, + [0x6b] = INAT_MODRM | INAT_VEXOK, + [0x6c] = INAT_MODRM | INAT_VEXOK, + [0x6d] = INAT_MODRM | INAT_VEXOK, + [0x6e] = INAT_MODRM | INAT_VEXOK, + [0x6f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x74] = INAT_MODRM | INAT_VEXOK, + [0x75] = INAT_MODRM | INAT_VEXOK, + [0x76] = INAT_MODRM | INAT_VEXOK, + [0x7c] = INAT_MODRM | INAT_VEXOK, + [0x7d] = INAT_MODRM | INAT_VEXOK, + [0x7e] = INAT_MODRM | INAT_VEXOK, + [0x7f] = INAT_MODRM | INAT_VEXOK, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd0] = INAT_MODRM | INAT_VEXOK, + [0xd1] = INAT_MODRM | INAT_VEXOK, + [0xd2] = INAT_MODRM | INAT_VEXOK, + [0xd3] = INAT_MODRM | INAT_VEXOK, + [0xd4] = INAT_MODRM | INAT_VEXOK, + [0xd5] = INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM | INAT_VEXOK, + [0xd7] = INAT_MODRM | INAT_VEXOK, + [0xd8] = INAT_MODRM | INAT_VEXOK, + [0xd9] = INAT_MODRM | INAT_VEXOK, + [0xda] = INAT_MODRM | INAT_VEXOK, + [0xdb] = INAT_MODRM | INAT_VEXOK, + [0xdc] = INAT_MODRM | INAT_VEXOK, + [0xdd] = INAT_MODRM | INAT_VEXOK, + [0xde] = INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MODRM | INAT_VEXOK, + [0xe0] = INAT_MODRM | INAT_VEXOK, + [0xe1] = INAT_MODRM | INAT_VEXOK, + [0xe2] = INAT_MODRM | INAT_VEXOK, + [0xe3] = INAT_MODRM | INAT_VEXOK, + [0xe4] = INAT_MODRM | INAT_VEXOK, + [0xe5] = INAT_MODRM | INAT_VEXOK, + [0xe6] = INAT_MODRM | INAT_VEXOK, + [0xe7] = INAT_MODRM | INAT_VEXOK, + [0xe8] = INAT_MODRM | INAT_VEXOK, + [0xe9] = INAT_MODRM | INAT_VEXOK, + [0xea] = INAT_MODRM | INAT_VEXOK, + [0xeb] = INAT_MODRM | INAT_VEXOK, + [0xec] = INAT_MODRM | INAT_VEXOK, + [0xed] = INAT_MODRM | INAT_VEXOK, + [0xee] = INAT_MODRM | INAT_VEXOK, + [0xef] = INAT_MODRM | INAT_VEXOK, + [0xf1] = INAT_MODRM | INAT_VEXOK, + [0xf2] = INAT_MODRM | INAT_VEXOK, + [0xf3] = INAT_MODRM | INAT_VEXOK, + [0xf4] = INAT_MODRM | INAT_VEXOK, + [0xf5] = INAT_MODRM | INAT_VEXOK, + [0xf6] = INAT_MODRM | INAT_VEXOK, + [0xf7] = INAT_MODRM | INAT_VEXOK, + [0xf8] = INAT_MODRM | INAT_VEXOK, + [0xf9] = INAT_MODRM | INAT_VEXOK, + [0xfa] = INAT_MODRM | INAT_VEXOK, + [0xfb] = INAT_MODRM | INAT_VEXOK, + [0xfc] = INAT_MODRM | INAT_VEXOK, + [0xfd] = INAT_MODRM | INAT_VEXOK, + [0xfe] = INAT_MODRM | INAT_VEXOK, +}; +const insn_attr_t inat_escape_table_1_2[INAT_OPCODE_TABLE_SIZE] = { + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK, + [0x2d] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x52] = INAT_MODRM | INAT_VEXOK, + [0x53] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5b] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x6f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7e] = INAT_MODRM | INAT_VEXOK, + [0x7f] = INAT_MODRM | INAT_VEXOK, + [0xb8] = INAT_MODRM, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM, + [0xe6] = INAT_MODRM | INAT_VEXOK, +}; +const insn_attr_t inat_escape_table_1_3[INAT_OPCODE_TABLE_SIZE] = { + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK, + [0x2d] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7c] = INAT_MODRM | INAT_VEXOK, + [0x7d] = INAT_MODRM | INAT_VEXOK, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd0] = INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM, + [0xe6] = INAT_MODRM | INAT_VEXOK, + [0xf0] = INAT_MODRM | INAT_VEXOK, +}; + +/* Table: 3-byte opcode 1 (0x0f 0x38) */ +const insn_attr_t inat_escape_table_2[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MODRM | INAT_VARIANT, + [0x01] = INAT_MODRM | INAT_VARIANT, + [0x02] = INAT_MODRM | INAT_VARIANT, + [0x03] = INAT_MODRM | INAT_VARIANT, + [0x04] = INAT_MODRM | INAT_VARIANT, + [0x05] = INAT_MODRM | INAT_VARIANT, + [0x06] = INAT_MODRM | INAT_VARIANT, + [0x07] = INAT_MODRM | INAT_VARIANT, + [0x08] = INAT_MODRM | INAT_VARIANT, + [0x09] = INAT_MODRM | INAT_VARIANT, + [0x0a] = INAT_MODRM | INAT_VARIANT, + [0x0b] = INAT_MODRM | INAT_VARIANT, + [0x0c] = INAT_VARIANT, + [0x0d] = INAT_VARIANT, + [0x0e] = INAT_VARIANT, + [0x0f] = INAT_VARIANT, + [0x10] = INAT_VARIANT, + [0x13] = INAT_VARIANT, + [0x14] = INAT_VARIANT, + [0x15] = INAT_VARIANT, + [0x16] = INAT_VARIANT, + [0x17] = INAT_VARIANT, + [0x18] = INAT_VARIANT, + [0x19] = INAT_VARIANT, + [0x1a] = INAT_VARIANT, + [0x1c] = INAT_MODRM | INAT_VARIANT, + [0x1d] = INAT_MODRM | INAT_VARIANT, + [0x1e] = INAT_MODRM | INAT_VARIANT, + [0x20] = INAT_VARIANT, + [0x21] = INAT_VARIANT, + [0x22] = INAT_VARIANT, + [0x23] = INAT_VARIANT, + [0x24] = INAT_VARIANT, + [0x25] = INAT_VARIANT, + [0x28] = INAT_VARIANT, + [0x29] = INAT_VARIANT, + [0x2a] = INAT_VARIANT, + [0x2b] = INAT_VARIANT, + [0x2c] = INAT_VARIANT, + [0x2d] = INAT_VARIANT, + [0x2e] = INAT_VARIANT, + [0x2f] = INAT_VARIANT, + [0x30] = INAT_VARIANT, + [0x31] = INAT_VARIANT, + [0x32] = INAT_VARIANT, + [0x33] = INAT_VARIANT, + [0x34] = INAT_VARIANT, + [0x35] = INAT_VARIANT, + [0x36] = INAT_VARIANT, + [0x37] = INAT_VARIANT, + [0x38] = INAT_VARIANT, + [0x39] = INAT_VARIANT, + [0x3a] = INAT_VARIANT, + [0x3b] = INAT_VARIANT, + [0x3c] = INAT_VARIANT, + [0x3d] = INAT_VARIANT, + [0x3e] = INAT_VARIANT, + [0x3f] = INAT_VARIANT, + [0x40] = INAT_VARIANT, + [0x41] = INAT_VARIANT, + [0x45] = INAT_VARIANT, + [0x46] = INAT_VARIANT, + [0x47] = INAT_VARIANT, + [0x58] = INAT_VARIANT, + [0x59] = INAT_VARIANT, + [0x5a] = INAT_VARIANT, + [0x78] = INAT_VARIANT, + [0x79] = INAT_VARIANT, + [0x80] = INAT_VARIANT, + [0x81] = INAT_VARIANT, + [0x82] = INAT_VARIANT, + [0x8c] = INAT_VARIANT, + [0x8e] = INAT_VARIANT, + [0x90] = INAT_VARIANT, + [0x91] = INAT_VARIANT, + [0x92] = INAT_VARIANT, + [0x93] = INAT_VARIANT, + [0x96] = INAT_VARIANT, + [0x97] = INAT_VARIANT, + [0x98] = INAT_VARIANT, + [0x99] = INAT_VARIANT, + [0x9a] = INAT_VARIANT, + [0x9b] = INAT_VARIANT, + [0x9c] = INAT_VARIANT, + [0x9d] = INAT_VARIANT, + [0x9e] = INAT_VARIANT, + [0x9f] = INAT_VARIANT, + [0xa6] = INAT_VARIANT, + [0xa7] = INAT_VARIANT, + [0xa8] = INAT_VARIANT, + [0xa9] = INAT_VARIANT, + [0xaa] = INAT_VARIANT, + [0xab] = INAT_VARIANT, + [0xac] = INAT_VARIANT, + [0xad] = INAT_VARIANT, + [0xae] = INAT_VARIANT, + [0xaf] = INAT_VARIANT, + [0xb6] = INAT_VARIANT, + [0xb7] = INAT_VARIANT, + [0xb8] = INAT_VARIANT, + [0xb9] = INAT_VARIANT, + [0xba] = INAT_VARIANT, + [0xbb] = INAT_VARIANT, + [0xbc] = INAT_VARIANT, + [0xbd] = INAT_VARIANT, + [0xbe] = INAT_VARIANT, + [0xbf] = INAT_VARIANT, + [0xdb] = INAT_VARIANT, + [0xdc] = INAT_VARIANT, + [0xdd] = INAT_VARIANT, + [0xde] = INAT_VARIANT, + [0xdf] = INAT_VARIANT, + [0xf0] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0xf1] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0xf2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf3] = INAT_MAKE_GROUP(23), + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT, + [0xf6] = INAT_VARIANT, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT, +}; +const insn_attr_t inat_escape_table_2_1[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MODRM | INAT_VEXOK, + [0x01] = INAT_MODRM | INAT_VEXOK, + [0x02] = INAT_MODRM | INAT_VEXOK, + [0x03] = INAT_MODRM | INAT_VEXOK, + [0x04] = INAT_MODRM | INAT_VEXOK, + [0x05] = INAT_MODRM | INAT_VEXOK, + [0x06] = INAT_MODRM | INAT_VEXOK, + [0x07] = INAT_MODRM | INAT_VEXOK, + [0x08] = INAT_MODRM | INAT_VEXOK, + [0x09] = INAT_MODRM | INAT_VEXOK, + [0x0a] = INAT_MODRM | INAT_VEXOK, + [0x0b] = INAT_MODRM | INAT_VEXOK, + [0x0c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x10] = INAT_MODRM, + [0x13] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x14] = INAT_MODRM, + [0x15] = INAT_MODRM, + [0x16] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x17] = INAT_MODRM | INAT_VEXOK, + [0x18] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x19] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1c] = INAT_MODRM | INAT_VEXOK, + [0x1d] = INAT_MODRM | INAT_VEXOK, + [0x1e] = INAT_MODRM | INAT_VEXOK, + [0x20] = INAT_MODRM | INAT_VEXOK, + [0x21] = INAT_MODRM | INAT_VEXOK, + [0x22] = INAT_MODRM | INAT_VEXOK, + [0x23] = INAT_MODRM | INAT_VEXOK, + [0x24] = INAT_MODRM | INAT_VEXOK, + [0x25] = INAT_MODRM | INAT_VEXOK, + [0x28] = INAT_MODRM | INAT_VEXOK, + [0x29] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2b] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x30] = INAT_MODRM | INAT_VEXOK, + [0x31] = INAT_MODRM | INAT_VEXOK, + [0x32] = INAT_MODRM | INAT_VEXOK, + [0x33] = INAT_MODRM | INAT_VEXOK, + [0x34] = INAT_MODRM | INAT_VEXOK, + [0x35] = INAT_MODRM | INAT_VEXOK, + [0x36] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x37] = INAT_MODRM | INAT_VEXOK, + [0x38] = INAT_MODRM | INAT_VEXOK, + [0x39] = INAT_MODRM | INAT_VEXOK, + [0x3a] = INAT_MODRM | INAT_VEXOK, + [0x3b] = INAT_MODRM | INAT_VEXOK, + [0x3c] = INAT_MODRM | INAT_VEXOK, + [0x3d] = INAT_MODRM | INAT_VEXOK, + [0x3e] = INAT_MODRM | INAT_VEXOK, + [0x3f] = INAT_MODRM | INAT_VEXOK, + [0x40] = INAT_MODRM | INAT_VEXOK, + [0x41] = INAT_MODRM | INAT_VEXOK, + [0x45] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x46] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x47] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x78] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x79] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x80] = INAT_MODRM, + [0x81] = INAT_MODRM, + [0x82] = INAT_MODRM, + [0x8c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x8e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x90] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x91] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x92] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x93] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x96] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x97] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x98] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x99] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9b] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xaa] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xab] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xac] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xad] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xae] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xaf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xba] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbb] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbc] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbd] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbe] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xdb] = INAT_MODRM | INAT_VEXOK, + [0xdc] = INAT_MODRM | INAT_VEXOK, + [0xdd] = INAT_MODRM | INAT_VEXOK, + [0xde] = INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MODRM | INAT_VEXOK, + [0xf0] = INAT_MODRM, + [0xf1] = INAT_MODRM, + [0xf6] = INAT_MODRM, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; +const insn_attr_t inat_escape_table_2_2[INAT_OPCODE_TABLE_SIZE] = { + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf6] = INAT_MODRM, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; +const insn_attr_t inat_escape_table_2_3[INAT_OPCODE_TABLE_SIZE] = { + [0xf0] = INAT_MODRM | INAT_MODRM, + [0xf1] = INAT_MODRM | INAT_MODRM, + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; + +/* Table: 3-byte opcode 2 (0x0f 0x3a) */ +const insn_attr_t inat_escape_table_3[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_VARIANT, + [0x01] = INAT_VARIANT, + [0x02] = INAT_VARIANT, + [0x04] = INAT_VARIANT, + [0x05] = INAT_VARIANT, + [0x06] = INAT_VARIANT, + [0x08] = INAT_VARIANT, + [0x09] = INAT_VARIANT, + [0x0a] = INAT_VARIANT, + [0x0b] = INAT_VARIANT, + [0x0c] = INAT_VARIANT, + [0x0d] = INAT_VARIANT, + [0x0e] = INAT_VARIANT, + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x14] = INAT_VARIANT, + [0x15] = INAT_VARIANT, + [0x16] = INAT_VARIANT, + [0x17] = INAT_VARIANT, + [0x18] = INAT_VARIANT, + [0x19] = INAT_VARIANT, + [0x1d] = INAT_VARIANT, + [0x20] = INAT_VARIANT, + [0x21] = INAT_VARIANT, + [0x22] = INAT_VARIANT, + [0x38] = INAT_VARIANT, + [0x39] = INAT_VARIANT, + [0x40] = INAT_VARIANT, + [0x41] = INAT_VARIANT, + [0x42] = INAT_VARIANT, + [0x44] = INAT_VARIANT, + [0x46] = INAT_VARIANT, + [0x4a] = INAT_VARIANT, + [0x4b] = INAT_VARIANT, + [0x4c] = INAT_VARIANT, + [0x60] = INAT_VARIANT, + [0x61] = INAT_VARIANT, + [0x62] = INAT_VARIANT, + [0x63] = INAT_VARIANT, + [0xdf] = INAT_VARIANT, + [0xf0] = INAT_VARIANT, +}; +const insn_attr_t inat_escape_table_3_1[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x01] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x02] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x05] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x06] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x08] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x09] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0e] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x15] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x17] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x18] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x19] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x20] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x21] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x22] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x38] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x39] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x40] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x41] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x42] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x44] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x46] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x60] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x61] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x62] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x63] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, +}; +const insn_attr_t inat_escape_table_3_3[INAT_OPCODE_TABLE_SIZE] = { + [0xf0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; + +/* GrpTable: Grp1 */ + +/* GrpTable: Grp1A */ + +/* GrpTable: Grp2 */ + +/* GrpTable: Grp3_1 */ +const insn_attr_t inat_group_table_6[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, +}; + +/* GrpTable: Grp3_2 */ +const insn_attr_t inat_group_table_7[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, +}; + +/* GrpTable: Grp4 */ +const insn_attr_t inat_group_table_8[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, +}; + +/* GrpTable: Grp5 */ +const insn_attr_t inat_group_table_9[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM | INAT_FORCE64, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM | INAT_FORCE64, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM | INAT_FORCE64, +}; + +/* GrpTable: Grp6 */ +const insn_attr_t inat_group_table_10[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, +}; + +/* GrpTable: Grp7 */ +const insn_attr_t inat_group_table_11[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, +}; + +/* GrpTable: Grp8 */ + +/* GrpTable: Grp9 */ +const insn_attr_t inat_group_table_22[INAT_GROUP_TABLE_SIZE] = { + [0x1] = INAT_MODRM, + [0x6] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0x7] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, +}; +const insn_attr_t inat_group_table_22_1[INAT_GROUP_TABLE_SIZE] = { + [0x6] = INAT_MODRM, +}; +const insn_attr_t inat_group_table_22_2[INAT_GROUP_TABLE_SIZE] = { + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, +}; + +/* GrpTable: Grp10 */ + +/* GrpTable: Grp11A */ +const insn_attr_t inat_group_table_4[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE), +}; + +/* GrpTable: Grp11B */ +const insn_attr_t inat_group_table_5[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x7] = INAT_MAKE_IMM(INAT_IMM_VWORD32), +}; + +/* GrpTable: Grp12 */ +const insn_attr_t inat_group_table_14[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, +}; +const insn_attr_t inat_group_table_14_1[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, +}; + +/* GrpTable: Grp13 */ +const insn_attr_t inat_group_table_15[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, +}; +const insn_attr_t inat_group_table_15_1[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, +}; + +/* GrpTable: Grp14 */ +const insn_attr_t inat_group_table_16[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x3] = INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x7] = INAT_VARIANT, +}; +const insn_attr_t inat_group_table_16_1[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, +}; + +/* GrpTable: Grp15 */ +const insn_attr_t inat_group_table_19[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_VARIANT, + [0x1] = INAT_VARIANT, + [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, +}; +const insn_attr_t inat_group_table_19_2[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, +}; + +/* GrpTable: Grp16 */ +const insn_attr_t inat_group_table_13[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, +}; + +/* GrpTable: Grp17 */ +const insn_attr_t inat_group_table_23[INAT_GROUP_TABLE_SIZE] = { + [0x1] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; + +/* GrpTable: GrpP */ + +/* GrpTable: GrpPDLK */ + +/* GrpTable: GrpRNG */ + +/* Escape opcode map array */ +const insn_attr_t * const inat_escape_tables[INAT_ESC_MAX + 1][INAT_LSTPFX_MAX + 1] = { + [1][0] = inat_escape_table_1, + [1][1] = inat_escape_table_1_1, + [1][2] = inat_escape_table_1_2, + [1][3] = inat_escape_table_1_3, + [2][0] = inat_escape_table_2, + [2][1] = inat_escape_table_2_1, + [2][2] = inat_escape_table_2_2, + [2][3] = inat_escape_table_2_3, + [3][0] = inat_escape_table_3, + [3][1] = inat_escape_table_3_1, + [3][3] = inat_escape_table_3_3, +}; + +/* Group opcode map array */ +const insn_attr_t * const inat_group_tables[INAT_GRP_MAX + 1][INAT_LSTPFX_MAX + 1] = { + [4][0] = inat_group_table_4, + [5][0] = inat_group_table_5, + [6][0] = inat_group_table_6, + [7][0] = inat_group_table_7, + [8][0] = inat_group_table_8, + [9][0] = inat_group_table_9, + [10][0] = inat_group_table_10, + [11][0] = inat_group_table_11, + [13][0] = inat_group_table_13, + [14][0] = inat_group_table_14, + [14][1] = inat_group_table_14_1, + [15][0] = inat_group_table_15, + [15][1] = inat_group_table_15_1, + [16][0] = inat_group_table_16, + [16][1] = inat_group_table_16_1, + [19][0] = inat_group_table_19, + [19][2] = inat_group_table_19_2, + [22][0] = inat_group_table_22, + [22][1] = inat_group_table_22_1, + [22][2] = inat_group_table_22_2, + [23][0] = inat_group_table_23, +}; + +/* AVX opcode map array */ +const insn_attr_t * const inat_avx_tables[X86_VEX_M_MAX + 1][INAT_LSTPFX_MAX + 1] = { + [1][0] = inat_escape_table_1, + [1][1] = inat_escape_table_1_1, + [1][2] = inat_escape_table_1_2, + [1][3] = inat_escape_table_1_3, + [2][0] = inat_escape_table_2, + [2][1] = inat_escape_table_2_1, + [2][2] = inat_escape_table_2_2, + [2][3] = inat_escape_table_2_3, + [3][0] = inat_escape_table_3, + [3][1] = inat_escape_table_3_1, + [3][3] = inat_escape_table_3_3, +}; diff --git a/kpatch-build/insn/inat.c b/kpatch-build/insn/inat.c new file mode 100644 index 0000000..c1f01a8 --- /dev/null +++ b/kpatch-build/insn/inat.c @@ -0,0 +1,97 @@ +/* + * x86 instruction attribute tables + * + * Written by Masami Hiramatsu + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#include + +/* Attribute tables are generated from opcode map */ +#include "inat-tables.c" + +/* Attribute search APIs */ +insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode) +{ + return inat_primary_table[opcode]; +} + +int inat_get_last_prefix_id(insn_byte_t last_pfx) +{ + insn_attr_t lpfx_attr; + + lpfx_attr = inat_get_opcode_attribute(last_pfx); + return inat_last_prefix_id(lpfx_attr); +} + +insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, int lpfx_id, + insn_attr_t esc_attr) +{ + const insn_attr_t *table; + int n; + + n = inat_escape_id(esc_attr); + + table = inat_escape_tables[n][0]; + if (!table) + return 0; + if (inat_has_variant(table[opcode]) && lpfx_id) { + table = inat_escape_tables[n][lpfx_id]; + if (!table) + return 0; + } + return table[opcode]; +} + +insn_attr_t inat_get_group_attribute(insn_byte_t modrm, int lpfx_id, + insn_attr_t grp_attr) +{ + const insn_attr_t *table; + int n; + + n = inat_group_id(grp_attr); + + table = inat_group_tables[n][0]; + if (!table) + return inat_group_common_attribute(grp_attr); + if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) { + table = inat_group_tables[n][lpfx_id]; + if (!table) + return inat_group_common_attribute(grp_attr); + } + return table[X86_MODRM_REG(modrm)] | + inat_group_common_attribute(grp_attr); +} + +insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m, + insn_byte_t vex_p) +{ + const insn_attr_t *table; + if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX) + return 0; + /* At first, this checks the master table */ + table = inat_avx_tables[vex_m][0]; + if (!table) + return 0; + if (!inat_is_group(table[opcode]) && vex_p) { + /* If this is not a group, get attribute directly */ + table = inat_avx_tables[vex_m][vex_p]; + if (!table) + return 0; + } + return table[opcode]; +} + diff --git a/kpatch-build/insn/inat.d b/kpatch-build/insn/inat.d new file mode 100644 index 0000000..bdd19f9 --- /dev/null +++ b/kpatch-build/insn/inat.d @@ -0,0 +1,10 @@ +insn/inat.o: insn/inat.c insn/asm/insn.h insn/asm/inat.h \ + insn/asm/inat_types.h insn/inat-tables.c + +insn/asm/insn.h: + +insn/asm/inat.h: + +insn/asm/inat_types.h: + +insn/inat-tables.c: diff --git a/kpatch-build/insn/inat.o b/kpatch-build/insn/inat.o new file mode 100644 index 0000000000000000000000000000000000000000..3e79de9b1f112ee2eb9fd39e1faf602a8e636c6e GIT binary patch literal 28528 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 +#else +#include +#endif +#include +#include + +#define unlikely(a) a + +/* Verify next sizeof(t) bytes can be on the same instruction */ +#define validate_next(t, insn, n) \ + ((insn)->next_byte + sizeof(t) + n - (insn)->kaddr <= MAX_INSN_SIZE) + +#define __get_next(t, insn) \ + ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) + +#define __peek_nbyte_next(t, insn, n) \ + ({ t r = *(t*)((insn)->next_byte + n); r; }) + +#define get_next(t, insn) \ + ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); }) + +#define peek_nbyte_next(t, insn, n) \ + ({ if (unlikely(!validate_next(t, insn, n))) goto err_out; __peek_nbyte_next(t, insn, n); }) + +#define peek_next(t, insn) peek_nbyte_next(t, insn, 0) + +/** + * insn_init() - initialize struct insn + * @insn: &struct insn to be initialized + * @kaddr: address (in kernel memory) of instruction (or copy thereof) + * @x86_64: !0 for 64-bit kernel or 64-bit app + */ +void insn_init(struct insn *insn, const void *kaddr, int x86_64) +{ + memset(insn, 0, sizeof(*insn)); + insn->kaddr = kaddr; + insn->next_byte = kaddr; + insn->x86_64 = x86_64 ? 1 : 0; + insn->opnd_bytes = 4; + if (x86_64) + insn->addr_bytes = 8; + else + insn->addr_bytes = 4; +} + +/** + * insn_get_prefixes - scan x86 instruction prefix bytes + * @insn: &struct insn containing instruction + * + * Populates the @insn->prefixes bitmap, and updates @insn->next_byte + * to point to the (first) opcode. No effect if @insn->prefixes.got + * is already set. + */ +void insn_get_prefixes(struct insn *insn) +{ + struct insn_field *prefixes = &insn->prefixes; + insn_attr_t attr; + insn_byte_t b, lb; + int i, nb; + + if (prefixes->got) + return; + + nb = 0; + lb = 0; + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + while (inat_is_legacy_prefix(attr)) { + /* Skip if same prefix */ + for (i = 0; i < nb; i++) + if (prefixes->bytes[i] == b) + goto found; + if (nb == 4) + /* Invalid instruction */ + break; + prefixes->bytes[nb++] = b; + if (inat_is_address_size_prefix(attr)) { + /* address size switches 2/4 or 4/8 */ + if (insn->x86_64) + insn->addr_bytes ^= 12; + else + insn->addr_bytes ^= 6; + } else if (inat_is_operand_size_prefix(attr)) { + /* oprand size switches 2/4 */ + insn->opnd_bytes ^= 6; + } +found: + prefixes->nbytes++; + insn->next_byte++; + lb = b; + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + } + /* Set the last prefix */ + if (lb && lb != insn->prefixes.bytes[3]) { + if (unlikely(insn->prefixes.bytes[3])) { + /* Swap the last prefix */ + b = insn->prefixes.bytes[3]; + for (i = 0; i < nb; i++) + if (prefixes->bytes[i] == lb) + prefixes->bytes[i] = b; + } + insn->prefixes.bytes[3] = lb; + } + + /* Decode REX prefix */ + if (insn->x86_64) { + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + if (inat_is_rex_prefix(attr)) { + insn->rex_prefix.value = b; + insn->rex_prefix.nbytes = 1; + insn->next_byte++; + if (X86_REX_W(b)) + /* REX.W overrides opnd_size */ + insn->opnd_bytes = 8; + } + } + insn->rex_prefix.got = 1; + + /* Decode VEX prefix */ + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + if (inat_is_vex_prefix(attr)) { + insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1); + if (!insn->x86_64) { + /* + * In 32-bits mode, if the [7:6] bits (mod bits of + * ModRM) on the second byte are not 11b, it is + * LDS or LES. + */ + if (X86_MODRM_MOD(b2) != 3) + goto vex_end; + } + insn->vex_prefix.bytes[0] = b; + insn->vex_prefix.bytes[1] = b2; + if (inat_is_vex3_prefix(attr)) { + b2 = peek_nbyte_next(insn_byte_t, insn, 2); + insn->vex_prefix.bytes[2] = b2; + insn->vex_prefix.nbytes = 3; + insn->next_byte += 3; + if (insn->x86_64 && X86_VEX_W(b2)) + /* VEX.W overrides opnd_size */ + insn->opnd_bytes = 8; + } else { + insn->vex_prefix.nbytes = 2; + insn->next_byte += 2; + } + } +vex_end: + insn->vex_prefix.got = 1; + + prefixes->got = 1; + +err_out: + return; +} + +/** + * insn_get_opcode - collect opcode(s) + * @insn: &struct insn containing instruction + * + * Populates @insn->opcode, updates @insn->next_byte to point past the + * opcode byte(s), and set @insn->attr (except for groups). + * If necessary, first collects any preceding (prefix) bytes. + * Sets @insn->opcode.value = opcode1. No effect if @insn->opcode.got + * is already 1. + */ +void insn_get_opcode(struct insn *insn) +{ + struct insn_field *opcode = &insn->opcode; + insn_byte_t op; + int pfx_id; + if (opcode->got) + return; + if (!insn->prefixes.got) + insn_get_prefixes(insn); + + /* Get first opcode */ + op = get_next(insn_byte_t, insn); + opcode->bytes[0] = op; + opcode->nbytes = 1; + + /* Check if there is VEX prefix or not */ + if (insn_is_avx(insn)) { + insn_byte_t m, p; + m = insn_vex_m_bits(insn); + p = insn_vex_p_bits(insn); + insn->attr = inat_get_avx_attribute(op, m, p); + if (!inat_accept_vex(insn->attr) && !inat_is_group(insn->attr)) + insn->attr = 0; /* This instruction is bad */ + goto end; /* VEX has only 1 byte for opcode */ + } + + insn->attr = inat_get_opcode_attribute(op); + while (inat_is_escape(insn->attr)) { + /* Get escaped opcode */ + op = get_next(insn_byte_t, insn); + opcode->bytes[opcode->nbytes++] = op; + pfx_id = insn_last_prefix_id(insn); + insn->attr = inat_get_escape_attribute(op, pfx_id, insn->attr); + } + if (inat_must_vex(insn->attr)) + insn->attr = 0; /* This instruction is bad */ +end: + opcode->got = 1; + +err_out: + return; +} + +/** + * insn_get_modrm - collect ModRM byte, if any + * @insn: &struct insn containing instruction + * + * Populates @insn->modrm and updates @insn->next_byte to point past the + * ModRM byte, if any. If necessary, first collects the preceding bytes + * (prefixes and opcode(s)). No effect if @insn->modrm.got is already 1. + */ +void insn_get_modrm(struct insn *insn) +{ + struct insn_field *modrm = &insn->modrm; + insn_byte_t pfx_id, mod; + if (modrm->got) + return; + if (!insn->opcode.got) + insn_get_opcode(insn); + + if (inat_has_modrm(insn->attr)) { + mod = get_next(insn_byte_t, insn); + modrm->value = mod; + modrm->nbytes = 1; + if (inat_is_group(insn->attr)) { + pfx_id = insn_last_prefix_id(insn); + insn->attr = inat_get_group_attribute(mod, pfx_id, + insn->attr); + if (insn_is_avx(insn) && !inat_accept_vex(insn->attr)) + insn->attr = 0; /* This is bad */ + } + } + + if (insn->x86_64 && inat_is_force64(insn->attr)) + insn->opnd_bytes = 8; + modrm->got = 1; + +err_out: + return; +} + + +/** + * insn_rip_relative() - Does instruction use RIP-relative addressing mode? + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * ModRM byte. No effect if @insn->x86_64 is 0. + */ +int insn_rip_relative(struct insn *insn) +{ + struct insn_field *modrm = &insn->modrm; + + if (!insn->x86_64) + return 0; + if (!modrm->got) + insn_get_modrm(insn); + /* + * For rip-relative instructions, the mod field (top 2 bits) + * is zero and the r/m field (bottom 3 bits) is 0x5. + */ + return (modrm->nbytes && (modrm->value & 0xc7) == 0x5); +} + +/** + * insn_get_sib() - Get the SIB byte of instruction + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * ModRM byte. + */ +void insn_get_sib(struct insn *insn) +{ + insn_byte_t modrm; + + if (insn->sib.got) + return; + if (!insn->modrm.got) + insn_get_modrm(insn); + if (insn->modrm.nbytes) { + modrm = (insn_byte_t)insn->modrm.value; + if (insn->addr_bytes != 2 && + X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) { + insn->sib.value = get_next(insn_byte_t, insn); + insn->sib.nbytes = 1; + } + } + insn->sib.got = 1; + +err_out: + return; +} + + +/** + * insn_get_displacement() - Get the displacement of instruction + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * SIB byte. + * Displacement value is sign-expanded. + */ +void insn_get_displacement(struct insn *insn) +{ + insn_byte_t mod, rm, base; + + if (insn->displacement.got) + return; + if (!insn->sib.got) + insn_get_sib(insn); + if (insn->modrm.nbytes) { + /* + * Interpreting the modrm byte: + * mod = 00 - no displacement fields (exceptions below) + * mod = 01 - 1-byte displacement field + * mod = 10 - displacement field is 4 bytes, or 2 bytes if + * address size = 2 (0x67 prefix in 32-bit mode) + * mod = 11 - no memory operand + * + * If address size = 2... + * mod = 00, r/m = 110 - displacement field is 2 bytes + * + * If address size != 2... + * mod != 11, r/m = 100 - SIB byte exists + * mod = 00, SIB base = 101 - displacement field is 4 bytes + * mod = 00, r/m = 101 - rip-relative addressing, displacement + * field is 4 bytes + */ + mod = X86_MODRM_MOD(insn->modrm.value); + rm = X86_MODRM_RM(insn->modrm.value); + base = X86_SIB_BASE(insn->sib.value); + if (mod == 3) + goto out; + if (mod == 1) { + insn->displacement.value = get_next(char, insn); + insn->displacement.nbytes = 1; + } else if (insn->addr_bytes == 2) { + if ((mod == 0 && rm == 6) || mod == 2) { + insn->displacement.value = + get_next(short, insn); + insn->displacement.nbytes = 2; + } + } else { + if ((mod == 0 && rm == 5) || mod == 2 || + (mod == 0 && base == 5)) { + insn->displacement.value = get_next(int, insn); + insn->displacement.nbytes = 4; + } + } + } +out: + insn->displacement.got = 1; + +err_out: + return; +} + +/* Decode moffset16/32/64. Return 0 if failed */ +static int __get_moffset(struct insn *insn) +{ + switch (insn->addr_bytes) { + case 2: + insn->moffset1.value = get_next(short, insn); + insn->moffset1.nbytes = 2; + break; + case 4: + insn->moffset1.value = get_next(int, insn); + insn->moffset1.nbytes = 4; + break; + case 8: + insn->moffset1.value = get_next(int, insn); + insn->moffset1.nbytes = 4; + insn->moffset2.value = get_next(int, insn); + insn->moffset2.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->moffset1.got = insn->moffset2.got = 1; + + return 1; + +err_out: + return 0; +} + +/* Decode imm v32(Iz). Return 0 if failed */ +static int __get_immv32(struct insn *insn) +{ + switch (insn->opnd_bytes) { + case 2: + insn->immediate.value = get_next(short, insn); + insn->immediate.nbytes = 2; + break; + case 4: + case 8: + insn->immediate.value = get_next(int, insn); + insn->immediate.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + + return 1; + +err_out: + return 0; +} + +/* Decode imm v64(Iv/Ov), Return 0 if failed */ +static int __get_immv(struct insn *insn) +{ + switch (insn->opnd_bytes) { + case 2: + insn->immediate1.value = get_next(short, insn); + insn->immediate1.nbytes = 2; + break; + case 4: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + break; + case 8: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + insn->immediate2.value = get_next(int, insn); + insn->immediate2.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->immediate1.got = insn->immediate2.got = 1; + + return 1; +err_out: + return 0; +} + +/* Decode ptr16:16/32(Ap) */ +static int __get_immptr(struct insn *insn) +{ + switch (insn->opnd_bytes) { + case 2: + insn->immediate1.value = get_next(short, insn); + insn->immediate1.nbytes = 2; + break; + case 4: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + break; + case 8: + /* ptr16:64 is not exist (no segment) */ + return 0; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->immediate2.value = get_next(unsigned short, insn); + insn->immediate2.nbytes = 2; + insn->immediate1.got = insn->immediate2.got = 1; + + return 1; +err_out: + return 0; +} + +/** + * insn_get_immediate() - Get the immediates of instruction + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * displacement bytes. + * Basically, most of immediates are sign-expanded. Unsigned-value can be + * get by bit masking with ((1 << (nbytes * 8)) - 1) + */ +void insn_get_immediate(struct insn *insn) +{ + if (insn->immediate.got) + return; + if (!insn->displacement.got) + insn_get_displacement(insn); + + if (inat_has_moffset(insn->attr)) { + if (!__get_moffset(insn)) + goto err_out; + goto done; + } + + if (!inat_has_immediate(insn->attr)) + /* no immediates */ + goto done; + + switch (inat_immediate_size(insn->attr)) { + case INAT_IMM_BYTE: + insn->immediate.value = get_next(char, insn); + insn->immediate.nbytes = 1; + break; + case INAT_IMM_WORD: + insn->immediate.value = get_next(short, insn); + insn->immediate.nbytes = 2; + break; + case INAT_IMM_DWORD: + insn->immediate.value = get_next(int, insn); + insn->immediate.nbytes = 4; + break; + case INAT_IMM_QWORD: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + insn->immediate2.value = get_next(int, insn); + insn->immediate2.nbytes = 4; + break; + case INAT_IMM_PTR: + if (!__get_immptr(insn)) + goto err_out; + break; + case INAT_IMM_VWORD32: + if (!__get_immv32(insn)) + goto err_out; + break; + case INAT_IMM_VWORD: + if (!__get_immv(insn)) + goto err_out; + break; + default: + /* Here, insn must have an immediate, but failed */ + goto err_out; + } + if (inat_has_second_immediate(insn->attr)) { + insn->immediate2.value = get_next(char, insn); + insn->immediate2.nbytes = 1; + } +done: + insn->immediate.got = 1; + +err_out: + return; +} + +/** + * insn_get_length() - Get the length of instruction + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * immediates bytes. + */ +void insn_get_length(struct insn *insn) +{ + if (insn->length) + return; + if (!insn->immediate.got) + insn_get_immediate(insn); + insn->length = (unsigned char)((unsigned long)insn->next_byte + - (unsigned long)insn->kaddr); +} diff --git a/kpatch-build/insn/insn.d b/kpatch-build/insn/insn.d new file mode 100644 index 0000000..2eae0e5 --- /dev/null +++ b/kpatch-build/insn/insn.d @@ -0,0 +1,8 @@ +insn/insn.o: insn/insn.c insn/asm/inat.h insn/asm/inat_types.h \ + insn/asm/insn.h + +insn/asm/inat.h: + +insn/asm/inat_types.h: + +insn/asm/insn.h: diff --git a/kpatch-build/insn/insn.o b/kpatch-build/insn/insn.o new file mode 100644 index 0000000000000000000000000000000000000000..44ebef0f9dcda0ef98524afb4acff793ad51a058 GIT binary patch literal 24504 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 Re%?}1U3*PltY5YMe*+#tEam_J literal 0 HcmV?d00001 diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build new file mode 100755 index 0000000..bec8010 --- /dev/null +++ b/kpatch-build/kpatch-build @@ -0,0 +1,1401 @@ +#!/bin/bash +# +# kpatch build script +# +# Copyright (C) 2014 Seth Jennings +# Copyright (C) 2013,2014 Josh Poimboeuf +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, +# 02110-1301, USA. + +# This script takes a patch based on the version of the kernel +# currently running and creates a kernel module that will +# replace modified functions in the kernel such that the +# patched code takes effect. + +# This script: +# - Either uses a specified kernel source directory or downloads the kernel +# source package for the currently running kernel +# - Unpacks and prepares the source package for building if necessary +# - Builds the base kernel or module +# - Builds the patched kernel/module and monitors changed objects +# - Builds the patched objects with gcc flags -f[function|data]-sections +# - Runs kpatch tools to create and link the patch kernel module + +set -o pipefail + +BASE="$PWD" +SCRIPTDIR="$(readlink -f "$(dirname "$(type -p "$0")")")" +ARCH="$(uname -m)" +CPUS="$(getconf _NPROCESSORS_ONLN)" +CACHEDIR="${CACHEDIR:-$HOME/.kpatch}" +KERNEL_SRCDIR="$CACHEDIR/src" +RPMTOPDIR="$CACHEDIR/buildroot" +VERSIONFILE="$CACHEDIR/version" +TEMPDIR="$CACHEDIR/tmp" +ENVFILE="$TEMPDIR/kpatch-build.env" +LOGFILE="$CACHEDIR/build.log" +RELEASE_FILE=/etc/os-release +DEBUG=0 +SKIPCLEANUP=0 +SKIPCOMPILERCHECK=0 +ARCH_KCFLAGS="" +DEBUG_KCFLAGS="" +declare -a PATCH_LIST +APPLIED_PATCHES=0 +OOT_MODULE= +KLP_REPLACE=1 + +GCC="${CROSS_COMPILE:-}gcc" +CLANG="${CROSS_COMPILE:-}clang" +LD="${CROSS_COMPILE:-}ld" +LLD="${CROSS_COMPILE:-}ld.lld" +READELF="${CROSS_COMPILE:-}readelf" +OBJCOPY="${CROSS_COMPILE:-}objcopy" + +warn() { + echo "ERROR: $1" >&2 +} + +die() { + if [[ -z "$1" ]]; then + msg="kpatch build failed" + else + msg="$1" + fi + + if [[ -e "$LOGFILE" ]]; then + warn "$msg. Check $LOGFILE for more details." + else + warn "$msg." + fi + + exit 1 +} + +logger() { + local to_stdout=${1:-0} + + if [[ $DEBUG -ge 2 ]] || [[ "$to_stdout" -eq 1 ]]; then + # Log to both stdout and the logfile + tee -a "$LOGFILE" + else + # Log only to the logfile + cat >> "$LOGFILE" + fi +} + +trace_on() { + if [[ $DEBUG -eq 1 ]] || [[ $DEBUG -ge 3 ]]; then + set -o xtrace + fi +} + +trace_off() { + if [[ $DEBUG -eq 1 ]] || [[ $DEBUG -ge 3 ]]; then + set +o xtrace + fi +} + +save_env() { + export -p | grep -wv -e 'OLDPWD=' -e 'PWD=' > "$ENVFILE" +} + +verify_patch_files() { + local path + local dir + local ret=0 + + for patch in "${PATCH_LIST[@]}"; do + for path in $(lsdiff "$patch" 2>/dev/null); do + + dir=$(dirname "$path") + ext="${path##*.}" + + if [[ "$dir" =~ ^lib$ ]] || [[ "$dir" =~ ^lib/ ]] ; then + warn "$patch: unsupported patch to lib/: $path" + ret=1 + fi + + if [[ "$ext" == "S" ]] ; then + warn "$patch: unsupported patch to assembly: $path" + ret=1 + fi + + done + done + + [[ $ret == 1 ]] && die "Unsupported changes detected" +} + +apply_patches() { + local patch + + for patch in "${PATCH_LIST[@]}"; do + patch -N -p1 --dry-run < "$patch" 2>&1 | logger || die "$patch file failed to apply" + patch -N -p1 < "$patch" 2>&1 | logger || die "$patch file failed to apply" + (( APPLIED_PATCHES++ )) + done +} + +remove_patches() { + local patch + local idx + + for (( ; APPLIED_PATCHES>0; APPLIED_PATCHES-- )); do + idx=$(( APPLIED_PATCHES - 1)) + patch="${PATCH_LIST[$idx]}" + patch -p1 -R -d "$BUILDDIR" < "$patch" &> /dev/null + done + + # If $BUILDDIR was a git repo, make sure git actually sees that + # we've reverted our patch(es). + [[ -d "$BUILDDIR/.git" ]] && (cd "$BUILDDIR" && git update-index -q --refresh) +} + +cleanup() { + rm -f "$BUILDDIR/.scmversion" + + remove_patches + + # restore original vmlinux if it was overwritten by sourcedir build + [[ -e "$TEMPDIR/vmlinux" ]] && mv -f "$TEMPDIR/vmlinux" "$KERNEL_SRCDIR/" + + # restore original link-vmlinux.sh if we updated it for the build + [[ -e "$TEMPDIR/link-vmlinux.sh" ]] && mv -f "$TEMPDIR/link-vmlinux.sh" "$KERNEL_SRCDIR/scripts" + + # restore original Makefile.modfinal if we updated it for the build + [[ -e "$TEMPDIR/Makefile.modfinal" ]] && mv -f "$TEMPDIR/Makefile.modfinal" "$KERNEL_SRCDIR/scripts" + + [[ "$DEBUG" -eq 0 ]] && rm -rf "$TEMPDIR" + rm -rf "$RPMTOPDIR" + unset KCFLAGS + unset KCPPFLAGS +} + +clean_cache() { + rm -rf "${CACHEDIR:?}"/* + mkdir -p "$TEMPDIR" || die "Couldn't create $TEMPDIR" +} + +check_pipe_status() { + rc="${PIPESTATUS[0]}" + if [[ "$rc" = 139 ]]; then + # There doesn't seem to be a consistent/portable way of + # accessing the last executed command in bash, so just + # pass in the script name for now.. + warn "$1 SIGSEGV" + if ls core* &> /dev/null; then + cp core* /tmp + die "core file at /tmp/$(ls core*)" + fi + die "There was a SIGSEGV, but no core dump was found in the current directory. Depending on your distro you might find it in /var/lib/systemd/coredump or /var/crash." + fi +} + +kernel_version_gte() { + [ "${ARCHVERSION//-*/}" = "$(echo -e "${ARCHVERSION//-*}\\n$1" | sort -rV | head -n1)" ] +} + +kernel_is_rhel() { + [[ "$ARCHVERSION" =~ \.el[789] ]] +} + +rhel_kernel_version_gte() { + [ "${ARCHVERSION}" = "$(echo -e "${ARCHVERSION}\\n$1" | sort -rV | head -n1)" ] +} + +# klp.arch relocations were supported prior to v5.8 +# and prior to 4.18.0-284.el8 +use_klp_arch() +{ + if kernel_is_rhel; then + ! rhel_kernel_version_gte 4.18.0-284.el8 + else + ! kernel_version_gte 5.8.0 + fi +} + +support_klp_replace() +{ + if kernel_is_rhel; then + rhel_kernel_version_gte 4.18.0-193.el8 + else + kernel_version_gte 5.1.0 + fi +} + +find_dirs() { + if [[ -e "$SCRIPTDIR/create-diff-object" ]]; then + # git repo + TOOLSDIR="$SCRIPTDIR" + DATADIR="$(readlink -f "$SCRIPTDIR/../kmod")" + PLUGINDIR="$(readlink -f "$SCRIPTDIR/gcc-plugins")" + elif [[ -e "$SCRIPTDIR/../libexec/kpatch/create-diff-object" ]]; then + # installation path + TOOLSDIR="$(readlink -f "$SCRIPTDIR/../libexec/kpatch")" + DATADIR="$(readlink -f "$SCRIPTDIR/../share/kpatch")" + PLUGINDIR="$TOOLSDIR" + else + return 1 + fi +} + +find_core_symvers() { + SYMVERSFILE="" + if [[ -e "$SCRIPTDIR/create-diff-object" ]]; then + # git repo + SYMVERSFILE="$DATADIR/core/Module.symvers" + elif [[ -e "$SCRIPTDIR/../libexec/kpatch/create-diff-object" ]]; then + # installation path + if [[ -e "$SCRIPTDIR/../lib/kpatch/$ARCHVERSION/Module.symvers" ]]; then + SYMVERSFILE="$(readlink -f "$SCRIPTDIR/../lib/kpatch/$ARCHVERSION/Module.symvers")" + elif [[ -e /lib/modules/$ARCHVERSION/extra/kpatch/Module.symvers ]]; then + SYMVERSFILE="$(readlink -f "/lib/modules/$ARCHVERSION/extra/kpatch/Module.symvers")" + fi + fi + + [[ -e "$SYMVERSFILE" ]] +} + +gcc_version_from_file() { + "$READELF" -p .comment "$1" | grep -m 1 -o 'GCC:.*' +} + +gcc_version_check() { + local target="$1" + local c="$TEMPDIR/test.c" o="$TEMPDIR/test.o" + local out gccver kgccver + + # gcc --version varies between distributions therefore extract version + # by compiling a test file and compare it to vmlinux's version. + echo 'void main(void) {}' > "$c" + out="$("$GCC" -c -pg -ffunction-sections -o "$o" "$c" 2>&1)" + gccver="$(gcc_version_from_file "$o")" + kgccver="$(gcc_version_from_file "$target")" + + if [[ -n "$out" ]]; then + warn "gcc >= 4.8 required for -pg -ffunction-settings" + echo "gcc output: $out" + return 1 + fi + + out="$("$GCC" -c -gz=none -o "$o" "$c" 2>&1)" + if [[ -z "$out" ]]; then + DEBUG_KCFLAGS="-gz=none" + fi + rm -f "$c" "$o" + + # ensure gcc version matches that used to build the kernel + if [[ "$gccver" != "$kgccver" ]]; then + warn "gcc/kernel version mismatch" + echo "gcc version: $gccver" + echo "kernel version: $kgccver" + echo "Install the matching gcc version (recommended) or use --skip-compiler-check" + echo "to skip the version matching enforcement (not recommended)" + return 1 + fi + + return +} + +clang_version_from_file() { + "$READELF" -p .comment "$1" | grep -m 1 -Eo 'clang version [0-9.]+' +} + +clang_version_check() { + local target="$1" + local clangver kclangver + + clangver=$("$CLANG" --version | grep -m 1 -Eo 'clang version [0-9.]+') + kclangver="$(clang_version_from_file "$target")" + + # ensure clang version matches that used to build the kernel + if [[ "$clangver" != "$kclangver" ]]; then + warn "clang/kernel version mismatch" + echo "clang version: $clangver" + echo "kernel version: $kclangver" + echo "Install the matching clang version (recommended) or use --skip-compiler-check" + echo "to skip the version matching enforcement (not recommended)" + return 1 + fi + + return +} + +find_special_section_data() { + local -A check + + # Common features across all arches + check[b]=true # bug_entry + check[e]=true # exception_table_entry + + # Arch-specific features, without kernel CONFIG_ toggle + case "$ARCH" in + "x86_64") + check[a]=true # alt_instr + kernel_version_gte 5.10.0 && check[s]=true # static_call_site + ;; + "ppc64le") + check[f]=true # fixup_entry + ;; + "s390x") + check[a]=true # alt_instr + ;; + esac + + # Kernel CONFIG_ features + [[ -n "$CONFIG_PRINTK_INDEX" ]] && check[i]=true # pi_entry + [[ -n "$CONFIG_JUMP_LABEL" ]] && check[j]=true # jump_entry + [[ -n "$CONFIG_UNWINDER_ORC" ]] && check[o]=true # orc_entry + [[ -n "$CONFIG_PARAVIRT" ]] && check[p]=true # paravirt_patch_site + + local c AWK_OPTIONS + for c in "${!check[@]}"; do + AWK_OPTIONS+=" -vcheck_${c}=1" + done + + local SPECIAL_VARS + # If $AWK_OPTIONS are blank gawk would treat "" as a blank script + # shellcheck disable=SC2086 + SPECIAL_VARS="$("$READELF" -wi "$VMLINUX" | + gawk --non-decimal-data $AWK_OPTIONS ' + BEGIN { a = b = e = f = i = j = o = p = s = 0 } + + # Set state if name matches + check_a && a == 0 && /DW_AT_name.* alt_instr[[:space:]]*$/ {a = 1; next} + check_b && b == 0 && /DW_AT_name.* bug_entry[[:space:]]*$/ {b = 1; next} + check_e && e == 0 && /DW_AT_name.* exception_table_entry[[:space:]]*$/ {e = 1; next} + check_f && f == 0 && /DW_AT_name.* fixup_entry[[:space:]]*$/ {f = 1; next} + check_i && i == 0 && /DW_AT_name.* pi_entry[[:space:]]*$/ {i = 1; next} + check_j && j == 0 && /DW_AT_name.* jump_entry[[:space:]]*$/ {j = 1; next} + check_o && o == 0 && /DW_AT_name.* orc_entry[[:space:]]*$/ {o = 1; next} + check_p && p == 0 && /DW_AT_name.* paravirt_patch_site[[:space:]]*$/ {p = 1; next} + check_s && s == 0 && /DW_AT_name.* static_call_site[[:space:]]*$/ {s = 1; next} + + # Reset state unless this abbrev describes the struct size + a == 1 && !/DW_AT_byte_size/ { a = 0; next } + b == 1 && !/DW_AT_byte_size/ { b = 0; next } + e == 1 && !/DW_AT_byte_size/ { e = 0; next } + f == 1 && !/DW_AT_byte_size/ { f = 0; next } + i == 1 && !/DW_AT_byte_size/ { i = 0; next } + j == 1 && !/DW_AT_byte_size/ { j = 0; next } + o == 1 && !/DW_AT_byte_size/ { o = 0; next } + p == 1 && !/DW_AT_byte_size/ { p = 0; next } + s == 1 && !/DW_AT_byte_size/ { s = 0; next } + + # Now that we know the size, stop parsing for it + a == 1 {printf("export ALT_STRUCT_SIZE=%d\n", $4); a = 2} + b == 1 {printf("export BUG_STRUCT_SIZE=%d\n", $4); b = 2} + e == 1 {printf("export EX_STRUCT_SIZE=%d\n", $4); e = 2} + f == 1 {printf("export FIXUP_STRUCT_SIZE=%d\n", $4); f = 2} + i == 1 {printf("export PRINTK_INDEX_STRUCT_SIZE=%d\n", $4); i = 2} + j == 1 {printf("export JUMP_STRUCT_SIZE=%d\n", $4); j = 2} + o == 1 {printf("export ORC_STRUCT_SIZE=%d\n", $4); o = 2} + p == 1 {printf("export PARA_STRUCT_SIZE=%d\n", $4); p = 2} + s == 1 {printf("export STATIC_CALL_STRUCT_SIZE=%d\n", $4); s = 2} + + # Bail out once we have everything + (!check_a || a == 2) && + (!check_b || b == 2) && + (!check_e || e == 2) && + (!check_f || f == 2) && + (!check_i || i == 2) && + (!check_j || j == 2) && + (!check_o || o == 2) && + (!check_p || p == 2) && + (!check_s || s == 2) {exit}')" + + [[ -n "$SPECIAL_VARS" ]] && eval "$SPECIAL_VARS" + + [[ ${check[a]} && -z "$ALT_STRUCT_SIZE" ]] && die "can't find special struct alt_instr size" + [[ ${check[b]} && -z "$BUG_STRUCT_SIZE" ]] && die "can't find special struct bug_entry size" + [[ ${check[e]} && -z "$EX_STRUCT_SIZE" ]] && die "can't find special struct exception_table_entry size" + [[ ${check[f]} && -z "$FIXUP_STRUCT_SIZE" ]] && die "can't find special struct fixup_entry size" + [[ ${check[i]} && -z "$PRINTK_INDEX_STRUCT_SIZE" ]] && die "can't find special struct pi_entry size" + [[ ${check[j]} && -z "$JUMP_STRUCT_SIZE" ]] && die "can't find special struct jump_entry size" + [[ ${check[o]} && -z "$ORC_STRUCT_SIZE" ]] && die "can't find special struct orc_entry size" + [[ ${check[p]} && -z "$PARA_STRUCT_SIZE" ]] && die "can't find special struct paravirt_patch_site size" + [[ ${check[s]} && -z "$STATIC_CALL_STRUCT_SIZE" ]] && die "can't find special struct static_call_site size" + + save_env + return +} + +# path of file, relative to dir +# adapted from https://stackoverflow.com/a/24848739 +relpath() { + local file="$1" + local dir="$2" + + local filedir + local common + local result + + filedir="$(dirname "$(readlink -f "$file")")" + common="$(readlink -f "$dir")" + + if [[ "$filedir" = "$common" ]]; then + basename "$file" + return + fi + + while [[ "${filedir#$common/}" = "$filedir" ]]; do + common="$(dirname "$common")" + result="../$result" + done + + result="${result}${filedir#$common/}" + echo "${result}/$(basename "$file")" +} + +cmd_file_to_o_file() { + local parent="$1" + + # convert cmd file name to corresponding .o + parent_dir="$(dirname "$parent")" + parent_dir="${parent_dir#./}" + parent="$(basename "$parent")" + parent="${parent#.}" + parent="${parent%.cmd}" + parent="$parent_dir/$parent" + + [[ -f $parent ]] || die "can't find $parent associated with $1" + + echo "$parent" +} + +get_parent_from_parents() { + local parents=("$@") + + [[ ${#parents[@]} -eq 0 ]] && PARENT="" && return + [[ ${#parents[@]} -eq 1 ]] && PARENT="${parents[0]}" && return + + # multiple parents: + local parent + local mod_name="${parents[0]%.*}" + local mod_file + for parent in "${parents[@]}"; do + # for modules, there can be multiple matches. Some + # combination of foo.o, foo.mod, and foo.ko, depending + # on kernel version and whether the module is single or + # multi-object. Make sure a .mod and/or .ko exists, and no + # more than one .mod/.ko exists. + + [[ $parent = *.o ]] && continue + + if [[ ${parent%.*} != "$mod_name" ]]; then + mod_file="" + break + fi + + if [[ $parent = *.mod || $parent = *.ko ]]; then + mod_file=$parent + continue + fi + + mod_file="" + break + done + + if [[ -n $mod_file ]]; then + PARENT="$mod_file" + return + fi + + ERROR_IF_DIFF="multiple parent matches for $file: ${parents[*]}" + PARENT="${parents[0]}" +} + +__find_parent_obj_in_dir() { + local file="$1" + local dir="$2" + + declare -a parents + + while IFS='' read -r parent; do + parent="$(cmd_file_to_o_file "$parent")" + [[ $parent -ef $file ]] && continue + parents+=("$parent") + done < <(grep -El "[ ]${file/./\\.}([ \)]|$)" "$dir"/.*.cmd) + + get_parent_from_parents "${parents[@]}" +} + +find_parent_obj_in_dir() { + local file="$1" + local dir="$2" + + # make sure the dir has .cmd files + if ! compgen -G "$dir"/.*.cmd > /dev/null; then + PARENT="" + return + fi + + # 5.19+: ../acp/acp_hw.o + __find_parent_obj_in_dir "$(relpath "$file" "$dir")" "$dir" + [[ -n $PARENT ]] && return + + # pre-5.19 (and 5.19+ single-object modules): + if [[ $file == $dir* ]]; then + # arch/x86/kernel/smp.o + __find_parent_obj_in_dir "$file" "$dir" + else + # drivers/gpu/drm/amd/amdgpu/../acp/acp_hw.o + __find_parent_obj_in_dir "$dir"/"$(relpath "$file" "$dir")" "$dir" + fi +} + +find_parent_obj() { + local file="$1" + + # common case: look in same directory + find_parent_obj_in_dir "$file" "$(dirname "$file")" + [[ -n $PARENT ]] && return + + # if we previously had a successful deep find, try that dir first + if [[ -n "$LAST_DEEP_FIND_DIR" ]]; then + find_parent_obj_in_dir "$file" "$LAST_DEEP_FIND_DIR" + [[ -n "$PARENT" ]] && return + fi + + # prevent known deep finds + if [[ $file = drivers/gpu/drm/amd/* ]]; then + find_parent_obj_in_dir "$file" "drivers/gpu/drm/amd/amdgpu" + [[ -n "$PARENT" ]] && return + fi + if [[ $file = virt/kvm/* ]]; then + find_parent_obj_in_dir "$file" "arch/x86/kvm" + [[ -n "$PARENT" ]] && return + fi + if [[ $file = drivers/oprofile/* ]]; then + find_parent_obj_in_dir "$file" "arch/x86/oprofile" + [[ -n "$PARENT" ]] && return + fi + + # check higher-level dirs + local dir + dir="$(dirname "$file")" + while [[ ! $dir -ef . ]]; do + dir="$(dirname "$dir")" + find_parent_obj_in_dir "$file" "$dir" + [[ -n $PARENT ]] && return + done + + # slow path: search the entire tree ("deep find") + echo 'doing "deep find" for parent object' + declare -a parents + while IFS= read -r -d '' dir; do + find_parent_obj_in_dir "$file" "$dir" + if [[ -n $PARENT ]]; then + parents+=("$PARENT") + LAST_DEEP_FIND_DIR="$dir" + fi + done < <(find . -type d -print0) + + get_parent_from_parents "${parents[@]}" +} + +# find vmlinux or .ko associated with a .o file +find_kobj() { + local file="$1" + + if [[ -n $OOT_MODULE ]]; then + KOBJFILE="$OOT_MODULE" + return + fi + + KOBJFILE="$file" + ERROR_IF_DIFF= + while true; do + case "$KOBJFILE" in + *.mod) + KOBJFILE=${PARENT/.mod/.ko} + [[ -e $KOBJFILE ]] || die "can't find .ko for $PARENT" + return + ;; + *.ko) + return + ;; + */built-in.o|\ + */built-in.a|\ + arch/x86/kernel/ebda.o|\ + arch/x86/kernel/head*.o|\ + arch/x86/kernel/platform-quirks.o|\ + arch/x86/lib/lib.a|\ + lib/lib.a) + KOBJFILE=vmlinux + return + ;; + esac + + find_parent_obj "$KOBJFILE" + [[ -z "$PARENT" ]] && die "invalid ancestor $KOBJFILE for $file" + KOBJFILE="$PARENT" + done +} + +# Only allow alphanumerics and '_' and '-' in the module name. Everything else +# is replaced with '-'. Also truncate to 55 chars so the full name + NUL +# terminator fits in the kernel's 56-byte module name array. +module_name_string() { + echo "${1//[^a-zA-Z0-9_-]/-}" | cut -c 1-55 +} + +usage() { + echo "usage: $(basename "$0") [options] " >&2 + echo " patchN Input patchfile(s)" >&2 + echo " -h, --help Show this help message" >&2 + echo " -a, --archversion Specify the kernel arch version" >&2 + echo " -r, --sourcerpm Specify kernel source RPM" >&2 + echo " -s, --sourcedir Specify kernel source directory" >&2 + echo " -c, --config Specify kernel config file" >&2 + echo " -v, --vmlinux Specify original vmlinux" >&2 + echo " -j, --jobs Specify the number of make jobs" >&2 + echo " -t, --target Specify custom kernel build targets" >&2 + echo " -n, --name Specify the name of the kpatch module" >&2 + echo " -o, --output Specify output folder" >&2 + echo " -d, --debug Enable 'xtrace' and keep scratch files" >&2 + echo " in /tmp" >&2 + echo " (can be specified multiple times)" >&2 + echo " --oot-module Enable patching out-of-tree module," >&2 + echo " specify current version of module" >&2 + echo " --oot-module-src Specify out-of-tree module source directory" >&2 + echo " -R, --non-replace Disable replace patch (replace is on by default)" >&2 + echo " --skip-cleanup Skip post-build cleanup" >&2 + echo " --skip-compiler-check Skip compiler version matching check" >&2 + echo " (not recommended)" >&2 +} + +options="$(getopt -o ha:r:s:c:v:j:t:n:o:dR -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,output:,oot-module:,oot-module-src:,debug,skip-gcc-check,skip-compiler-check,skip-cleanup,non-replace" -- "$@")" || die "getopt failed" + +eval set -- "$options" + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -a|--archversion) + ARCHVERSION="$2" + shift + ;; + -r|--sourcerpm) + [[ ! -f "$2" ]] && die "source rpm '$2' not found" + SRCRPM="$(readlink -f "$2")" + shift + ;; + -s|--sourcedir) + [[ ! -d "$2" ]] && die "source dir '$2' not found" + USERSRCDIR="$(readlink -f "$2")" + shift + ;; + -c|--config) + [[ ! -f "$2" ]] && die "config file '$2' not found" + CONFIGFILE="$(readlink -f "$2")" + shift + ;; + -v|--vmlinux) + [[ ! -f "$2" ]] && die "vmlinux file '$2' not found" + VMLINUX="$(readlink -f "$2")" + shift + ;; + -j|--jobs) + [[ ! "$2" -gt 0 ]] && die "Invalid number of make jobs '$2'" + CPUS="$2" + shift + ;; + -t|--target) + TARGETS="$TARGETS $2" + shift + ;; + -n|--name) + MODNAME="$(module_name_string "$2")" + shift + ;; + -o|--output) + [[ ! -d "$2" ]] && die "output dir '$2' not found" + BASE="$(readlink -f "$2")" + shift + ;; + -d|--debug) + DEBUG=$((DEBUG + 1)) + if [[ $DEBUG -eq 1 ]]; then + echo "DEBUG mode enabled" + fi + ;; + --oot-module) + [[ ! -f "$2" ]] && die "out-of-tree module '$2' not found" + OOT_MODULE="$(readlink -f "$2")" + shift + ;; + --oot-module-src) + [[ ! -d "$2" ]] && die "out-of-tree module source dir '$2' not found" + OOT_MODULE_SRCDIR="$(readlink -f "$2")" + shift + ;; + -R|--non-replace) + KLP_REPLACE=0 + ;; + --skip-cleanup) + echo "Skipping cleanup" + SKIPCLEANUP=1 + ;; + --skip-gcc-check) + echo "DEPRECATED: --skip-gcc-check is deprecated, use --skip-compiler-check instead" + ;& + --skip-compiler-check) + echo "WARNING: Skipping compiler version matching check (not recommended)" + SKIPCOMPILERCHECK=1 + ;; + *) + [[ "$1" = "--" ]] && shift && continue + [[ ! -f "$1" ]] && die "patch file '$1' not found" + PATCH_LIST+=("$(readlink -f "$1")") + ;; + esac + shift +done + +if [[ ${#PATCH_LIST[@]} -eq 0 ]]; then + warn "no patch file(s) specified" + usage + exit 1 +fi + +trace_on + +if [[ -n "$SRCRPM" ]]; then + if [[ -n "$ARCHVERSION" ]]; then + warn "--archversion is incompatible with --sourcerpm" + exit 1 + fi + rpmname="$(basename "$SRCRPM")" + ARCHVERSION="${rpmname%.src.rpm}.$(uname -m)" + ARCHVERSION="${ARCHVERSION#kernel-}" + ARCHVERSION="${ARCHVERSION#alt-}" +fi + +if [[ -n "$OOT_MODULE" ]] && [[ -z "$OOT_MODULE_SRCDIR" ]]; then + warn "--oot-module requires --oot-module-src" + exit 1 +fi + +# ensure cachedir and tempdir are setup properly and cleaned +mkdir -p "$TEMPDIR" || die "Couldn't create $TEMPDIR" +rm -rf "${TEMPDIR:?}"/* +rm -f "$LOGFILE" + +if [[ -n "$USERSRCDIR" ]]; then + KERNEL_SRCDIR="$USERSRCDIR" + + [[ -z "$VMLINUX" ]] && VMLINUX="$KERNEL_SRCDIR"/vmlinux + [[ ! -e "$VMLINUX" ]] && die "can't find vmlinux" + + # Extract the target kernel version from vmlinux in this case. + VMLINUX_VER="$(strings "$VMLINUX" | grep -m 1 -e "^Linux version" | awk '{ print($3); }')" + if [[ -n "$ARCHVERSION" ]]; then + if [[ -n "$VMLINUX_VER" ]] && [[ "$ARCHVERSION" != "$VMLINUX_VER" ]]; then + die "Kernel version mismatch: $ARCHVERSION was specified but vmlinux was built for $VMLINUX_VER" + fi + else + if [[ -z "$VMLINUX_VER" ]]; then + die "Unable to determine the kernel version from vmlinux" + fi + ARCHVERSION="$VMLINUX_VER" + fi +fi + +if [[ -n "$OOT_MODULE" ]]; then + ARCHVERSION="$(modinfo -F vermagic "$OOT_MODULE" | awk '{print $1}')" +fi + +[[ -z "$ARCHVERSION" ]] && ARCHVERSION="$(uname -r)" + +if [[ -n "$OOT_MODULE" ]]; then + if [[ -z "$USERSRCDIR" ]]; then + KERNEL_SRCDIR="/lib/modules/$ARCHVERSION/build/" + fi + BUILDDIR="$OOT_MODULE_SRCDIR" +else + BUILDDIR="$KERNEL_SRCDIR" +fi + +[[ "$SKIPCLEANUP" -eq 0 ]] && trap cleanup EXIT INT TERM HUP + +KVER="${ARCHVERSION%%-*}" +if [[ "$ARCHVERSION" =~ - ]]; then + KREL="${ARCHVERSION##*-}" + KREL="${KREL%.*}" +fi +[[ "$ARCHVERSION" =~ .el7a. ]] && ALT="-alt" + +[[ -z "$TARGETS" ]] && TARGETS="vmlinux modules" + +# Don't check external file. +# shellcheck disable=SC1090 +if [[ -z "$USERSRCDIR" ]] && [[ -f "$RELEASE_FILE" ]]; then + source "$RELEASE_FILE" + DISTRO="$ID" +fi + +if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || [[ "$DISTRO" = centos ]] || [[ "$DISTRO" = openEuler ]]; then + [[ -z "$VMLINUX" ]] && VMLINUX="/usr/lib/debug/lib/modules/$ARCHVERSION/vmlinux" + [[ -e "$VMLINUX" ]] || die "kernel-debuginfo-$ARCHVERSION not installed" + + export PATH="/usr/lib64/ccache:$PATH" + +elif [[ "$DISTRO" = ubuntu ]] || [[ "$DISTRO" = debian ]]; then + [[ -z "$VMLINUX" ]] && VMLINUX="/usr/lib/debug/boot/vmlinux-$ARCHVERSION" + + if [[ "$DISTRO" = ubuntu ]]; then + [[ -e "$VMLINUX" ]] || die "linux-image-$ARCHVERSION-dbgsym not installed" + + elif [[ "$DISTRO" = debian ]]; then + [[ -e "$VMLINUX" ]] || die "linux-image-$ARCHVERSION-dbg not installed" + fi + + export PATH="/usr/lib/ccache:$PATH" +fi +save_env + +find_dirs || die "can't find supporting tools" + +if [[ -n "$USERSRCDIR" ]]; then + echo "Using source directory at $USERSRCDIR" + + # save original vmlinux before it gets overwritten by sourcedir build + if [[ "$VMLINUX" -ef "$KERNEL_SRCDIR"/vmlinux ]]; then + cp -f "$VMLINUX" "$TEMPDIR/vmlinux" || die + VMLINUX="$TEMPDIR/vmlinux" + fi +elif [[ -n "$OOT_MODULE" ]]; then + if [[ -z "${CONFIGFILE}" ]]; then + CONFIGFILE="/boot/config-${ARCHVERSION}" + fi +elif [[ -e "$KERNEL_SRCDIR"/.config ]] && [[ -e "$VERSIONFILE" ]] && [[ "$(cat "$VERSIONFILE")" = "$ARCHVERSION" ]]; then + echo "Using cache at $KERNEL_SRCDIR" + +else + if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || [[ "$DISTRO" = centos ]] || [[ "$DISTRO" = openEuler ]]; then + + [[ "$DISTRO" = fedora ]] && echo "Fedora distribution detected" + [[ "$DISTRO" = rhel ]] && echo "RHEL distribution detected" + [[ "$DISTRO" = ol ]] && echo "Oracle Linux distribution detected" + [[ "$DISTRO" = centos ]] && echo "CentOS distribution detected" + [[ "$DISTRO" = openEuler ]] && echo "OpenEuler distribution detected" + + clean_cache + + echo "Downloading kernel source for $ARCHVERSION" + if [[ -z "$SRCRPM" ]]; then + if [[ "$DISTRO" = fedora ]]; then + wget -P "$TEMPDIR" "http://kojipkgs.fedoraproject.org/packages/kernel/$KVER/$KREL/src/kernel-$KVER-$KREL.src.rpm" 2>&1 | logger || die + else + command -v yumdownloader &>/dev/null || die "yumdownloader (yum-utils or dnf-utils) not installed" + yumdownloader --source --destdir "$TEMPDIR" "kernel$ALT-$KVER-$KREL" 2>&1 | logger || die + fi + SRCRPM="$TEMPDIR/kernel$ALT-$KVER-$KREL.src.rpm" + fi + + echo "Unpacking kernel source" + + rpm -D "_topdir $RPMTOPDIR" -ivh "$SRCRPM" 2>&1 | logger || die + rpmbuild -D "_topdir $RPMTOPDIR" -bp --nodeps "--target=$(uname -m)" "$RPMTOPDIR"/SPECS/kernel$ALT.spec 2>&1 | logger || + die "rpmbuild -bp failed. you may need to run 'yum-builddep kernel' first." + + if [[ "$DISTRO" = openEuler ]]; then + # openEuler has two directories with the same content after 'rpm -D' + # openEuler 21.09 has linux-* and linux-*-source while openEuler 20.03 has linux-* and linux-*-Source + mv "$RPMTOPDIR"/BUILD/kernel-*/linux-*[sS]ource "$KERNEL_SRCDIR" 2>&1 | logger || die + else + mv "$RPMTOPDIR"/BUILD/kernel-*/linux-* "$KERNEL_SRCDIR" 2>&1 | logger || die + fi + rm -rf "$RPMTOPDIR" + rm -rf "$KERNEL_SRCDIR/.git" + + if [[ "$ARCHVERSION" == *-* ]]; then + sed -i "s/^EXTRAVERSION.*/EXTRAVERSION = -${ARCHVERSION##*-}/" "$KERNEL_SRCDIR/Makefile" || die + fi + + echo "$ARCHVERSION" > "$VERSIONFILE" || die + + if [[ "$DISTRO" = openEuler ]]; then + [[ -z "$CONFIGFILE" ]] && CONFIGFILE="/boot/config-${ARCHVERSION}" + else + [[ -z "$CONFIGFILE" ]] && CONFIGFILE="$KERNEL_SRCDIR/configs/kernel$ALT-$KVER-$ARCH.config" + fi + + (cd "$KERNEL_SRCDIR" && make mrproper 2>&1 | logger) || die + + elif [[ "$DISTRO" = ubuntu ]] || [[ "$DISTRO" = debian ]]; then + + echo "Debian/Ubuntu distribution detected" + + if [[ "$DISTRO" = ubuntu ]]; then + + # url may be changed for a different mirror + url="http://archive.ubuntu.com/ubuntu/pool/main/l" + sublevel="SUBLEVEL = 0" + + elif [[ "$DISTRO" = debian ]]; then + + # url may be changed for a different mirror + url="http://ftp.debian.org/debian/pool/main/l" + sublevel="SUBLEVEL =" + fi + + pkgname="$(dpkg-query -W -f='${Source}' "linux-image-$ARCHVERSION" | sed s/-signed//)" + pkgver="$(dpkg-query -W -f='${Version}' "linux-image-$ARCHVERSION")" + dscname="${pkgname}_${pkgver}.dsc" + + clean_cache + + cd "$TEMPDIR" || die + echo "Downloading and unpacking the kernel source for $ARCHVERSION" + # Download source deb pkg + (dget -u "$url/${pkgname}/${dscname}" 2>&1) | logger || die "dget: Could not fetch/unpack $url/${pkgname}/${dscname}" + mv "${pkgname}-$KVER" "$KERNEL_SRCDIR" || die + [[ -z "$CONFIGFILE" ]] && CONFIGFILE="/boot/config-${ARCHVERSION}" + if [[ "$ARCHVERSION" == *-* ]]; then + echo "-${ARCHVERSION#*-}" > "$KERNEL_SRCDIR/localversion" || die + fi + # for some reason the Ubuntu kernel versions don't follow the + # upstream SUBLEVEL; they are always at SUBLEVEL 0 + sed -i "s/^SUBLEVEL.*/${sublevel}/" "$KERNEL_SRCDIR/Makefile" || die + echo "$ARCHVERSION" > "$VERSIONFILE" || die + + else + die "Unsupported distribution" + fi +fi + +[[ -z "$CONFIGFILE" ]] && CONFIGFILE="$KERNEL_SRCDIR"/.config +[[ ! -e "$CONFIGFILE" ]] && die "can't find config file" +if [[ -z "$OOT_MODULE" && ! "$CONFIGFILE" -ef "$KERNEL_SRCDIR"/.config ]] ; then + cp -f "$CONFIGFILE" "$KERNEL_SRCDIR/.config" || die +fi + +# kernel option checking + +trace_off "reading .config" +# Don't check external file. +# shellcheck disable=SC1090 +source "$CONFIGFILE" +trace_on + +[[ "$DISTRO" = openEuler ]] && [[ -z "$CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY" ]] && \ + die "openEuler kernel doesn't have 'CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY' enabled" + +[[ -z "$CONFIG_DEBUG_INFO" ]] && die "kernel doesn't have 'CONFIG_DEBUG_INFO' enabled" +[[ "$ARCH" = "s390x" ]] && [[ -z "$CONFIG_EXPOLINE_EXTERN" ]] && [[ -n "$CONFIG_EXPOLINE" ]] && die "kernel doesn't have 'CONFIG_EXPOLINE_EXTERN' enabled" + +# Build variables - Set some defaults, then adjust features +# according to .config and kernel version +KPATCH_LDFLAGS="" +USE_KLP=0 +USE_KLP_ARCH=0 + +if [[ -n "$CONFIG_LIVEPATCH" ]] && (kernel_is_rhel || kernel_version_gte 4.9.0); then + + USE_KLP=1 + + if use_klp_arch; then + USE_KLP_ARCH=1 + KPATCH_LDFLAGS="--unique=.parainstructions --unique=.altinstructions" + CDO_FLAGS="--klp-arch" + fi + + if [[ "$KLP_REPLACE" -eq 1 ]] ; then + support_klp_replace || die "The kernel doesn't support klp replace" + else + export CFLAGS_MODULE="$CFLAGS_MODULE -DKLP_REPLACE_ENABLE=false" + fi +else + # No support for livepatch in the kernel. Kpatch core module is needed. + + # There may be ordering bugs, with jump labels and other special + # sections. Use with caution! + echo "WARNING: Use of kpatch core module (kpatch.ko) is deprecated! There may be bugs!" >&2 + + find_core_symvers || die "unable to find Module.symvers for kpatch core module" + KBUILD_EXTRA_SYMBOLS="$SYMVERSFILE" +fi + +# unsupported kernel option checking +[[ -n "$CONFIG_DEBUG_INFO_SPLIT" ]] && die "kernel option 'CONFIG_DEBUG_INFO_SPLIT' not supported" +[[ -n "$CONFIG_GCC_PLUGIN_LATENT_ENTROPY" ]] && die "kernel option 'CONFIG_GCC_PLUGIN_LATENT_ENTROPY' not supported" +[[ -n "$CONFIG_GCC_PLUGIN_RANDSTRUCT" ]] && die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported" + +# CONFIG_DEBUG_INFO_BTF invokes pahole, for which some versions don't +# support extended ELF sections. Disable the BTF typeinfo generation in +# link-vmlinux.sh and Makefile.modfinal since kpatch doesn't care about +# that anyway. +if [[ -n "$CONFIG_DEBUG_INFO_BTF" ]]; then + cp -f "$KERNEL_SRCDIR/scripts/link-vmlinux.sh" "$TEMPDIR/link-vmlinux.sh" || die + sed -i 's/CONFIG_DEBUG_INFO_BTF/DISABLED_FOR_KPATCH_BUILD/g' "$KERNEL_SRCDIR"/scripts/link-vmlinux.sh || die + + if [[ -e "$KERNEL_SRCDIR/scripts/Makefile.modfinal" ]]; then + cp -f "$KERNEL_SRCDIR/scripts/Makefile.modfinal" "$TEMPDIR/Makefile.modfinal" || die + sed -i 's/CONFIG_DEBUG_INFO_BTF_MODULES/DISABLED_FOR_KPATCH_BUILD/g' "$KERNEL_SRCDIR"/scripts/Makefile.modfinal || die + fi +fi + +if [[ -n "$CONFIG_CC_IS_CLANG" ]]; then + echo "WARNING: Clang support is experimental" +fi + +if [[ "$SKIPCOMPILERCHECK" -eq 0 ]]; then + if [[ -n "$OOT_MODULE" ]]; then + target="$OOT_MODULE" + else + target="$VMLINUX" + fi + if [[ -n "$CONFIG_CC_IS_CLANG" ]]; then + clang_version_check "$target" || die + else + gcc_version_check "$target" || die + fi +fi + +echo "Testing patch file(s)" +cd "$BUILDDIR" || die +verify_patch_files +apply_patches +remove_patches + +cp -LR "$DATADIR/patch" "$TEMPDIR" || die + +if [[ "$ARCH" = "ppc64le" ]]; then + ARCH_KCFLAGS="-mcmodel=large -fplugin=$PLUGINDIR/ppc64le-plugin.so" +fi + +if [[ "$ARCH" = "s390x" ]]; then + ARCH_KCFLAGS="-mno-pic-data-is-text-relative -fno-section-anchors" +fi + +export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections \ + $ARCH_KCFLAGS $DEBUG_KCFLAGS" + +echo "Reading special section data" +find_special_section_data + +if [[ $DEBUG -ge 4 ]]; then + export KPATCH_GCC_DEBUG=1 +fi +save_env + +echo "Building original source" +[[ -n "$OOT_MODULE" ]] || ./scripts/setlocalversion --save-scmversion || die +unset KPATCH_GCC_TEMPDIR + +KPATCH_CC_PREFIX="$TOOLSDIR/kpatch-cc " +declare -a MAKEVARS +if [[ -n "$CONFIG_CC_IS_CLANG" ]]; then + MAKEVARS+=("CC=${KPATCH_CC_PREFIX}${CLANG}") + MAKEVARS+=("HOSTCC=clang") +else + MAKEVARS+=("CC=${KPATCH_CC_PREFIX}${GCC}") +fi + +if [[ -n "$CONFIG_LD_IS_LLD" ]]; then + MAKEVARS+=("LD=${KPATCH_CC_PREFIX}${LLD}") + MAKEVARS+=("HOSTLD=ld.lld") +else + MAKEVARS+=("LD=${KPATCH_CC_PREFIX}${LD}") +fi + + +# $TARGETS used as list, no quotes. +# shellcheck disable=SC2086 +make "${MAKEVARS[@]}" "-j$CPUS" $TARGETS 2>&1 | logger || die + +# Save original module symvers +cp -f "$BUILDDIR/Module.symvers" "$TEMPDIR/Module.symvers" || die + +echo "Building patched source" +apply_patches +mkdir -p "$TEMPDIR/orig" "$TEMPDIR/patched" +export KPATCH_GCC_TEMPDIR="$TEMPDIR" +export KPATCH_GCC_SRCDIR="$BUILDDIR" +save_env +# $TARGETS used as list, no quotes. +# shellcheck disable=SC2086 +KBUILD_MODPOST_WARN=1 make "${MAKEVARS[@]}" "-j$CPUS" $TARGETS 2>&1 | logger || die + +# source.c:(.section+0xFF): undefined reference to `symbol' +grep "undefined reference" "$LOGFILE" | sed -r "s/^.*\`(.*)'$/\\1/" \ + >"${TEMPDIR}"/undefined_references + +# WARNING: "symbol" [path/to/module.ko] undefined! +grep "undefined!" "$LOGFILE" | cut -d\" -f2 >>"${TEMPDIR}"/undefined_references + +if [[ ! -e "$TEMPDIR/changed_objs" ]]; then + die "no changed objects found" +fi + +grep -q vmlinux "$KERNEL_SRCDIR/Module.symvers" || die "truncated $KERNEL_SRCDIR/Module.symvers file" + +if [[ -n "$CONFIG_MODVERSIONS" ]]; then + trace_off "reading Module.symvers" + while read -ra sym_line; do + if [[ ${#sym_line[@]} -lt 4 ]]; then + die "Malformed ${TEMPDIR}/Module.symvers file" + fi + + sym=${sym_line[1]} + + read -ra patched_sym_line <<< "$(grep "\s$sym\s" "$BUILDDIR/Module.symvers")" + if [[ ${#patched_sym_line[@]} -lt 4 ]]; then + die "Malformed symbol entry for ${sym} in ${BUILDDIR}/Module.symvers file" + fi + + # Assume that both original and patched symvers have the same format. + # In both cases, the symbol should have the same CRC, belong to the same + # Module/Namespace and have the same export type. + if [[ ${#sym_line[@]} -ne ${#patched_sym_line[@]} || \ + "${sym_line[*]}" != "${patched_sym_line[*]}" ]]; then + warn "Version disagreement for symbol ${sym}" + fi + done < "${TEMPDIR}/Module.symvers" + trace_on +fi + +# Read as words, no quotes. +# shellcheck disable=SC2013 +for i in $(cat "$TEMPDIR/changed_objs") +do + mkdir -p "$TEMPDIR/patched/$(dirname "$i")" || die + cp -f "$BUILDDIR/$i" "$TEMPDIR/patched/$i" || die +done + +echo "Extracting new and modified ELF sections" +# If no kpatch module name was provided on the command line: +# - For single input .patch, use the patch filename +# - For multiple input .patches, use "patch" +# - Prefix with "kpatch" or "livepatch" accordingly +if [[ -z "$MODNAME" ]] ; then + if [[ "${#PATCH_LIST[@]}" -eq 1 ]]; then + MODNAME="$(basename "${PATCH_LIST[0]}")" + if [[ "$MODNAME" =~ \.patch$ ]] || [[ "$MODNAME" =~ \.diff$ ]]; then + MODNAME="${MODNAME%.*}" + fi + else + MODNAME="patch" + fi + + if [[ "$USE_KLP" -eq 1 ]]; then + MODNAME="livepatch-$MODNAME" + else + MODNAME="kpatch-$MODNAME" + fi + + MODNAME="$(module_name_string "$MODNAME")" +fi +FILES="$(cat "$TEMPDIR/changed_objs")" +cd "$TEMPDIR" || die +mkdir output +declare -a objnames +CHANGED=0 +ERROR=0 + +# Prepare OOT module symvers file +if [[ -n "$OOT_MODULE" ]]; then + cp -f "$OOT_MODULE_SRCDIR/Module.symvers" "$TEMPDIR/Module.symvers" || die + awk '{ print $1 "\t" $2 "\t" $3 "\t" $4}' "${KERNEL_SRCDIR}/Module.symvers" >> "$TEMPDIR/Module.symvers" +fi + +for i in $FILES; do + # In RHEL 7 based kernels, copy_user_64.o misuses the .fixup section, + # which confuses create-diff-object. It's fine to skip it, it's an + # assembly file anyway. + [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = centos ]] || [[ "$DISTRO" = ol ]] && \ + [[ "$i" = arch/x86/lib/copy_user_64.o ]] && continue + + [[ "$i" = usr/initramfs_data.o ]] && continue + + mkdir -p "output/$(dirname "$i")" + cd "$BUILDDIR" || die + find_kobj "$i" + cd "$TEMPDIR" || die + if [[ -e "orig/$i" ]]; then + if [[ -n $OOT_MODULE ]]; then + KOBJFILE_NAME="$(basename --suffix=.ko "$OOT_MODULE")" + KOBJFILE_NAME="${KOBJFILE_NAME//-/_}" + KOBJFILE_PATH="$OOT_MODULE" + SYMTAB="${TEMPDIR}/module/${KOBJFILE_NAME}.symtab" + SYMVERS_FILE="$TEMPDIR/Module.symvers" + elif [[ "$(basename "$KOBJFILE")" = vmlinux ]]; then + KOBJFILE_NAME=vmlinux + KOBJFILE_PATH="$VMLINUX" + SYMTAB="${TEMPDIR}/${KOBJFILE_NAME}.symtab" + SYMVERS_FILE="$BUILDDIR/Module.symvers" + else + KOBJFILE_NAME=$(basename "${KOBJFILE%.ko}") + KOBJFILE_NAME="${KOBJFILE_NAME//-/_}" + KOBJFILE_PATH="${TEMPDIR}/module/$KOBJFILE" + SYMTAB="${KOBJFILE_PATH}.symtab" + SYMVERS_FILE="$BUILDDIR/Module.symvers" + fi + + "$READELF" -s --wide "$KOBJFILE_PATH" > "$SYMTAB" + if [[ "$ARCH" = "ppc64le" ]]; then + sed -ri 's/\s+\[: 8\]//' "$SYMTAB" + fi + + # create-diff-object orig.o patched.o parent-name parent-symtab + # Module.symvers patch-mod-name output.o + "$TOOLSDIR"/create-diff-object $CDO_FLAGS "orig/$i" "patched/$i" "$KOBJFILE_NAME" \ + "$SYMTAB" "$SYMVERS_FILE" "${MODNAME//-/_}" \ + "output/$i" 2>&1 | logger 1 + check_pipe_status create-diff-object + # create-diff-object returns 3 if no functional change is found + [[ "$rc" -eq 0 ]] || [[ "$rc" -eq 3 ]] || ERROR="$((ERROR + 1))" + if [[ "$rc" -eq 0 ]]; then + [[ -n "$ERROR_IF_DIFF" ]] && die "$ERROR_IF_DIFF" + CHANGED=1 + objnames[${#objnames[@]}]="$KOBJFILE" + fi + else + cp -f "patched/$i" "output/$i" || die + objnames[${#objnames[@]}]="$KOBJFILE" + fi +done + +if [[ "$ERROR" -ne 0 ]]; then + die "$ERROR error(s) encountered" +fi + +if [[ "$CHANGED" -eq 0 ]]; then + die "no functional changes found" +fi + +echo -n "Patched objects:" +for i in $(echo "${objnames[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ') +do + echo -n " $i" +done +echo + +export KCFLAGS="-I$DATADIR/patch $ARCH_KCFLAGS" +if [[ "$USE_KLP" -eq 0 ]]; then + export KCPPFLAGS="-D__KPATCH_MODULE__" +fi +save_env + +echo "Building patch module: $MODNAME.ko" + +if [[ -z "$USERSRCDIR" ]] && [[ "$DISTRO" = ubuntu ]]; then + # UBUNTU: add UTS_UBUNTU_RELEASE_ABI to utsrelease.h after regenerating it + UBUNTU_ABI="${ARCHVERSION#*-}" + UBUNTU_ABI="${UBUNTU_ABI%-*}" + echo "#define UTS_UBUNTU_RELEASE_ABI $UBUNTU_ABI" >> "$KERNEL_SRCDIR"/include/generated/utsrelease.h +fi + +cd "$TEMPDIR/output" || die +# $KPATCH_LDFLAGS and result of find used as list, no quotes. +# shellcheck disable=SC2086,SC2046 +"$LD" -r $KPATCH_LDFLAGS -o ../patch/tmp_output.o $(find . -name "*.o") 2>&1 | logger || die + +if [[ "$USE_KLP" -eq 1 ]]; then + cp -f "$TEMPDIR"/patch/tmp_output.o "$TEMPDIR"/patch/output.o || die + # Avoid MODPOST warning (pre-v5.8) and error (v5.8+) with an empty .cmd file + touch "$TEMPDIR"/patch/.output.o.cmd || die +else + # Add .kpatch.checksum for kpatch script + md5sum ../patch/tmp_output.o | awk '{printf "%s\0", $1}' > checksum.tmp || die + "$OBJCOPY" --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/tmp_output.o || die + rm -f checksum.tmp + "$TOOLSDIR"/create-kpatch-module "$TEMPDIR"/patch/tmp_output.o "$TEMPDIR"/patch/output.o 2>&1 | logger 1 + check_pipe_status create-kpatch-module + [[ "$rc" -ne 0 ]] && die "create-kpatch-module: exited with return code: $rc" +fi + +cd "$TEMPDIR/patch" || die + +# We no longer need kpatch-cc +for ((idx=0; idx<${#MAKEVARS[@]}; idx++)); do + MAKEVARS[$idx]=${MAKEVARS[$idx]/${KPATCH_CC_PREFIX}/} +done + +export KPATCH_BUILD="$KERNEL_SRCDIR" KPATCH_NAME="$MODNAME" \ +KBUILD_EXTRA_SYMBOLS="$KBUILD_EXTRA_SYMBOLS" \ +KPATCH_LDFLAGS="$KPATCH_LDFLAGS" \ +CROSS_COMPILE="$CROSS_COMPILE" +save_env + +make "${MAKEVARS[@]}" 2>&1 | logger || die + +if [[ "$USE_KLP" -eq 1 ]]; then + if [[ "$USE_KLP_ARCH" -eq 0 ]]; then + extra_flags="--no-klp-arch-sections" + fi + cp -f "$TEMPDIR/patch/$MODNAME.ko" "$TEMPDIR/patch/tmp.ko" || die + "$TOOLSDIR"/create-klp-module $extra_flags "$TEMPDIR/patch/tmp.ko" "$TEMPDIR/patch/$MODNAME.ko" 2>&1 | logger 1 + check_pipe_status create-klp-module + [[ "$rc" -ne 0 ]] && die "create-klp-module: exited with return code: $rc" +fi + +if [[ -n "$CONFIG_MODVERSIONS" ]]; then + # Check that final module does not reference symbols with different version + # than the target kernel + KP_MOD_VALID=true + # shellcheck disable=SC2086 + while read -ra mod_symbol; do + if [[ ${#mod_symbol[@]} -lt 2 ]]; then + continue + fi + + # Check if the symbol exists in the old Module.symvers, and if it does + # check that the CRCs are unchanged. + if ! awk -v sym="${mod_symbol[1]}" -v crc="${mod_symbol[0]}" \ + '$2==sym && $1!=crc { exit 1 }' "$TEMPDIR/Module.symvers"; then + warn "Patch module references ${mod_symbol[1]} with invalid version" + KP_MOD_VALID=false + fi + done <<< "$(modprobe --dump-modversions $TEMPDIR/patch/$MODNAME.ko)" + if ! $KP_MOD_VALID; then + die "Patch module referencing altered exported kernel symbols cannot be loaded" + fi +fi + +"$READELF" --wide --symbols "$TEMPDIR/patch/$MODNAME.ko" 2>/dev/null | \ + sed -r 's/\s+\[: 8\]//' | \ + awk '($4=="FUNC" || $4=="OBJECT") && ($5=="GLOBAL" || $5=="WEAK") && $7!="UND" {print $NF}' \ + >"${TEMPDIR}"/new_symbols + +if [[ "$USE_KLP" -eq 0 ]]; then + cat >>"${TEMPDIR}"/new_symbols <<-EOF + kpatch_shadow_free + kpatch_shadow_alloc + kpatch_register + kpatch_shadow_get + kpatch_unregister + kpatch_root_kobj + EOF +fi + +# Compare undefined_references and new_symbols files and print only the first +# column containing lines unique to first file. +UNDEFINED=$(comm -23 <(sort -u "${TEMPDIR}"/undefined_references) \ + <(sort -u "${TEMPDIR}"/new_symbols) | tr '\n' ' ') +[[ -n "$UNDEFINED" ]] && die "Undefined symbols: $UNDEFINED" + +cp -f "$TEMPDIR/patch/$MODNAME.ko" "$BASE" || die + +[[ "$DEBUG" -eq 0 && "$SKIPCLEANUP" -eq 0 ]] && rm -f "$LOGFILE" + +echo "SUCCESS" diff --git a/kpatch-build/kpatch-cc b/kpatch-build/kpatch-cc new file mode 100755 index 0000000..d2bccdd --- /dev/null +++ b/kpatch-build/kpatch-cc @@ -0,0 +1,91 @@ +#!/bin/bash + +if [[ ${KPATCH_GCC_DEBUG:-0} -ne 0 ]]; then + set -o xtrace +fi + +TOOLCHAINCMD="$1" +shift + +if [[ -z "$KPATCH_GCC_TEMPDIR" ]]; then + exec "$TOOLCHAINCMD" "$@" +fi + +declare -a args=("$@") + +if [[ "$TOOLCHAINCMD" =~ ^(.*-)?gcc$ || "$TOOLCHAINCMD" =~ ^(.*-)?clang$ ]] ; then + while [ "$#" -gt 0 ]; do + if [ "$1" = "-o" ]; then + obj="$2" + + # skip copying the temporary .o files created by + # recordmcount.pl + [[ "$obj" = */.tmp_mc_*.o ]] && break; + + [[ "$obj" = */.tmp_*.o ]] && obj="${obj/.tmp_/}" + relobj=${obj##$KPATCH_GCC_SRCDIR/} + case "$relobj" in + *.mod.o|\ + *built-in.o|\ + *built-in.a|\ + vmlinux.o|\ + .tmp_kallsyms1.o|\ + .tmp_kallsyms2.o|\ + arch/x86/boot/*|\ + arch/x86/entry/vdso/*|\ + arch/x86/purgatory/*|\ + arch/x86/realmode/*|\ + arch/x86/tools/*|\ + arch/x86/vdso/*|\ + arch/powerpc/kernel/prom_init.o|\ + arch/powerpc/kernel/vdso64/*|\ + arch/s390/boot/*|\ + arch/s390/purgatory/*|\ + arch/s390/kernel/vdso64/*|\ + drivers/firmware/efi/libstub/*|\ + init/version.o|\ + kernel/system_certificates.o|\ + lib/*|\ + tools/*|\ + .*.o|\ + */.lib_exports.o) + break + ;; + *.o) + mkdir -p "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$relobj")" + [[ -e "$obj" ]] && cp -f "$obj" "$KPATCH_GCC_TEMPDIR/orig/$relobj" + echo "$relobj" >> "$KPATCH_GCC_TEMPDIR/changed_objs" + break + ;; + *) + break + ;; + esac + fi + shift + done +elif [[ "$TOOLCHAINCMD" =~ ^(.*-)?ld || "$TOOLCHAINCMD" =~ ^(.*-)?ld.lld ]] ; then + while [ "$#" -gt 0 ]; do + if [ "$1" = "-o" ]; then + obj="$2" + relobj=${obj//$KPATCH_GCC_SRCDIR\//} + case "$obj" in + *.ko) + mkdir -p "$KPATCH_GCC_TEMPDIR/module/$(dirname "$relobj")" + cp -f "$obj" "$KPATCH_GCC_TEMPDIR/module/$relobj" + break + ;; + .tmp_vmlinux*|vmlinux) + args+=(--warn-unresolved-symbols) + break + ;; + *) + break + ;; + esac + fi + shift + done +fi + +exec "$TOOLCHAINCMD" "${args[@]}" diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c new file mode 100644 index 0000000..c7d12ec --- /dev/null +++ b/kpatch-build/kpatch-elf.c @@ -0,0 +1,1041 @@ +/* + * kpatch-elf.c + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +/* + * This file provides a common api to create, inspect, and manipulate + * kpatch_elf objects. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asm/insn.h" +#include "kpatch-elf.h" + +/******************* + * Helper functions + ******************/ + +char *status_str(enum status status) +{ + switch(status) { + case NEW: + return "NEW"; + case CHANGED: + return "CHANGED"; + case SAME: + return "SAME"; + default: + ERROR("status_str"); + } + /* never reached */ + return NULL; +} + +bool is_rela_section(struct section *sec) +{ + return (sec->sh.sh_type == SHT_RELA); +} + +bool is_text_section(struct section *sec) +{ + return (sec->sh.sh_type == SHT_PROGBITS && + (sec->sh.sh_flags & SHF_EXECINSTR)); +} + +bool is_debug_section(struct section *sec) +{ + char *name; + if (is_rela_section(sec)) + name = sec->base->name; + else + name = sec->name; + + return !strncmp(name, ".debug_", 7) || + !strncmp(name, ".eh_frame", 9); +} + +struct section *find_section_by_index(struct list_head *list, unsigned int index) +{ + struct section *sec; + + list_for_each_entry(sec, list, list) + if (sec->index == index) + return sec; + + return NULL; +} + +struct section *find_section_by_name(struct list_head *list, const char *name) +{ + struct section *sec; + + list_for_each_entry(sec, list, list) + if (!strcmp(sec->name, name)) + return sec; + + return NULL; +} + +struct symbol *find_symbol_by_index(struct list_head *list, size_t index) +{ + struct symbol *sym; + + list_for_each_entry(sym, list, list) + if (sym->index == index) + return sym; + + return NULL; +} + +struct symbol *find_symbol_by_name(struct list_head *list, const char *name) +{ + struct symbol *sym; + + list_for_each_entry(sym, list, list) + if (sym->name && !strcmp(sym->name, name)) + return sym; + + return NULL; +} + +struct rela *find_rela_by_offset(struct section *relasec, unsigned int offset) +{ + struct rela *rela; + + list_for_each_entry(rela, &relasec->relas, list) { + if (rela->offset == offset) + return rela; + } + + return NULL; +} + +unsigned int absolute_rela_type(struct kpatch_elf *kelf) +{ + switch(kelf->arch) { + case PPC64: + return R_PPC64_ADDR64; + case X86_64: + return R_X86_64_64; + case S390: + return R_390_64; + default: + ERROR("unsupported arch"); + } + return 0; +} + +/* returns the offset of the string in the string table */ +int offset_of_string(struct list_head *list, char *name) +{ + struct string *string; + int index = 0; + + /* try to find string in the string list */ + list_for_each_entry(string, list, list) { + if (!strcmp(string->name, name)) + return index; + index += (int)strlen(string->name) + 1; + } + + /* allocate a new string */ + ALLOC_LINK(string, list); + string->name = name; + return index; +} + +static void rela_insn(const struct section *sec, const struct rela *rela, + struct insn *insn) +{ + unsigned long insn_addr, start, end, rela_addr; + + start = (unsigned long)sec->data->d_buf; + end = start + sec->sh.sh_size; + + if (end <= start) + ERROR("bad section size"); + + rela_addr = start + rela->offset; + for (insn_addr = start; insn_addr < end; insn_addr += insn->length) { + insn_init(insn, (void *)insn_addr, 1); + insn_get_length(insn); + if (!insn->length) + ERROR("can't decode instruction in section %s at offset 0x%lx", + sec->name, insn_addr); + if (rela_addr >= insn_addr && + rela_addr < insn_addr + insn->length) + return; + } + + ERROR("can't find instruction for rela at %s+0x%x", + sec->name, rela->offset); +} + +/* + * Return the addend, adjusted for any PC-relative relocation trickery, to + * point to the relevant symbol offset. + */ +long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec, + struct rela *rela) +{ + long add_off; + struct section *sec = relasec->base; + + switch(kelf->arch) { + case PPC64: + add_off = 0; + break; + case X86_64: + if (!is_text_section(sec) || + rela->type == R_X86_64_64 || + rela->type == R_X86_64_32S) + add_off = 0; + else if (rela->type == R_X86_64_PC32 || + rela->type == R_X86_64_PLT32 || + rela->type == R_X86_64_NONE) { + struct insn insn; + rela_insn(sec, rela, &insn); + add_off = (long)insn.next_byte - + (long)sec->data->d_buf - + rela->offset; + } else + ERROR("unhandled rela type %d", rela->type); + break; + case S390: + /* + * For branch and relative load instructions, + * add_off is -2. + */ + if (rela->type == R_390_GOTENT || + rela->type == R_390_PLT32DBL || + rela->type == R_390_PC32DBL) + add_off = -2; + else if (rela->type == R_390_32 || + rela->type == R_390_64 || + rela->type == R_390_PC32 || + rela->type == R_390_PC64) + add_off = 0; + else + ERROR("unhandled rela type %d", rela->type); + break; + default: + ERROR("unsupported arch\n"); + } + + return rela->addend + add_off; +} + +unsigned int insn_length(struct kpatch_elf *kelf, void *addr) +{ + struct insn decoded_insn; + char *insn = addr; + + switch(kelf->arch) { + + case X86_64: + insn_init(&decoded_insn, addr, 1); + insn_get_length(&decoded_insn); + return decoded_insn.length; + + case PPC64: + return 4; + + case S390: + switch(insn[0] >> 6) { + case 0: + return 2; + case 1: + case 2: + return 4; + case 3: + return 6; + } + + default: + ERROR("unsupported arch"); + } + + return 0; +} + +static void kpatch_create_rela_list(struct kpatch_elf *kelf, + struct section *relasec) +{ + int index = 0, skip = 0; + struct rela *rela; + unsigned int symndx; + unsigned long rela_nr; + + /* find matching base (text/data) section */ + relasec->base = find_section_by_index(&kelf->sections, relasec->sh.sh_info); + if (!relasec->base) + ERROR("can't find base section for rela section %s", relasec->name); + + /* create reverse link from base section to this rela section */ + relasec->base->rela = relasec; + + rela_nr = relasec->sh.sh_size / relasec->sh.sh_entsize; + + log_debug("\n=== rela list for %s (%ld entries) ===\n", + relasec->base->name, rela_nr); + + if (is_debug_section(relasec)) { + log_debug("skipping rela listing for .debug_* section\n"); + skip = 1; + } + + /* read and store the rela entries */ + while (rela_nr--) { + ALLOC_LINK(rela, &relasec->relas); + + if (!gelf_getrela(relasec->data, index, &rela->rela)) + ERROR("gelf_getrela"); + index++; + + rela->type = GELF_R_TYPE(rela->rela.r_info); + rela->addend = rela->rela.r_addend; + rela->offset = (unsigned int)rela->rela.r_offset; + symndx = (unsigned int)GELF_R_SYM(rela->rela.r_info); + rela->sym = find_symbol_by_index(&kelf->symbols, symndx); + if (!rela->sym) + ERROR("could not find rela entry symbol\n"); + if (rela->sym->sec && + (rela->sym->sec->sh.sh_flags & SHF_STRINGS)) { + rela->string = rela->sym->sec->data->d_buf + + rela->sym->sym.st_value + + rela_target_offset(kelf, relasec, rela); + if (!rela->string) + ERROR("could not lookup rela string for %s+%ld", + rela->sym->name, rela->addend); + } + + if (skip) + continue; + log_debug("offset %d, type %d, %s %s %ld", rela->offset, + rela->type, rela->sym->name, + (rela->addend < 0)?"-":"+", labs(rela->addend)); + if (rela->string) + log_debug(" (string = %s)", rela->string); + log_debug("\n"); + } +} + +static void kpatch_create_section_list(struct kpatch_elf *kelf) +{ + Elf_Scn *scn = NULL; + struct section *sec; + size_t shstrndx, sections_nr; + + if (elf_getshdrnum(kelf->elf, §ions_nr)) + ERROR("elf_getshdrnum"); + + /* + * elf_getshdrnum() includes section index 0 but elf_nextscn + * doesn't return that section so subtract one. + */ + sections_nr--; + + if (elf_getshdrstrndx(kelf->elf, &shstrndx)) + ERROR("elf_getshdrstrndx"); + + log_debug("=== section list (%zu) ===\n", sections_nr); + + while (sections_nr--) { + ALLOC_LINK(sec, &kelf->sections); + + scn = elf_nextscn(kelf->elf, scn); + if (!scn) + ERROR("scn NULL"); + + if (!gelf_getshdr(scn, &sec->sh)) + ERROR("gelf_getshdr"); + + sec->name = elf_strptr(kelf->elf, shstrndx, sec->sh.sh_name); + if (!sec->name) + ERROR("elf_strptr"); + + sec->data = elf_getdata(scn, NULL); + if (!sec->data) + ERROR("elf_getdata"); + + sec->index = (unsigned int)elf_ndxscn(scn); + + + if (sec->sh.sh_type == SHT_SYMTAB_SHNDX) + kelf->symtab_shndx = sec->data; + + log_debug("ndx %02d, data %p, size %zu, name %s\n", + sec->index, sec->data->d_buf, sec->data->d_size, + sec->name); + } + + /* Sanity check, one more call to elf_nextscn() should return NULL */ + if (elf_nextscn(kelf->elf, scn)) + ERROR("expected NULL"); +} + +static void kpatch_create_symbol_list(struct kpatch_elf *kelf) +{ + struct section *symtab; + struct symbol *sym; + unsigned int symbols_nr, index = 0; + Elf32_Word shndx; + + symtab = find_section_by_name(&kelf->sections, ".symtab"); + if (!symtab) + ERROR("missing symbol table"); + + symbols_nr = (unsigned int)(symtab->sh.sh_size / symtab->sh.sh_entsize); + + log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr); + + while (symbols_nr--) { + ALLOC_LINK(sym, &kelf->symbols); + + INIT_LIST_HEAD(&sym->children); + + sym->index = index; + if (!gelf_getsym(symtab->data, index, &sym->sym)) + ERROR("gelf_getsym"); + index++; + + sym->name = elf_strptr(kelf->elf, symtab->sh.sh_link, + sym->sym.st_name); + if (!sym->name) + ERROR("elf_strptr"); + + sym->type = GELF_ST_TYPE(sym->sym.st_info); + sym->bind = GELF_ST_BIND(sym->sym.st_info); + + shndx = sym->sym.st_shndx; + if (shndx == SHN_XINDEX && + !gelf_getsymshndx(symtab->data, kelf->symtab_shndx, sym->index, &sym->sym, &shndx)) + ERROR("couldn't find extended section index for symbol %s; idx=%d", + sym->name, sym->index); + + if ((sym->sym.st_shndx > SHN_UNDEF && + sym->sym.st_shndx < SHN_LORESERVE) || + sym->sym.st_shndx == SHN_XINDEX) { + sym->sec = find_section_by_index(&kelf->sections, shndx); + if (!sym->sec) + ERROR("couldn't find section for symbol %s\n", + sym->name); + + if (sym->type == STT_SECTION) { + sym->sec->secsym = sym; + /* use the section name as the symbol name */ + sym->name = sym->sec->name; + } + } + + log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s", + sym->index, sym->type, sym->bind, sym->sym.st_shndx, + sym->name); + if (sym->sec) + log_debug(" -> %s", sym->sec->name); + log_debug("\n"); + } + +} + +struct kpatch_elf *kpatch_elf_open(const char *name) +{ + Elf *elf; + int fd; + struct kpatch_elf *kelf; + struct section *relasec; + GElf_Ehdr ehdr; + + fd = open(name, O_RDONLY); + if (fd == -1) + ERROR("open"); + + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + if (!elf) + ERROR("elf_begin"); + + kelf = malloc(sizeof(*kelf)); + if (!kelf) + ERROR("malloc"); + memset(kelf, 0, sizeof(*kelf)); + INIT_LIST_HEAD(&kelf->sections); + INIT_LIST_HEAD(&kelf->symbols); + INIT_LIST_HEAD(&kelf->strings); + + /* read and store section, symbol entries from file */ + kelf->elf = elf; + kelf->fd = fd; + + if (!gelf_getehdr(kelf->elf, &ehdr)) + ERROR("gelf_getehdr"); + switch (ehdr.e_machine) { + case EM_PPC64: + kelf->arch = PPC64; + break; + case EM_X86_64: + kelf->arch = X86_64; + break; + case EM_S390: + kelf->arch = S390; + break; + default: + ERROR("Unsupported target architecture"); + } + + kpatch_create_section_list(kelf); + kpatch_create_symbol_list(kelf); + + /* for each rela section, read and store the rela entries */ + list_for_each_entry(relasec, &kelf->sections, list) { + if (!is_rela_section(relasec)) + continue; + INIT_LIST_HEAD(&relasec->relas); + kpatch_create_rela_list(kelf, relasec); + } + + return kelf; +} + +void kpatch_dump_kelf(struct kpatch_elf *kelf) +{ + struct section *sec; + struct symbol *sym; + struct rela *rela; + + if (loglevel > DEBUG) + return; + + printf("\n=== Sections ===\n"); + list_for_each_entry(sec, &kelf->sections, list) { + printf("%02d %s (%s)", sec->index, sec->name, status_str(sec->status)); + if (is_rela_section(sec)) { + printf(", base-> %s\n", sec->base->name); + /* skip .debug_* sections */ + if (is_debug_section(sec)) + goto next; + printf("rela section expansion\n"); + list_for_each_entry(rela, &sec->relas, list) { + printf("sym %d, offset %d, type %d, %s %s %ld\n", + rela->sym->index, rela->offset, + rela->type, rela->sym->name, + (rela->addend < 0)?"-":"+", + labs(rela->addend)); + } + } else { + if (sec->sym) + printf(", sym-> %s", sec->sym->name); + if (sec->secsym) + printf(", secsym-> %s", sec->secsym->name); + if (sec->rela) + printf(", rela-> %s", sec->rela->name); + } +next: + printf("\n"); + } + + printf("\n=== Symbols ===\n"); + list_for_each_entry(sym, &kelf->symbols, list) { + printf("sym %02d, type %d, bind %d, ndx %02d, name %s (%s)", + sym->index, sym->type, sym->bind, sym->sym.st_shndx, + sym->name, status_str(sym->status)); + if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT)) + printf(" -> %s", sym->sec->name); + printf("\n"); + } +} + +bool is_null_sym(struct symbol *sym) +{ + return !strlen(sym->name); +} + +bool is_file_sym(struct symbol *sym) +{ + return sym->type == STT_FILE; +} + +bool is_local_func_sym(struct symbol *sym) +{ + return sym->bind == STB_LOCAL && sym->type == STT_FUNC; +} + +bool is_local_sym(struct symbol *sym) +{ + return sym->bind == STB_LOCAL; +} + +void print_strtab(char *buf, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) { + if (buf[i] == 0) + printf("\\0"); + else + printf("%c",buf[i]); + } +} + +void kpatch_create_shstrtab(struct kpatch_elf *kelf) +{ + struct section *shstrtab, *sec; + size_t size, offset, len; + char *buf; + + shstrtab = find_section_by_name(&kelf->sections, ".shstrtab"); + if (!shstrtab) + return; + + /* determine size of string table */ + size = 1; /* for initial NULL terminator */ + list_for_each_entry(sec, &kelf->sections, list) + size += strlen(sec->name) + 1; /* include NULL terminator */ + + /* allocate data buffer */ + buf = malloc(size); + if (!buf) + ERROR("malloc"); + memset(buf, 0, size); + + /* populate string table and link with section header */ + offset = 1; + list_for_each_entry(sec, &kelf->sections, list) { + len = strlen(sec->name) + 1; + sec->sh.sh_name = (unsigned int)offset; + memcpy(buf + offset, sec->name, len); + offset += len; + } + + if (offset != size) + ERROR("shstrtab size mismatch"); + + shstrtab->data->d_buf = buf; + shstrtab->data->d_size = size; + + if (loglevel <= DEBUG) { + printf("shstrtab: "); + print_strtab(buf, size); + printf("\n"); + + list_for_each_entry(sec, &kelf->sections, list) + printf("%s @ shstrtab offset %d\n", + sec->name, sec->sh.sh_name); + } +} + +void kpatch_create_strtab(struct kpatch_elf *kelf) +{ + struct section *strtab, *shstrtab; + struct symbol *sym; + size_t size = 0, offset = 0, len; + char *buf; + + strtab = find_section_by_name(&kelf->sections, ".strtab"); + if (!strtab) + ERROR("find_section_by_name"); + + shstrtab = find_section_by_name(&kelf->sections, ".shstrtab"); + + /* determine size of string table */ + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type == STT_SECTION) + continue; + size += strlen(sym->name) + 1; /* include NULL terminator */ + } + + /* and when covering for missing .shstrtab ... */ + if (!shstrtab) { + /* factor out into common (sh)strtab feeder */ + struct section *sec; + + list_for_each_entry(sec, &kelf->sections, list) + size += strlen(sec->name) + 1; /* include NULL terminator */ + } + + /* allocate data buffer */ + buf = malloc(size); + if (!buf) + ERROR("malloc"); + memset(buf, 0, size); + + /* populate string table and link with section header */ + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type == STT_SECTION) { + sym->sym.st_name = 0; + continue; + } + len = strlen(sym->name) + 1; + sym->sym.st_name = (unsigned int)offset; + memcpy(buf + offset, sym->name, len); + offset += len; + } + + if (!shstrtab) { + struct section *sec; + + /* populate string table and link with section header */ + list_for_each_entry(sec, &kelf->sections, list) { + len = strlen(sec->name) + 1; + sec->sh.sh_name = (unsigned int)offset; + memcpy(buf + offset, sec->name, len); + offset += len; + } + } + + if (offset != size) + ERROR("strtab size mismatch"); + + strtab->data->d_buf = buf; + strtab->data->d_size = size; + + if (loglevel <= DEBUG) { + printf("strtab: "); + print_strtab(buf, size); + printf("\n"); + + list_for_each_entry(sym, &kelf->symbols, list) + printf("%s @ strtab offset %d\n", + sym->name, sym->sym.st_name); + } +} + +void kpatch_create_symtab(struct kpatch_elf *kelf) +{ + struct section *symtab; + struct section *strtab; + struct symbol *sym; + char *buf; + size_t size; + int nr = 0, nr_local = 0; + unsigned long offset = 0; + + symtab = find_section_by_name(&kelf->sections, ".symtab"); + if (!symtab) + ERROR("find_section_by_name"); + + /* count symbols */ + list_for_each_entry(sym, &kelf->symbols, list) + nr++; + + /* create new symtab buffer */ + size = nr * symtab->sh.sh_entsize; + buf = malloc(size); + if (!buf) + ERROR("malloc"); + memset(buf, 0, size); + + offset = 0; + list_for_each_entry(sym, &kelf->symbols, list) { + memcpy(buf + offset, &sym->sym, symtab->sh.sh_entsize); + offset += symtab->sh.sh_entsize; + + if (is_local_sym(sym)) + nr_local++; + } + + symtab->data->d_buf = buf; + symtab->data->d_size = size; + + /* update symtab section header */ + strtab = find_section_by_name(&kelf->sections, ".strtab"); + if (!strtab) + ERROR("missing .strtab section"); + + symtab->sh.sh_link = strtab->index; + symtab->sh.sh_info = nr_local; +} + +struct section *create_section_pair(struct kpatch_elf *kelf, char *name, + int entsize, int nr) +{ + char *relaname; + struct section *sec, *relasec; + int size = entsize * nr; + + relaname = malloc(strlen(name) + strlen(".rela") + 1); + if (!relaname) + ERROR("malloc"); + strcpy(relaname, ".rela"); + strcat(relaname, name); + + /* allocate text section resources */ + ALLOC_LINK(sec, &kelf->sections); + sec->name = name; + + /* set data */ + sec->data = malloc(sizeof(*sec->data)); + if (!sec->data) + ERROR("malloc"); + sec->data->d_buf = malloc(size); + if (!sec->data->d_buf) + ERROR("malloc"); + memset(sec->data->d_buf, 0, size); + sec->data->d_size = size; + sec->data->d_type = ELF_T_BYTE; + + /* set section header */ + sec->sh.sh_type = SHT_PROGBITS; + sec->sh.sh_entsize = entsize; + sec->sh.sh_addralign = 8; + sec->sh.sh_flags = SHF_ALLOC; + sec->sh.sh_size = size; + + /* allocate rela section resources */ + ALLOC_LINK(relasec, &kelf->sections); + relasec->name = relaname; + relasec->base = sec; + INIT_LIST_HEAD(&relasec->relas); + + /* set data, buffers generated by kpatch_rebuild_rela_section_data() */ + relasec->data = malloc(sizeof(*relasec->data)); + if (!relasec->data) + ERROR("malloc"); + relasec->data->d_type = ELF_T_RELA; + + /* set section header */ + relasec->sh.sh_type = SHT_RELA; + relasec->sh.sh_entsize = sizeof(GElf_Rela); + relasec->sh.sh_addralign = 8; + + /* set text rela section pointer */ + sec->rela = relasec; + + return sec; +} + +void kpatch_remove_and_free_section(struct kpatch_elf *kelf, char *secname) +{ + struct section *sec, *safesec; + struct rela *rela, *saferela; + + list_for_each_entry_safe(sec, safesec, &kelf->sections, list) { + if (strcmp(secname, sec->name)) + continue; + + if (is_rela_section(sec)) { + list_for_each_entry_safe(rela, saferela, &sec->relas, list) { + list_del(&rela->list); + memset(rela, 0, sizeof(*rela)); + free(rela); + } + } + + /* + * Remove the STT_SECTION symbol from the symtab, + * otherwise when we remove the section we'll end up + * with UNDEF section symbols in the symtab. + */ + if (!is_rela_section(sec) && sec->secsym) { + list_del(&sec->secsym->list); + memset(sec->secsym, 0, sizeof(*sec->secsym)); + free(sec->secsym); + } + + list_del(&sec->list); + memset(sec, 0, sizeof(*sec)); + free(sec); + } +} + +void kpatch_reindex_elements(struct kpatch_elf *kelf) +{ + struct section *sec; + struct symbol *sym; + unsigned int index; + + index = 1; /* elf write function handles NULL section 0 */ + list_for_each_entry(sec, &kelf->sections, list) + sec->index = index++; + + index = 0; + list_for_each_entry(sym, &kelf->symbols, list) { + sym->index = index++; + if (sym->sec) + sym->sym.st_shndx = (unsigned short)sym->sec->index; + else if (sym->sym.st_shndx != SHN_ABS && + sym->sym.st_shndx != SHN_LIVEPATCH) + sym->sym.st_shndx = SHN_UNDEF; + } +} + +void kpatch_rebuild_rela_section_data(struct section *sec) +{ + struct rela *rela; + int nr = 0, index = 0; + GElf_Rela *relas; + size_t size; + + list_for_each_entry(rela, &sec->relas, list) + nr++; + + size = nr * sizeof(*relas); + relas = malloc(size); + if (!relas) + ERROR("malloc"); + + sec->data->d_buf = relas; + sec->data->d_size = size; + /* d_type remains ELF_T_RELA */ + + sec->sh.sh_size = size; + + list_for_each_entry(rela, &sec->relas, list) { + relas[index].r_offset = rela->offset; + relas[index].r_addend = rela->addend; + relas[index].r_info = GELF_R_INFO(rela->sym->index, rela->type); + index++; + } + + /* sanity check, index should equal nr */ + if (index != nr) + ERROR("size mismatch in rebuilt rela section"); +} + +void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile, + mode_t mode) +{ + int fd; + struct section *sec; + struct section *shstrtab; + Elf *elfout; + GElf_Ehdr eh, ehout; + Elf_Scn *scn; + Elf_Data *data; + GElf_Shdr sh; + + fd = creat(outfile, mode); + if (fd == -1) + ERROR("creat"); + + elfout = elf_begin(fd, ELF_C_WRITE, NULL); + if (!elfout) + ERROR("elf_begin"); + + if (!gelf_newehdr(elfout, gelf_getclass(elf))) + ERROR("gelf_newehdr"); + + if (!gelf_getehdr(elfout, &ehout)) + ERROR("gelf_getehdr"); + + if (!gelf_getehdr(elf, &eh)) + ERROR("gelf_getehdr"); + + 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; + + shstrtab = find_section_by_name(&kelf->sections, ".shstrtab"); + if (!shstrtab) + shstrtab = find_section_by_name(&kelf->sections, ".strtab"); + if (!shstrtab) + ERROR("missing .shstrtab, .strtab sections"); + + ehout.e_shstrndx = (unsigned short)shstrtab->index; + + /* add changed sections */ + list_for_each_entry(sec, &kelf->sections, list) { + scn = elf_newscn(elfout); + if (!scn) + ERROR("elf_newscn"); + + data = elf_newdata(scn); + if (!data) + ERROR("elf_newdata"); + + if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) + ERROR("elf_flagdata"); + + data->d_type = sec->data->d_type; + data->d_buf = sec->data->d_buf; + data->d_size = sec->data->d_size; + + if(!gelf_getshdr(scn, &sh)) + ERROR("gelf_getshdr"); + + sh = sec->sh; + + if (!gelf_update_shdr(scn, &sh)) + ERROR("gelf_update_shdr"); + } + + if (!gelf_update_ehdr(elfout, &ehout)) + ERROR("gelf_update_ehdr"); + + if (elf_update(elfout, ELF_C_WRITE) < 0) { + printf("%s\n",elf_errmsg(-1)); + ERROR("elf_update"); + } + + elf_end(elfout); + close(fd); +} + +/* + * While this is a one-shot program without a lot of proper cleanup in case + * of an error, this function serves a debugging purpose: to break down and + * zero data structures we shouldn't be accessing anymore. This should + * help cause an immediate and obvious issue when a logic error leads to + * accessing data that is not intended to be accessed past a particular point. + */ +void kpatch_elf_teardown(struct kpatch_elf *kelf) +{ + struct section *sec, *safesec; + struct symbol *sym, *safesym; + struct rela *rela, *saferela; + + list_for_each_entry_safe(sec, safesec, &kelf->sections, list) { + if (sec->twin) + sec->twin->twin = NULL; + if (is_rela_section(sec)) { + list_for_each_entry_safe(rela, saferela, &sec->relas, list) { + memset(rela, 0, sizeof(*rela)); + free(rela); + } + } + memset(sec, 0, sizeof(*sec)); + free(sec); + } + + list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) { + if (sym->twin) + sym->twin->twin = NULL; + memset(sym, 0, sizeof(*sym)); + free(sym); + } + + INIT_LIST_HEAD(&kelf->sections); + INIT_LIST_HEAD(&kelf->symbols); +} + +void kpatch_elf_free(struct kpatch_elf *kelf) +{ + elf_end(kelf->elf); + close(kelf->fd); + memset(kelf, 0, sizeof(*kelf)); + free(kelf); +} diff --git a/kpatch-build/kpatch-elf.d b/kpatch-build/kpatch-elf.d new file mode 100644 index 0000000..445d9ad --- /dev/null +++ b/kpatch-build/kpatch-elf.d @@ -0,0 +1,16 @@ +kpatch-elf.o: kpatch-elf.c insn/asm/insn.h insn/asm/inat.h \ + insn/asm/inat_types.h kpatch-elf.h list.h log.h kpatch.h + +insn/asm/insn.h: + +insn/asm/inat.h: + +insn/asm/inat_types.h: + +kpatch-elf.h: + +list.h: + +log.h: + +kpatch.h: diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h new file mode 100644 index 0000000..3bc6e76 --- /dev/null +++ b/kpatch-build/kpatch-elf.h @@ -0,0 +1,187 @@ +/* + * kpatch-elf.h + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#ifndef _KPATCH_ELF_H_ +#define _KPATCH_ELF_H_ + +#include +#include +#include "list.h" +#include "log.h" + +#define KLP_SYM_PREFIX ".klp.sym." +#define KLP_RELASEC_PREFIX ".klp.rela." +#define KLP_ARCH_PREFIX ".klp.arch." +#define SHF_RELA_LIVEPATCH 0x00100000 +#define SHN_LIVEPATCH 0xff20 + +/******************* + * Data structures + * ****************/ +struct section; +struct symbol; +struct rela; + +enum status { + NEW, + CHANGED, + SAME +}; + +struct section { + struct list_head list; + struct section *twin; + GElf_Shdr sh; + Elf_Data *data; + char *name; + unsigned int index; + enum status status; + int include; + int ignore; + int grouped; + union { + struct { /* if (is_rela_section()) */ + struct section *base; + struct list_head relas; + }; + struct { /* else */ + struct section *rela; + struct symbol *secsym, *sym; + }; + }; +}; + +enum symbol_strip { + SYMBOL_DEFAULT, + SYMBOL_USED, + SYMBOL_STRIP, +}; + +struct symbol { + struct list_head list; + struct symbol *twin; + struct symbol *parent; + struct list_head children; + struct list_head subfunction_node; + struct section *sec; + GElf_Sym sym; + char *name; + struct object_symbol *lookup_table_file_sym; + unsigned int index; + unsigned char bind, type; + enum status status; + union { + int include; /* used in the patched elf */ + enum symbol_strip strip; /* used in the output elf */ + }; + int has_func_profiling; +}; + +struct rela { + struct list_head list; + GElf_Rela rela; + struct symbol *sym; + unsigned int type; + unsigned int offset; + long addend; + char *string; + bool need_dynrela; +}; + +struct string { + struct list_head list; + char *name; +}; + +enum architecture { + PPC64 = 0x1 << 0, + X86_64 = 0x1 << 1, + S390 = 0x1 << 2, +}; + +struct kpatch_elf { + Elf *elf; + enum architecture arch; + struct list_head sections; + struct list_head symbols; + struct list_head strings; + Elf_Data *symtab_shndx; + int fd; +}; + +/******************* + * Helper functions + ******************/ +char *status_str(enum status status); +bool is_rela_section(struct section *sec); +bool is_text_section(struct section *sec); +bool is_debug_section(struct section *sec); + +struct section *find_section_by_index(struct list_head *list, unsigned int index); +struct section *find_section_by_name(struct list_head *list, const char *name); +struct symbol *find_symbol_by_index(struct list_head *list, size_t index); +struct symbol *find_symbol_by_name(struct list_head *list, const char *name); +struct rela *find_rela_by_offset(struct section *relasec, unsigned int offset); + +#define ALLOC_LINK(_new, _list) \ +{ \ + (_new) = malloc(sizeof(*(_new))); \ + if (!(_new)) \ + ERROR("malloc"); \ + memset((_new), 0, sizeof(*(_new))); \ + INIT_LIST_HEAD(&(_new)->list); \ + if (_list) \ + list_add_tail(&(_new)->list, (_list)); \ +} + +unsigned int absolute_rela_type(struct kpatch_elf *kelf); +int offset_of_string(struct list_head *list, char *name); +long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec, + struct rela *rela); +unsigned int insn_length(struct kpatch_elf *kelf, void *addr); + +#ifndef R_PPC64_ENTRY +#define R_PPC64_ENTRY 118 +#endif + +/************* + * Functions + * **********/ +struct kpatch_elf *kpatch_elf_open(const char *name); +void kpatch_dump_kelf(struct kpatch_elf *kelf); + +bool is_null_sym(struct symbol *sym); +bool is_file_sym(struct symbol *sym); +bool is_local_func_sym(struct symbol *sym); +bool is_local_sym(struct symbol *sym); + +void print_strtab(char *buf, size_t size); +void kpatch_create_shstrtab(struct kpatch_elf *kelf); +void kpatch_create_strtab(struct kpatch_elf *kelf); +void kpatch_create_symtab(struct kpatch_elf *kelf); +struct section *create_section_pair(struct kpatch_elf *kelf, char *name, + int entsize, int nr); +void kpatch_remove_and_free_section(struct kpatch_elf *kelf, char *secname); +void kpatch_reindex_elements(struct kpatch_elf *kelf); +void kpatch_rebuild_rela_section_data(struct section *sec); +void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile, + mode_t mode); +void kpatch_elf_teardown(struct kpatch_elf *kelf); +void kpatch_elf_free(struct kpatch_elf *kelf); +#endif /* _KPATCH_ELF_H_ */ diff --git a/kpatch-build/kpatch-elf.o b/kpatch-build/kpatch-elf.o new file mode 100644 index 0000000000000000000000000000000000000000..9f101a458342b71d3e1cb01bb4fa90cae72f84e0 GIT binary patch literal 66488 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 + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#ifndef _LIST_H +#define _LIST_H + +/** + * Get offset of a member + */ +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * Casts a member of a structure out to the containing structure + * @param ptr the pointer to the member. + * @param type the type of the container struct this is embedded in. + * @param 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) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/** + * 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. + */ +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) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * 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; + 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; + 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(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +#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_struct 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_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_struct within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, 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_struct 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_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* _LIST_H_ */ diff --git a/kpatch-build/log.h b/kpatch-build/log.h new file mode 100644 index 0000000..eefa0fc --- /dev/null +++ b/kpatch-build/log.h @@ -0,0 +1,27 @@ +#ifndef _LOG_H_ +#define _LOG_H_ + +#include +#include "kpatch.h" + +/* Files that include log.h must define loglevel and childobj */ +extern enum loglevel loglevel; +extern char *childobj; + +#define ERROR(format, ...) \ + err(EXIT_STATUS_ERROR, "ERROR: %s: %s: %d: " format, childobj, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__) +#define log_normal(format, ...) log(NORMAL, "%s: " format, childobj, ##__VA_ARGS__) + +#define log(level, format, ...) \ +({ \ + if (loglevel <= (level)) \ + printf(format, ##__VA_ARGS__); \ +}) + +enum loglevel { + DEBUG, + NORMAL +}; +#endif /* _LOG_H_ */ diff --git a/kpatch-build/lookup.c b/kpatch-build/lookup.c new file mode 100644 index 0000000..f2596b1 --- /dev/null +++ b/kpatch-build/lookup.c @@ -0,0 +1,561 @@ +/* + * lookup.c + * + * This file contains functions that assist in the reading and searching + * the symbol table of an ELF object. + * + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2014 Josh Poimboeuf + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lookup.h" +#include "log.h" + +struct object_symbol { + unsigned long addr; + unsigned long size; + char *name; + int type, bind; +}; + +struct export_symbol { + char *name; + char *objname; +}; + +struct lookup_table { + int obj_nr, exp_nr; + struct object_symbol *obj_syms; + struct export_symbol *exp_syms; + char *objname; +}; + +#define for_each_obj_symbol(ndx, iter, table) \ + for (ndx = 0, iter = table->obj_syms; ndx < table->obj_nr; ndx++, iter++) + +#define for_each_obj_symbol_continue(ndx, iter, table) \ + for (iter = table->obj_syms + ndx; ndx < table->obj_nr; ndx++, iter++) + +#define for_each_exp_symbol(ndx, iter, table) \ + for (ndx = 0, iter = table->exp_syms; ndx < table->exp_nr; ndx++, iter++) + +static bool maybe_discarded_sym(const char *name) +{ + if (!name) + return false; + + /* + * Sometimes these symbols are discarded during linking, and sometimes + * they're not, depending on whether the parent object is vmlinux or a + * module, and also depending on the kernel version. For simplicity, + * we just always skip them when comparing object symbol tables. + */ + if (!strncmp(name, "__exitcall_", 11) || + !strncmp(name, "__brk_reservation_fn_", 21) || + !strncmp(name, "__func_stack_frame_non_standard_", 32) || + strstr(name, "__addressable_") || + strstr(name, "__UNIQUE_ID_") || + !strncmp(name, ".L.str", 6)) + return true; + + return false; +} + +static bool locals_match(struct lookup_table *table, int idx, + struct symbol *file_sym, struct list_head *sym_list) +{ + struct symbol *sym; + struct object_symbol *table_sym; + int i, found; + + i = idx + 1; + for_each_obj_symbol_continue(i, table_sym, table) { + if (table_sym->type == STT_FILE) + break; + if (table_sym->bind != STB_LOCAL) + continue; + if (table_sym->type != STT_FUNC && table_sym->type != STT_OBJECT) + continue; + + found = 0; + sym = file_sym; + list_for_each_entry_continue(sym, sym_list, list) { + if (sym->type == STT_FILE) + break; + if (sym->bind != STB_LOCAL) + continue; + + if (sym->type == table_sym->type && + !strcmp(sym->name, table_sym->name)) { + found = 1; + break; + } + } + + if (!found) + return false; + } + + sym = file_sym; + list_for_each_entry_continue(sym, sym_list, list) { + if (sym->type == STT_FILE) + break; + if (sym->bind != STB_LOCAL) + continue; + if (sym->type != STT_FUNC && sym->type != STT_OBJECT) + continue; + /* + * Symbols which get discarded at link time are missing from + * the lookup table, so skip them. + */ + if (maybe_discarded_sym(sym->name)) + continue; + + found = 0; + i = idx + 1; + for_each_obj_symbol_continue(i, table_sym, table) { + if (table_sym->type == STT_FILE) + break; + if (table_sym->bind != STB_LOCAL) + continue; + if (maybe_discarded_sym(table_sym->name)) + continue; + + if (sym->type == table_sym->type && + !strcmp(sym->name, table_sym->name)) { + found = 1; + break; + } + } + + if (!found) + return false; + } + + return true; +} + +static void find_local_syms(struct lookup_table *table, struct symbol *file_sym, + struct list_head *sym_list) +{ + struct object_symbol *sym; + struct object_symbol *lookup_table_file_sym = NULL; + int i; + + for_each_obj_symbol(i, sym, table) { + if (sym->type != STT_FILE) + continue; + if (strcmp(file_sym->name, sym->name)) + continue; + if (!locals_match(table, i, file_sym, sym_list)) + continue; + if (lookup_table_file_sym) + ERROR("found duplicate matches for %s local symbols in %s symbol table", + file_sym->name, table->objname); + + lookup_table_file_sym = sym; + } + + if (!lookup_table_file_sym) + ERROR("couldn't find matching %s local symbols in %s symbol table", + file_sym->name, table->objname); + + list_for_each_entry_continue(file_sym, sym_list, list) { + if (file_sym->type == STT_FILE) + break; + file_sym->lookup_table_file_sym = lookup_table_file_sym; + } +} + +/* + * Because there can be duplicate symbols and duplicate filenames we need to + * correlate each symbol from the elf file to it's corresponding symbol in + * lookup table. Both the elf file and the lookup table can be split on + * STT_FILE symbols into blocks of symbols originating from a single source + * file. We then compare local symbol lists from both blocks and store the + * pointer to STT_FILE symbol in lookup table for later use in + * lookup_local_symbol(). + */ +static void find_local_syms_multiple(struct lookup_table *table, + struct kpatch_elf *kelf) +{ + struct symbol *sym; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type == STT_FILE) + find_local_syms(table, sym, &kelf->symbols); + } +} + +/* Strip the path and replace '-' with '_' */ +static char *make_modname(char *modname) +{ + char *cur, *name; + + if (!modname) + return NULL; + + name = strdup(basename(modname)); + if (!name) + ERROR("strdup"); + + cur = name; /* use cur as tmp */ + while (*cur != '\0') { + if (*cur == '-') + *cur = '_'; + cur++; + } + + return name; +} + +static void symtab_read(struct lookup_table *table, char *path) +{ + FILE *file; + long unsigned int addr; + int alloc_nr = 0, i = 0; + int matched; + bool skip = false; + char line[256], name[256], size[16], type[16], bind[16], ndx[16]; + + if ((file = fopen(path, "r")) == NULL) + ERROR("fopen"); + + /* + * First, get an upper limit on the number of entries for allocation + * purposes: + */ + while (fgets(line, 256, file)) + alloc_nr++; + + table->obj_syms = malloc(alloc_nr * sizeof(*table->obj_syms)); + if (!table->obj_syms) + ERROR("malloc table.obj_syms"); + memset(table->obj_syms, 0, alloc_nr * sizeof(*table->obj_syms)); + + rewind(file); + + /* Now read the actual entries: */ + while (fgets(line, 256, file)) { + + /* + * On powerpc, "readelf -s" shows both .dynsym and .symtab + * tables. .dynsym is just a subset of .symtab, so skip it to + * avoid duplicates. + */ + if (!strncmp(line, "Symbol table ", 13)) { + if (strstr(line, ".dynsym")) { + skip = true; + continue; + } else if (strstr(line, ".symtab")) { + skip = false; + continue; + } + } + if (skip) + continue; + + matched = sscanf(line, "%*s %lx %s %s %s %*s %s %s\n", + &addr, size, type, bind, ndx, name); + + if (matched == 5) { + name[0] = '\0'; + matched++; + } + + if (matched != 6 || + !strcmp(ndx, "UND") || + !strcmp(type, "SECTION")) + continue; + + table->obj_syms[i].addr = addr; + table->obj_syms[i].size = strtoul(size, NULL, 0); + + if (!strcmp(bind, "LOCAL")) { + table->obj_syms[i].bind = STB_LOCAL; + } else if (!strcmp(bind, "GLOBAL")) { + table->obj_syms[i].bind = STB_GLOBAL; + } else if (!strcmp(bind, "WEAK")) { + table->obj_syms[i].bind = STB_WEAK; + } else { + ERROR("unknown symbol bind %s", bind); + } + + if (!strcmp(type, "NOTYPE")) { + table->obj_syms[i].type = STT_NOTYPE; + } else if (!strcmp(type, "OBJECT")) { + table->obj_syms[i].type = STT_OBJECT; + } else if (!strcmp(type, "FUNC")) { + table->obj_syms[i].type = STT_FUNC; + } else if (!strcmp(type, "FILE")) { + table->obj_syms[i].type = STT_FILE; + } else { + ERROR("unknown symbol type %s", type); + } + + table->obj_syms[i].name = strdup(name); + if (!table->obj_syms[i].name) + ERROR("strdup"); + + i++; + } + + table->obj_nr = i; + + fclose(file); +} + +/* + * The Module.symvers file format is one of the following, depending on kernel + * version: + * + * + * + * + * + * All we care about is Symbol and Module. Since the format is unpredictable, + * we have to dynamically determine which column is Module by looking for + * "vmlinux". + */ +static void symvers_read(struct lookup_table *table, char *path) +{ + FILE *file; + int i, column, mod_column = 0; + char line[4096]; + char *tmp, *objname, *symname; + + if ((file = fopen(path, "r")) == NULL) + ERROR("fopen"); + + while (fgets(line, 4096, file)) { + table->exp_nr++; + + if (mod_column) + continue; + + /* Find the module column */ + for (column = 1, tmp = line; (tmp = strchr(tmp, '\t')); column++) { + tmp++; + if (*tmp && !strncmp(tmp, "vmlinux", 7)) + mod_column = column; + } + } + + if (table->exp_nr && !mod_column) + ERROR("Module.symvers: invalid format"); + + table->exp_syms = malloc(table->exp_nr * sizeof(*table->exp_syms)); + if (!table->exp_syms) + ERROR("malloc table.exp_syms"); + memset(table->exp_syms, 0, + table->exp_nr * sizeof(*table->exp_syms)); + + rewind(file); + for (i = 0; fgets(line, 4096, file); i++) { + char *name = NULL, *mod = NULL; + + for (column = 1, tmp = line; (tmp = strchr(tmp, '\t')); column++) { + *tmp++ = '\0'; + if (*tmp && column == 1) + name = tmp; + else if (*tmp && column == mod_column) + mod = tmp; + } + + if (!name || !mod) + continue; + + symname = strdup(name); + if (!symname) + perror("strdup"); + + objname = make_modname(mod); + + table->exp_syms[i].name = symname; + table->exp_syms[i].objname = objname; + } + + fclose(file); +} + +struct lookup_table *lookup_open(char *symtab_path, char *objname, + char *symvers_path, struct kpatch_elf *kelf) +{ + struct lookup_table *table; + + table = malloc(sizeof(*table)); + if (!table) + ERROR("malloc table"); + memset(table, 0, sizeof(*table)); + + table->objname = objname; + symtab_read(table, symtab_path); + symvers_read(table, symvers_path); + + find_local_syms_multiple(table, kelf); + + return table; +} + +void lookup_close(struct lookup_table *table) +{ + int i; + struct object_symbol *obj_sym; + struct export_symbol *exp_sym; + + for_each_obj_symbol(i, obj_sym, table) + free(obj_sym->name); + free(table->obj_syms); + + for_each_exp_symbol(i, exp_sym, table) { + free(exp_sym->name); + free(exp_sym->objname); + } + free(table->exp_syms); + free(table); +} + +static bool lookup_local_symbol(struct lookup_table *table, + struct symbol *lookup_sym, + struct lookup_result *result) +{ + struct object_symbol *sym; + unsigned long sympos = 0; + int i, in_file = 0; + + memset(result, 0, sizeof(*result)); + for_each_obj_symbol(i, sym, table) { + if (sym->bind == STB_LOCAL && !strcmp(sym->name, + lookup_sym->name)) + sympos++; + + if (lookup_sym->lookup_table_file_sym == sym) { + in_file = 1; + continue; + } + + if (!in_file) + continue; + + if (sym->type == STT_FILE) + break; + + if (sym->bind == STB_LOCAL && !strcmp(sym->name, + lookup_sym->name)) { + if (result->objname) + ERROR("duplicate local symbol found for %s", + lookup_sym->name); + + result->objname = table->objname; + result->addr = sym->addr; + result->size = sym->size; + result->sympos = sympos; + result->global = false; + result->exported = false; + } + } + + return !!result->objname; +} + +static bool lookup_exported_symbol(struct lookup_table *table, char *name, + struct lookup_result *result) +{ + struct export_symbol *sym; + int i; + + if (result) + memset(result, 0, sizeof(*result)); + + for_each_exp_symbol(i, sym, table) { + if (!strcmp(sym->name, name)) { + + if (!result) + return true; + + if (result->objname) + ERROR("duplicate exported symbol found for %s", name); + + result->objname = sym->objname; + result->addr = 0; /* determined at runtime */ + result->size = 0; /* not used for exported symbols */ + result->sympos = 0; /* always 0 for exported symbols */ + result->global = true; + result->exported = true; + } + } + + return result && result->objname; +} + +bool is_exported(struct lookup_table *table, char *name) +{ + return lookup_exported_symbol(table, name, NULL); +} + +static bool lookup_global_symbol(struct lookup_table *table, char *name, + struct lookup_result *result) +{ + struct object_symbol *sym; + int i; + + memset(result, 0, sizeof(*result)); + for_each_obj_symbol(i, sym, table) { + if ((sym->bind == STB_GLOBAL || sym->bind == STB_WEAK) && + !strcmp(sym->name, name)) { + + if (result->objname) + ERROR("duplicate global symbol found for %s", name); + + result->objname = table->objname; + result->addr = sym->addr; + result->size = sym->size; + result->sympos = 0; /* always 0 for global symbols */ + result->global = true; + result->exported = is_exported(table, name); + } + } + + return !!result->objname; +} + +bool lookup_symbol(struct lookup_table *table, struct symbol *sym, + struct lookup_result *result) +{ + if (lookup_local_symbol(table, sym, result)) + return true; + + if (lookup_global_symbol(table, sym->name, result)) + return true; + + return lookup_exported_symbol(table, sym->name, result); +} diff --git a/kpatch-build/lookup.d b/kpatch-build/lookup.d new file mode 100644 index 0000000..4d6cb93 --- /dev/null +++ b/kpatch-build/lookup.d @@ -0,0 +1,11 @@ +lookup.o: lookup.c lookup.h kpatch-elf.h list.h log.h kpatch.h + +lookup.h: + +kpatch-elf.h: + +list.h: + +log.h: + +kpatch.h: diff --git a/kpatch-build/lookup.h b/kpatch-build/lookup.h new file mode 100644 index 0000000..e1277f1 --- /dev/null +++ b/kpatch-build/lookup.h @@ -0,0 +1,23 @@ +#ifndef _LOOKUP_H_ +#define _LOOKUP_H_ + +#include +#include "kpatch-elf.h" + +struct lookup_table; + +struct lookup_result { + char *objname; + unsigned long addr; + unsigned long size; + unsigned long sympos; + bool global, exported; +}; + +struct lookup_table *lookup_open(char *symtab_path, char *objname, + char *symvers_path, struct kpatch_elf *kelf); +void lookup_close(struct lookup_table *table); +bool lookup_symbol(struct lookup_table *table, struct symbol *sym, + struct lookup_result *result); + +#endif /* _LOOKUP_H_ */ diff --git a/kpatch-build/lookup.o b/kpatch-build/lookup.o new file mode 100644 index 0000000000000000000000000000000000000000..0954f9a55ea571dd4a4f44561414244764c8456f GIT binary patch literal 31560 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 literal 0 HcmV?d00001 diff --git a/kpatch/Makefile b/kpatch/Makefile new file mode 100644 index 0000000..448968f --- /dev/null +++ b/kpatch/Makefile @@ -0,0 +1,12 @@ +include ../Makefile.inc + +all: + +install: all + $(INSTALL) -d $(SBINDIR) + $(INSTALL) kpatch $(SBINDIR) + +uninstall: + $(RM) $(SBINDIR)/kpatch + +clean: diff --git a/kpatch/kpatch b/kpatch/kpatch new file mode 100755 index 0000000..2a00b07 --- /dev/null +++ b/kpatch/kpatch @@ -0,0 +1,649 @@ +#!/bin/bash +# +# kpatch hot patch module management script +# +# Copyright (C) 2014 Seth Jennings +# Copyright (C) 2014 Josh Poimboeuf +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, +# 02110-1301, USA. + +# This is the kpatch user script that manages installing, loading, and +# displaying information about kernel patch modules installed on the system. + +INSTALLDIR=/var/lib/kpatch +SCRIPTDIR="$(readlink -f "$(dirname "$(type -p "$0")")")" +VERSION="0.9.7" +POST_ENABLE_WAIT=15 # seconds +POST_SIGNAL_WAIT=60 # seconds +MODULE_REF_WAIT=15 # seconds + +# How many times to try loading the patch if activeness safety check fails. +MAX_LOAD_ATTEMPTS=5 +# How long to wait before retry, in seconds. +RETRY_INTERVAL=2 + +usage_cmd() { + printf ' %-20s\n%s\n' "$1" "$(fmt -w 80 <(echo " $2"))" >&2 +} + +usage () { + # ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION + # When changing this, please also update the man page. Thanks! + echo "usage: kpatch []" >&2 + echo >&2 + echo "Valid commands:" >&2 + usage_cmd "install [-k|--kernel-version=] " "install patch module to be loaded at boot" + usage_cmd "uninstall [-k|--kernel-version=] " "uninstall patch module" + echo >&2 + usage_cmd "load --all" "load all installed patch modules into the running kernel" + usage_cmd "load " "load patch module into the running kernel" + usage_cmd "unload --all" "unload all patch modules from the running kernel" + usage_cmd "unload " "unload patch module from the running kernel" + echo >&2 + usage_cmd "info " "show information about a patch module" + echo >&2 + usage_cmd "list" "list installed patch modules" + echo >&2 + usage_cmd "signal" "signal/poke any process stalling the current patch transition. This is only useful on systems that have the sysfs livepatch signal interface. On other systems, the signaling should be done automatically by the OS and this subcommand is a no-op." + echo >&2 + usage_cmd "version" "display the kpatch version" + exit 1 +} + +warn() { + echo "kpatch: $*" >&2 +} + +die() { + warn "$@" + exit 1 +} + +__find_module () { + MODULE="$1" + [[ -f "$MODULE" ]] && return + + MODULE="$INSTALLDIR/$(uname -r)/$1" + [[ -f "$MODULE" ]] && return + + return 1 +} + +mod_name () { + MODNAME="$(basename "$1")" + MODNAME="${MODNAME%.ko}" + MODNAME="${MODNAME//-/_}" +} + +find_module () { + arg="$1" + if [[ "$arg" =~ \.ko ]]; then + __find_module "$arg" || return 1 + mod_name "$MODULE" + return + else + for i in "$INSTALLDIR/$(uname -r)"/*; do + mod_name "$i" + if [[ "$MODNAME" == "$arg" ]]; then + MODULE="$i" + return + fi + done + fi + + return 1 +} + +find_core_module() { + COREMOD="$SCRIPTDIR"/../kmod/core/kpatch.ko + [[ -f "$COREMOD" ]] && return + + COREMOD="/usr/local/lib/kpatch/$(uname -r)/kpatch.ko" + [[ -f "$COREMOD" ]] && return + + COREMOD="/usr/lib/kpatch/$(uname -r)/kpatch.ko" + [[ -f "$COREMOD" ]] && return + + COREMOD="/usr/local/lib/modules/$(uname -r)/extra/kpatch/kpatch.ko" + [[ -f "$COREMOD" ]] && return + + COREMOD="/usr/lib/modules/$(uname -r)/extra/kpatch/kpatch.ko" + [[ -f "$COREMOD" ]] && return + + return 1 +} + +kpatch_core_loaded() { + [[ -d "/sys/kernel/kpatch" ]] +} + +core_loaded () { + [[ -d "/sys/kernel/kpatch" ]] || [[ -d "/sys/kernel/livepatch" ]] +} + +get_module_name () { + readelf -p .gnu.linkonce.this_module "$1" | grep '\[.*\]' | awk '{print $3}' +} + +init_sysfs_var() { + # If the kernel is configured with CONFIG_LIVEPATCH, use that. + # Otherwise, use the kpatch core module (kpatch.ko). + if [[ -e /sys/kernel/livepatch ]] ; then + # livepatch ABI + SYSFS="/sys/kernel/livepatch" + + elif [[ -e /sys/kernel/kpatch/patches ]] ; then + # kpatch pre-0.4 ABI + SYSFS="/sys/kernel/kpatch/patches" + + else + # kpatch 0.4 ABI + SYSFS="/sys/kernel/kpatch" + fi +} + +verify_module_checksum () { + modname="$(get_module_name "$1")" + [[ -z "$modname" ]] && return 1 + + checksum="$(readelf -p .kpatch.checksum "$1" 2>&1 | grep '\[.*\]' | awk '{print $3}')" + + # Fail checksum match only if both exist and diverge + if [[ -n "$checksum" ]] && [[ -e "$SYSFS/${modname}/checksum" ]] ; then + sysfs_checksum="$(cat "$SYSFS/${modname}/checksum")" + [[ "$checksum" == "$sysfs_checksum" ]] || return 1 + fi + + return 0 +} + +in_transition() { + local moddir="$SYSFS/$1" + [[ $(cat "$moddir/transition" 2>/dev/null) == "1" ]] && return 0 + return 1 +} + +is_stalled() { + local module="$1" + local pid="$2" + local patch_enabled + local patch_state + + patch_enabled="$(cat "$SYSFS/$module/enabled" 2>/dev/null)" + patch_state="$(cat "/proc/$pid/patch_state" 2>/dev/null)" + + # No patch transition in progress + [[ "$patch_state" == "-1" ]] && return 1 + + [[ -z "$patch_enabled" ]] || [[ -z "$patch_state" ]] && return 1 + + # Stalls can be determined if the process state does not match + # the transition target (ie, "enabled" and "patched", "disabled" + # and "unpatched"). The state value enumerations match, so we + # can just compare them directly: + [[ "$patch_enabled" != "$patch_state" ]] && return 0 + return 1 +} + +get_transition_patch() { + local module + local modname + for module in "$SYSFS"/*; do + modname=$(basename "$module") + if in_transition "$modname" ; then + echo "$modname" + return + fi + done +} + +show_stalled_processes() { + local module + local proc_task + local tid + + module=$(get_transition_patch) + [[ -z "$module" ]] && return + + echo "" + echo "Stalled processes:" + for proc_task in /proc/[0-9]*/task/[0-9]*; do + tid=${proc_task#*/task/} + is_stalled "$module" "$tid" && echo -e "$tid $(cat "$proc_task"/comm 2>/dev/null)\nstack:\n$(cat "$proc_task"/stack 2>/dev/null)" + done +} + +signal_stalled_processes() { + local module + local proc_task + local tid + + module=$(get_transition_patch) + [[ -z "$module" ]] && return + + if [[ -e "/sys/kernel/livepatch/$module/signal" ]] ; then + echo "signaling stalled process(es):" + echo 1 > "/sys/kernel/livepatch/$module/signal" + else + warn "Livepatch process signaling is performed automatically on your system." + warn "Skipping manual process signaling." + fi +} + +wait_for_patch_transition() { + local module="$1" + local i + + in_transition "$module" || return 0 + + echo "waiting (up to $POST_ENABLE_WAIT seconds) for patch transition to complete..." + for (( i=0; i/dev/null) -gt "0" ]] +} + +wait_for_zero_module_ref_count() { + local modname="$1" + local i=0 + + # We can't rely on a zero refcount with kpatch.ko as it + # implements KPATCH_FORCE_UNSAFE with an additional reference on + # kpatch-patch modules to avoid potential crashes. + kpatch_core_loaded && return 0 + + module_ref_count "$modname" || return 0 + + echo "waiting (up to $MODULE_REF_WAIT seconds) for module refcount..." + for (( i=0; i "${moddir}/enabled" || die "failed to re-enable module $modname" + if ! wait_for_patch_transition "$modname" ; then + show_stalled_processes + echo "module $modname did not complete its transition, disabling..." + echo 0 > "${moddir}/enabled" || die "failed to disable module $modname" + wait_for_patch_transition "$modname" + die "error: failed to re-enable module $modname (transition stalled), patch disabled" + fi + return + else + die "error: cannot re-enable patch module $modname, cannot verify checksum match" + fi + else + echo "module named $modname already loaded and enabled" + fi + else + # Cleanup possibly loaded, but disabled patch. + remove_module "$modname" "quiet" + + echo "loading patch module: $module" + local i=0 + while true; do + out="$(LC_ALL=C insmod "$module" 2>&1)" + [[ -z "$out" ]] && break + echo "$out" 1>&2 + [[ ! "$out" =~ "Device or resource busy" ]] && + die "failed to load module $module" + + # "Device or resource busy" means the activeness safety check + # failed. Retry in a few seconds. + i=$((i+1)) + if [[ $i -eq $MAX_LOAD_ATTEMPTS ]]; then + die "failed to load module $module" + break + else + warn "retrying..." + sleep $RETRY_INTERVAL + fi + done + fi + + if ! wait_for_patch_transition "$modname" ; then + show_stalled_processes + echo "module $modname did not complete its transition, unloading..." + unload_module "$modname" + die "error: failed to load module $modname (transition stalled)" + fi + + return 0 +} + +disable_patch () { + local modname="$1" + + local enabled="$SYSFS/$modname/enabled" + if ! [[ -e "$enabled" ]]; then + if [[ -d "/sys/module/$modname" ]] ; then + # Module is loaded, but already disabled + return 0 + fi + warn "patch module $modname is not loaded" + return 1 + fi + + if [[ "$(cat "$enabled")" -eq 1 ]]; then + echo "disabling patch module: $modname" + local i=0 + while true; do + out="$(export LC_ALL=C; sh -c "echo 0 > $enabled" 2>&1)" + [[ -z "$out" ]] && break + echo "$out" 1>&2 + if [[ ! "$out" =~ "Device or resource busy" ]]; then + return 1 + fi + + # "Device or resource busy" means the activeness safety check + # failed. Retry in a few seconds. + i=$((i+1)) + if [[ $i -eq $MAX_LOAD_ATTEMPTS ]]; then + return 1 + else + warn "retrying..." + sleep $RETRY_INTERVAL + fi + done + fi +} + +disable_patch_strict () { + local modname="$1" + + disable_patch "$modname" || die "failed to disable module $modname" + + if ! wait_for_patch_transition "$modname" ; then + die "transition stalled for $modname" + fi +} + +remove_module () { + local modname="$1" + + if ! wait_for_zero_module_ref_count "$modname"; then + die "failed to unload module $modname (refcnt)" + fi + + if [[ "$#" -lt 2 || "$2" != "quiet" ]] ; then + echo "unloading patch module: $modname" + fi + # ignore any error here because rmmod can fail if the module used + # KPATCH_FORCE_UNSAFE. + rmmod "$modname" 2> /dev/null || return 0 +} + +unload_module () { + PATCH="${1//-/_}" + PATCH="${PATCH%.ko}" + disable_patch_strict "$PATCH" + remove_module "$PATCH" +} + +get_module_version() { + MODVER="$(modinfo -F vermagic "$1")" || return 1 + MODVER="${MODVER/ */}" +} + +unset MODULE + +# Initialize the $SYSFS var. This only works if the core module has been +# loaded. Otherwise, the value of $SYSFS doesn't matter at this point anyway, +# and we'll have to call this function again after loading it. +init_sysfs_var + +[[ "$#" -lt 1 ]] && usage +case "$1" in +"load") + [[ "$#" -ne 2 ]] && usage + case "$2" in + "--all") + for i in "$INSTALLDIR/$(uname -r)"/*.ko; do + [[ -e "$i" ]] || continue + load_module "$i" || die "failed to load module $i" + done + ;; + *) + PATCH="$2" + find_module "$PATCH" || die "can't find $PATCH" + load_module "$MODULE" || die "failed to load module $PATCH" + ;; + esac + ;; + +"unload") + [[ "$#" -ne 2 ]] && usage + case "$2" in + "--all") + # Versions of linux < 5.1 livepatching require patches to be + # disabled in the inverse order in which they were enabled. + while true; do + nr_disabled=0 + for module in "$SYSFS"/*; do + modname="$(basename "$module")" + + [[ -e "$module" ]] || continue + disable_patch "$modname" || continue + if ! wait_for_patch_transition "$modname" ; then + warn "transition stalled for $modname" + continue + fi + remove_module "$modname" + nr_disabled=$((nr_disabled + 1)) + done + if [ $nr_disabled -eq 0 ]; then + break + fi + done + + nr_remaining=0 + for module in "$SYSFS"/*; do + modname="$(basename "$module")" + + [[ -e "$module" ]] || continue + nr_remaining=$((nr_remaining + 1)) + warn "failed to unload module $modname" + done + + if [ $nr_remaining -gt 0 ]; then + exit 1 + fi + ;; + *) + unload_module "$(basename "$2")" || die "failed to unload module $2" + ;; + esac + ;; + +"install") + KVER="$(uname -r)" + shift + options="$(getopt -o k: -l "kernel-version:" -- "$@")" || die "getopt failed" + eval set -- "$options" + while [[ $# -gt 0 ]]; do + case "$1" in + -k|--kernel-version) + KVER="$2" + shift + ;; + --) + [[ -z "$2" ]] && die "no module file specified" + PATCH="$2" + ;; + esac + shift + done + + [[ ! -e "$PATCH" ]] && die "$PATCH doesn't exist" + [[ "${PATCH: -3}" == ".ko" ]] || die "$PATCH isn't a .ko file" + + get_module_version "$PATCH" || die "modinfo failed" + [[ "$KVER" != "$MODVER" ]] && die "invalid module version $MODVER for kernel $KVER" + + [[ -e "$INSTALLDIR/$KVER/$(basename "$PATCH")" ]] && die "$PATCH is already installed" + + echo "installing $PATCH ($KVER)" + mkdir -p "$INSTALLDIR/$KVER" || die "failed to create install directory" + cp -f "$PATCH" "$INSTALLDIR/$KVER" || die "failed to install module $PATCH" + command -v systemctl > /dev/null 2>&1 && systemctl enable kpatch.service + ;; + +"uninstall") + KVER="$(uname -r)" + shift + options="$(getopt -o k: -l "kernel-version:" -- "$@")" || die "getopt failed" + eval set -- "$options" + while [[ $# -gt 0 ]]; do + case "$1" in + -k|--kernel-version) + KVER="$2" + shift + ;; + --) + [[ -z "$2" ]] && die "no module file specified" + PATCH="$2" + [[ "$PATCH" != "$(basename "$PATCH")" ]] && die "please supply patch module name without path" + ;; + esac + shift + done + + MODULE="$INSTALLDIR/$KVER/$PATCH" + if [[ ! -f "$MODULE" ]]; then + mod_name "$PATCH" + PATCHNAME="$MODNAME" + for i in "$INSTALLDIR/$KVER"/*; do + mod_name "$i" + if [[ "$MODNAME" == "$PATCHNAME" ]]; then + MODULE="$i" + break + fi + done + fi + + [[ ! -e "$MODULE" ]] && die "$PATCH is not installed for kernel $KVER" + + echo "uninstalling $PATCH ($KVER)" + rm -f "$MODULE" || die "failed to uninstall module $PATCH" + rmdir --ignore-fail-on-non-empty "$INSTALLDIR/$KVER" || die "failed to remove directory $INSTALLDIR/$KVER" + rmdir --ignore-fail-on-non-empty "$INSTALLDIR" || die "failed to remove directory $INSTALLDIR" + + ;; + +"list") + [[ "$#" -ne 1 ]] && usage + echo "Loaded patch modules:" + for module in "$SYSFS"/*; do + if [[ -e "$module" ]]; then + modname=$(basename "$module") + if [[ "$(cat "$module/enabled" 2>/dev/null)" -eq 1 ]]; then + in_transition "$modname" && state="enabling..." \ + || state="enabled" + else + in_transition "$modname" && state="disabling..." \ + || state="disabled" + fi + echo "$modname [$state]" + fi + done + show_stalled_processes + echo "" + echo "Installed patch modules:" + for kdir in "$INSTALLDIR"/*; do + [[ -e "$kdir" ]] || continue + for module in "$kdir"/*.ko; do + [[ -e "$module" ]] || continue + mod_name "$module" + echo "$MODNAME ($(basename "$kdir"))" + done + done + ;; + +"info") + [[ "$#" -ne 2 ]] && usage + PATCH="$2" + find_module "$PATCH" || die "can't find $PATCH" + echo "Patch information for $PATCH:" + modinfo "$MODULE" || die "failed to get info for module $PATCH" + ;; + +"signal") + [[ "$#" -ne 1 ]] && usage + signal_stalled_processes + ;; + +"help"|"-h"|"--help") + usage + ;; + +"version"|"-v"|"--version") + echo "$VERSION" + ;; + +*) + echo "subcommand $1 not recognized" + usage + ;; +esac diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 0000000..762042d --- /dev/null +++ b/man/Makefile @@ -0,0 +1,22 @@ +include ../Makefile.inc + +all: kpatch.1.gz kpatch-build.1.gz + +kpatch.1.gz: kpatch.1 + gzip -c -9 $< > $@ + +kpatch-build.1.gz: kpatch-build.1 + gzip -c -9 $< > $@ + +install: all + $(INSTALL) -d $(MANDIR) + $(INSTALL) -m 644 kpatch.1.gz $(MANDIR) + $(INSTALL) -m 644 kpatch-build.1.gz $(MANDIR) + +uninstall: + $(RM) $(MANDIR)/kpatch.1* + $(RM) $(MANDIR)/kpatch-build.1* + +clean: + $(RM) kpatch.1.gz + $(RM) kpatch-build.1.gz diff --git a/man/kpatch-build.1 b/man/kpatch-build.1 new file mode 100644 index 0000000..f0e4d7d --- /dev/null +++ b/man/kpatch-build.1 @@ -0,0 +1,83 @@ +.\" Manpage for kpatch-build. +.\" Contact udoseidel@gmx.de to correct errors or typos. +.TH man 1 "23 Mar 2014" "1.0" "kpatch-build man page" +.SH NAME +kpatch-build \- build script +.SH SYNOPSIS +kpatch-build [options] +.SH DESCRIPTION +This script takes a patch based on the version of the kernel +currently running and creates a kernel module that will replace +modified functions in the kernel such that the patched code takes +effect. + +.SH OPTIONS + +-h|--help + Show this help message + +-a|--archversion + Specify the kernel arch version + +-r|--sourcerpm + Specify kernel source RPM + +-s|--sourcedir + Specify kernel source directory + +-c|--config + Specify kernel config file + +-v|--vmlinux + Specify original vmlinux + +-j|--jobs + Specify the number of make jobs + +-t|--target + Specify custom kernel build targets + +-n|--name + Specify the name of the kpatch module + +-o|--output + Specify output folder + +-d|--debug + Keep scratch files in /tmp + (can be specified multiple times) + +--oot-module + Enable patching out-of-tree module, + specify current version of module + +--oot-module-src + Specify out-of-tree module source directory + +-R|--non-replace + Disable replace flag of KLP + (replace is on by default) + +--skip-cleanup + Skip post-build cleanup + +--skip-compiler-check + Skips check that ensures that the system compiler version and + the compiler version that built the kernel match. Skipping this + check is not recommended, but is useful if the exact compiler + version is not available or is not easily installed. Use + only when confident that the two versions of compiler output + identical objects for a given target. Otherwise, use of + this option might result in unexpected changed objects + being detected. + +.SH SEE ALSO +kpatch(1) +.SH BUGS +No known bugs. +.SH AUTHOR +Udo Seidel (udoseidel@gmx.de) +.SH COPYRIGHT +Copyright (C) 2014: Seth Jennings , Copyright (C) +2013,2014: Josh Poimboeuf + diff --git a/man/kpatch-build.1.gz b/man/kpatch-build.1.gz new file mode 100644 index 0000000000000000000000000000000000000000..a42393f8c2b746ad49fd63ecaf0390ed41ca66c1 GIT binary patch literal 998 zcmVsJhXN&ri3+C43T zqFL8y(l)VSIfr7q2TB@Av?)^HBaVUn@jE0Xt&+AEL!SA*dCxHWv4DFfd)6S-O2e^d zwrtbFaZzPSM%tBE>4G`a+du52YzlDxTxPryI z_vApsySG<=EMRe!y`|r|mw@yB7fF`i!o$t|I(ec0kwW}1rRKd2sQmlGW}DyTFOh$% z-f|_)KX4tyP;-GF0^!p-U+wO;`@79UvTwN=jDclGG{7L@3udqaCBe1`PpA!9167B= zN7NEUQaVamT5$&Lq~x*zCMzg4G8+s<7<8&~f@;(<3nwlFXzT?mF(C!7IeDp_EJL)w z<;(#XM}Y*M7lyb9T`FI3A8dkkO*PMwkk=-plqX5r{+p&Piar5=yj3TnQ<`1^9U4Og zCJ-YK(`7s47O*{*ygtuE@Q5*eB55KS<#dU<@8*;-;-G`w_MX6I0#{uB9!yF~Lg_P6 zN}`lX*1Y)zJ}N>d3Hn6Pr%rI`p66JldBY_WFzS-@K*)nC%k8DqsU1}l0whT- zp)J!5+Bv>-#;R_NB+ghwd5$EGWF7wGfn?Lr#JP&oKpaIJ_=ytQKT)A3R7I!? z3m59~6|wizIt_Ffm^ZcyoxUh(dkO>-O!>L$oUpw2v&X6Lmt-zg%BDjIL|IE#h+(q) z{F3)n)v48pL!e6!GgA@5z|YrVN@r#{HM)G2(<`gLYW>dFP|0-M4l?kGn-Je1f?BY~ zJN^1?8xkJnG~5(TafS+OMmdK}&5`$MDUeAQ7lHl*T_ihPh*32`Ro7EVbxKQC9!G|O z@HcKDO5-#YeOgI#HWoV=sZ(L-4tN6|@{=x*eF%2GXPs|B7Jc@<6@F#Cjp&{-l*&>e z$XtiA!fHu2td~23wG+VOl;N{K(4$jtVDvkfU{8#TP*pn7gAAGBbj7$dmI;AX29G8* zKuHpxT9k43D&PDmd3#c$apSu(W%~bA2L>-`8&z?jPMh$sFlhJ_*^K)`NpHxUK5>If zpAFfH#q(_qR~$Otv_5-7&GjSiBtFw{S$_>%CL4dLqXW+@&=;^m8xX^F%-3tU`Ic{n zXX4`OGKl|r{G2Bb3XW2pXoOtjUo4W}JnnBdyX3J_kcX!ZE?&QS(ZFi6{l2^Ve7jFp zsz2)x=*8+XJaT^$*S7El!|Pz++8pBNM~xMgF{6@wSi%cpLd5sW$c8V [] +.SH DESCRIPTION +kpatch is a user script that manages installing, loading, and +displaying information about kernel patch modules installed on +the system. +.SH COMMANDS + +install [-k|--kernel-version=] + install patch module to be loaded at boot + +uninstall [-k|--kernel-version=] + uninstall patch module + +load --all + load all installed patch modules into the running kernel + +load + load patch module into the running kernel + +unload --all + unload all patch modules from the running kernel + +unload + unload patch module from the running kernel + +info + show information about a patch module + +list + list installed patch modules + +signal + signal/poke any process stalling the current patch transition. + This is only useful on systems that have the sysfs livepatch signal interface. + On other systems, the signaling should be done automatically by the OS and + this subcommand is a no-op. + +version + display the kpatch version + +.SH SEE ALSO +kpatch-build(1) +.SH BUGS +No known bugs. +.SH AUTHOR +Udo Seidel (udoseidel@gmx.de) +.SH COPYRIGHT +Copyright (C) 2014: Seth Jennings and +Josh Poimboeuf + diff --git a/man/kpatch.1.gz b/man/kpatch.1.gz new file mode 100644 index 0000000000000000000000000000000000000000..6dc57944e03b16a1d96a84af151f38e3a96e1a4a GIT binary patch literal 698 zcmV;r0!95FiwFpsTQ*|?18Z<$bYo~PF#x4hO>f&U487}D5IMC#?KRy_Ll!hkgC@oD zfjT`DZ4a`o#BOZSkmPO#_Txv%a@r>AE(?r6rXC+3X_Db%59VA}d@Eps3U(DYNfxq@ z>|>OMCkE;iwa8OZTyD!>VJg5NB&d|2JE0UR4diK>3N<;dr%-YUedt|$CTay2=lw4| z==H;M`fvFi!p7{eFq*<*G@mek^B6#e20RSYx)2sk*iuO2;L-hJxr$~{=Q2pJBuF}h zhXGexJ$!Pot4TDzovqfh<)TC88aUKiDA0+@D+4Cu#?x!ar8c}Ma=AT&0(t5V#0xCV zbye_&3QzYRRKe;+=RziG|C6!c%@RqG7C-{~7C*;B7RPcwh2_6kjtg+CLo>Enh3aejuSXJP~`=r354g z)$yFQ?hbCHQEBQTwT+viB>A;LTcd=OHxM@ty^QQQc8tdA)OGBqza4Ig0ahVnzKuJq zA7IDgcR`0}SIT4pqaV@IFNz?p^CCUzpSsKMch?bHAnYXmln~cjyXMF~x?4||x9l!O zh}?dJlfzYc>c@R`e>=OLuGtu?M&;Ygz{&X3ZN{(UYcjYIu6;BN^mBW>R3fEPhm_v1 g-9k60Ggx6>#wh9y44$j@{1(aBAJ##QgVF^60IbSSSpWb4 literal 0 HcmV?d00001 diff --git a/test/difftree.sh b/test/difftree.sh new file mode 100755 index 0000000..9a0ebf4 --- /dev/null +++ b/test/difftree.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +# The purpose of this test script is to determine if create-diff-object can +# properly recognize object file equivalence when passed the same file for both +# the original and patched objects. This verifies that create-diff-object is +# correctly parsing, correlating, and comparing the different elements of the +# object file. In practice, a situation similar to the test case occurs when a +# commonly included header file changes, causing Make to rebuild many objects +# that have no functional change. + +# This script requires a built kernel object tree to be in the kpatch cache +# directory at $HOME/.kpatch/obj + +#set -x + +OBJDIR="$HOME/.kpatch/obj" +# shellcheck disable=SC2046 +SCRIPTDIR=$(readlink -f $(dirname $(type -p "$0"))) +TEMPDIR=$(mktemp -d) +RESULTSDIR="$TEMPDIR/results" + +if [[ ! -d $OBJDIR ]]; then + echo "please run kpatch-build to populate the object tree in $OBJDIR" +fi + +cd "$OBJDIR" || exit 1 +# shellcheck disable=SC2044 +for i in $(find ./* -name '*.o') +do + # copied from kpatch-build/kpatch-gcc; keep in sync + case $i in + *.mod.o|\ + *built-in.o|\ + *built-in.a|\ + vmlinux.o|\ + .tmp_kallsyms1.o|\ + .tmp_kallsyms2.o|\ + init/version.o|\ + arch/x86/boot/version.o|\ + arch/x86/boot/compressed/eboot.o|\ + arch/x86/boot/header.o|\ + arch/x86/boot/compressed/efi_stub_64.o|\ + arch/x86/boot/compressed/piggy.o|\ + kernel/system_certificates.o|\ + .*.o) + continue + ;; + esac + # skip objects that are the linked product of more than one object file + [[ $(readelf -s "$i" | awk '$4=="FILE" {n++} END {print n}') -ne 1 ]] && continue + "$SCRIPTDIR"/../kpatch-build/create-diff-object "$i" "$i" "/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" "$TEMPDIR/output.o" > "$TEMPDIR/log.txt" 2>&1 + RETCODE=$? + # expect RETCODE to be 3 indicating no change + [[ $RETCODE -eq 3 ]] && continue + # otherwise record error + # shellcheck disable=SC2046 + mkdir -p "$RESULTSDIR"/$(dirname "$i") || exit 1 + cp "$i" "$RESULTSDIR/$i" || exit 1 + case $RETCODE in + 139) + echo "$i: segfault" | tee + if [[ ! -e core ]]; then + echo "no corefile, run "ulimit -c unlimited" to capture corefile" + else + mv core "$RESULTSDIR/$i.core" || exit 1 + fi + ;; + 0) + echo "$i: incorrectly detected change" + mv "$TEMPDIR/log.txt" "$RESULTSDIR/$i.log" || exit 1 + ;; + 1|2) + echo "$i: error code $RETCODE" + mv "$TEMPDIR/log.txt" "$RESULTSDIR/$i.log" || exit 1 + ;; + *) + exit 1 # script error + ;; + esac +done +rm -f "$TEMPDIR/log.txt" > /dev/null 2>&1 + +# try to group the errors together in some meaningful way +cd "$RESULTSDIR" || exit 1 +echo "" +echo "Results:" +# shellcheck disable=SC2044 +for i in $(find ./* -iname '*.log') +do + head -1 "$i" | cut -f2-3 -d':' +done | sort | uniq -c | sort -n -r | tee "$TEMPDIR/results.log" + +echo "results are in $TEMPDIR" diff --git a/test/integration/.gitignore b/test/integration/.gitignore new file mode 100644 index 0000000..42aff9e --- /dev/null +++ b/test/integration/.gitignore @@ -0,0 +1,2 @@ +test.log +COMBINED.patch diff --git a/test/integration/Makefile b/test/integration/Makefile new file mode 100644 index 0000000..e18d8d0 --- /dev/null +++ b/test/integration/Makefile @@ -0,0 +1,51 @@ +include /etc/os-release + +PATCH_DIR?=${ID}-${VERSION_ID} + +all: + $(error please specify local or remote) + +local: slow + +remote: remote_slow + +slow: clean + ./kpatch-test --kpatch-build-opts="$(KPATCH_BUILD_OPTS)" -d $(PATCH_DIR) $(PATCHES) + +quick: clean + ./kpatch-test --kpatch-build-opts="$(KPATCH_BUILD_OPTS)" -d $(PATCH_DIR) --quick $(PATCHES) + +cached: + ./kpatch-test --kpatch-build-opts="$(KPATCH_BUILD_OPTS)" -d $(PATCH_DIR) --cached $(PATCHES) + +vagrant: vagrant-quick + +vagrant-quick: + ./test-vagrant + +vagrant-slow: + ./test-vagrant --slow + +clean: + rm -f *.ko *.log COMBINED.patch + +check_host: +ifndef SSH_HOST + $(error SSH_HOST is undefined) +endif + +SSH_USER ?= root + +remote_setup: check_host + ssh $(SSH_USER)@$(SSH_HOST) exit + ssh $(SSH_USER)@$(SSH_HOST) "ls kpatch-setup &> /dev/null" || \ + (scp remote-setup $(SSH_USER)@$(SSH_HOST):kpatch-setup && \ + ssh $(SSH_USER)@$(SSH_HOST) "./kpatch-setup") + +remote_sync: remote_setup + ssh $(SSH_USER)@$(SSH_HOST) "rm -rf kpatch-test" + rsync -Cavz --include=core $(shell readlink -f ../../..) $(SSH_USER)@$(SSH_HOST):kpatch-test + ssh $(SSH_USER)@$(SSH_HOST) "cd kpatch-test/kpatch && make" + +remote_slow: remote_sync + ssh $(SSH_USER)@$(SSH_HOST) "cd kpatch-test/kpatch/test/integration && make slow" diff --git a/test/integration/centos-7 b/test/integration/centos-7 new file mode 120000 index 0000000..66d70af --- /dev/null +++ b/test/integration/centos-7 @@ -0,0 +1 @@ +rhel-7.8/ \ No newline at end of file diff --git a/test/integration/centos-8 b/test/integration/centos-8 new file mode 120000 index 0000000..64546c1 --- /dev/null +++ b/test/integration/centos-8 @@ -0,0 +1 @@ +rhel-8.2 \ No newline at end of file diff --git a/test/integration/common/multiple.template b/test/integration/common/multiple.template new file mode 100755 index 0000000..678279c --- /dev/null +++ b/test/integration/common/multiple.template @@ -0,0 +1,80 @@ +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" +ROOTDIR="$(readlink -f $SCRIPTDIR/../../..)" +KPATCH="sudo $ROOTDIR/kpatch/kpatch" + +MODULE_PREFIX="test-" +MODULE_POSTFIX=".ko" +TEST_POSTFIX="-LOADED.test" +PATCH_POSTFIX=".patch" +DISABLED_POSTFIX="${PATCH_POSTFIX}.disabled" + +set -o errexit + +declare -a loaded_modules + +cleanup_modules() { + for ((idx=${#loaded_modules[@]}-1 ; idx>=0 ; idx--)); do + mod=${loaded_modules[idx]} + $KPATCH unload $mod + done +} + +die_clean() { + cleanup_modules + exit 1 +} + +die() { + echo "ERROR: $@" >&2 + die_clean +} + +ko_to_test() { + tmp=${1%${MODULE_POSTFIX}}${TEST_POSTFIX} + echo ${tmp#${MODULE_PREFIX}} +} + +# make sure any modules added here are disjoint +declare -a modules + +for file in "${SCRIPTDIR}"/*"${TEST_POSTFIX}"; do + name=$(basename ${file}) + skip=0 + for bname in "${blacklist[@]}"; do + if [ "${bname}" == "${name}" ]; then + skip=1 + break + fi + done + if ! [ -e ${file/${TEST_POSTFIX}/${PATCH_POSTFIX}} ] && \ + [ -e ${file/${TEST_POSTFIX}/${DISABLED_POSTFIX}} ]; then + skip=1 + fi + if [ ${skip} -eq 0 ]; then + modules+=(${MODULE_PREFIX}${name%${TEST_POSTFIX}}${MODULE_POSTFIX}) + fi +done + +for mod in "${modules[@]}"; do + testprog=$(ko_to_test $mod) + $SCRIPTDIR/$testprog && die "$SCRIPTDIR/$testprog succeeded before loading any modules" +done + +for mod in "${modules[@]}"; do + $KPATCH load $mod || die_clean + loaded_modules+=($mod) +done + +for mod in "${modules[@]}"; do + testprog=$(ko_to_test $mod) + $SCRIPTDIR/$testprog || die "$SCRIPTDIR/$testprog failed after loading modules" +done + +cleanup_modules + +for mod in "${modules[@]}"; do + testprog=$(ko_to_test $mod) + $SCRIPTDIR/$testprog && die "$SCRIPTDIR/$testprog succeeded after unloading modules" +done + +exit 0 diff --git a/test/integration/fedora-27/README b/test/integration/fedora-27/README new file mode 100644 index 0000000..82dad91 --- /dev/null +++ b/test/integration/fedora-27/README @@ -0,0 +1 @@ +4.13.9-300.fc27.x86_64 diff --git a/test/integration/fedora-27/data-new-LOADED.test b/test/integration/fedora-27/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/fedora-27/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/fedora-27/data-new.patch b/test/integration/fedora-27/data-new.patch new file mode 100644 index 0000000..a205824 --- /dev/null +++ b/test/integration/fedora-27/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-11-17 15:58:41.113211972 -0500 ++++ src/fs/proc/meminfo.c 2017-11-17 15:58:58.554211972 -0500 +@@ -42,6 +42,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -153,6 +155,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/fedora-27/gcc-static-local-var-6.patch b/test/integration/fedora-27/gcc-static-local-var-6.patch new file mode 100644 index 0000000..0272159 --- /dev/null +++ b/test/integration/fedora-27/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index 9bf2604..026ac6c 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -109,6 +109,8 @@ static int nf_ip6_reroute(struct net *net, struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -122,6 +124,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/fedora-27/macro-callbacks.patch b/test/integration/fedora-27/macro-callbacks.patch new file mode 100644 index 0000000..569cc7c --- /dev/null +++ b/test/integration/fedora-27/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.old/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.old/drivers/input/joydev.c 2017-09-03 16:56:17.000000000 -0400 ++++ src/drivers/input/joydev.c 2018-03-22 16:32:40.963082354 -0400 +@@ -1010,3 +1010,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.old/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.old/drivers/input/misc/pcspkr.c 2018-03-22 16:29:27.716082354 -0400 ++++ src/drivers/input/misc/pcspkr.c 2018-03-22 16:32:40.963082354 -0400 +@@ -132,3 +132,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.old/fs/aio.c src/fs/aio.c +--- src.old/fs/aio.c 2017-09-03 16:56:17.000000000 -0400 ++++ src/fs/aio.c 2018-03-22 16:32:40.962082354 -0400 +@@ -46,6 +46,50 @@ + + #include "internal.h" + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled b/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled new file mode 100755 index 0000000..e2b647d --- /dev/null +++ b/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo && grep kpatch=1 /proc/cmdline diff --git a/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW.patch.disabled b/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW.patch.disabled new file mode 100644 index 0000000..a0a183b --- /dev/null +++ b/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW.patch.disabled @@ -0,0 +1,40 @@ +Disabled due to https://github.com/dynup/kpatch/issues/767 +--- +Index: src/fs/proc/cmdline.c +=================================================================== +--- src.orig/fs/proc/cmdline.c ++++ src/fs/proc/cmdline.c +@@ -7,7 +7,7 @@ + static int cmdline_proc_show(struct seq_file *m, void *v) + { + seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_puts(m, " kpatch=1\n"); + return 0; + } + +Index: src/fs/proc/meminfo.c +=================================================================== +--- src.orig/fs/proc/meminfo.c ++++ src/fs/proc/meminfo.c +@@ -120,7 +120,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + + #ifdef CONFIG_MEMORY_FAILURE + seq_printf(m, "HardwareCorrupted: %5lu kB\n", +Index: src/include/linux/kernel.h +=================================================================== +--- src.orig/include/linux/kernel.h ++++ src/include/linux/kernel.h +@@ -3,6 +3,7 @@ + #define _LINUX_KERNEL_H + + ++ + #include + #include + #include diff --git a/test/integration/fedora-27/module-call-external.patch b/test/integration/fedora-27/module-call-external.patch new file mode 100644 index 0000000..5dced50 --- /dev/null +++ b/test/integration/fedora-27/module-call-external.patch @@ -0,0 +1,33 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2017-11-17 15:58:26.667211972 -0500 ++++ src/fs/nfsd/export.c 2017-11-17 15:59:26.338211972 -0500 +@@ -1194,6 +1194,8 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1203,6 +1205,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2017-11-17 15:58:49.333211972 -0500 ++++ src/net/netlink/af_netlink.c 2017-11-17 15:59:26.338211972 -0500 +@@ -2739,4 +2739,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/fedora-27/module-shadow.patch.disabled b/test/integration/fedora-27/module-shadow.patch.disabled new file mode 100644 index 0000000..37c7997 --- /dev/null +++ b/test/integration/fedora-27/module-shadow.patch.disabled @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2017-11-17 15:58:19.369211972 -0500 ++++ src/arch/x86/kvm/vmx.c 2017-11-17 15:59:29.615211972 -0500 +@@ -11259,10 +11259,20 @@ static void vmx_leave_nested(struct kvm_ + * It should only be called before L2 actually succeeded to run, and when + * vmcs01 is current (it doesn't leave_guest_mode() or switch vmcss). + */ ++#include "kpatch.h" + static void nested_vmx_entry_failure(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12, + u32 reason, unsigned long qualification) + { ++ int *kpatch; ++ ++ kpatch = kpatch_shadow_alloc(vcpu, "kpatch", sizeof(*kpatch), ++ GFP_KERNEL); ++ if (kpatch) { ++ kpatch_shadow_get(vcpu, "kpatch"); ++ kpatch_shadow_free(vcpu, "kpatch"); ++ } ++ + load_vmcs12_host_state(vcpu, vmcs12); + vmcs12->vm_exit_reason = reason | VMX_EXIT_REASONS_FAILED_VMENTRY; + vmcs12->exit_qualification = qualification; diff --git a/test/integration/fedora-27/multiple.test b/test/integration/fedora-27/multiple.test new file mode 100755 index 0000000..a7ea608 --- /dev/null +++ b/test/integration/fedora-27/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-cmdline-rebuild-SLOW-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/fedora-27/new-function.patch b/test/integration/fedora-27/new-function.patch new file mode 100644 index 0000000..9ba590f --- /dev/null +++ b/test/integration/fedora-27/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2017-11-17 15:58:00.462211972 -0500 ++++ src/drivers/tty/n_tty.c 2017-11-17 15:59:31.240211972 -0500 +@@ -2269,7 +2269,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2356,6 +2356,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/fedora-27/new-globals.patch b/test/integration/fedora-27/new-globals.patch new file mode 100644 index 0000000..ef638e7 --- /dev/null +++ b/test/integration/fedora-27/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-11-17 15:58:41.126211972 -0500 ++++ src/fs/proc/cmdline.c 2017-11-17 15:59:32.886211972 -0500 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-11-17 15:59:24.724211972 -0500 ++++ src/fs/proc/meminfo.c 2017-11-17 15:59:32.887211972 -0500 +@@ -19,6 +19,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -65,6 +67,7 @@ static int meminfo_proc_show(struct seq_ + + available = si_mem_available(); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/fedora-27/remote-setup b/test/integration/fedora-27/remote-setup new file mode 100755 index 0000000..dfcb7ae --- /dev/null +++ b/test/integration/fedora-27/remote-setup @@ -0,0 +1,45 @@ +#!/bin/bash -x + +# install rpms on a Fedora 22 system to prepare it for kpatch integration tests + +set -o errexit + +[[ $UID != 0 ]] && sudo=sudo + +warn() { + echo "ERROR: $1" >&2 +} + +die() { + warn "$@" + exit 1 +} + +install_rpms() { + # crude workaround for a weird dnf bug where it fails to download + $sudo dnf install -y $* || $sudo dnf install -y $* +} + +install_rpms gcc elfutils elfutils-devel pesign openssl numactl-devel wget patchutils + +$sudo dnf builddep -y kernel || $sudo dnf builddep -y kernel + +# install kernel debuginfo and devel RPMs for target kernel +kverrel=$(uname -r) +kverrel=${kverrel%.x86_64} +kver=${kverrel%%-*} +krel=${kverrel#*-} +install_rpms https://kojipkgs.fedoraproject.org/packages/kernel/$kver/$krel/x86_64/kernel-debuginfo-$kver-$krel.x86_64.rpm https://kojipkgs.fedoraproject.org/packages/kernel/$kver/$krel/x86_64/kernel-debuginfo-common-x86_64-$kver-$krel.x86_64.rpm https://kojipkgs.fedoraproject.org/packages/kernel/$kver/$krel/x86_64/kernel-devel-$kver-$krel.x86_64.rpm + +# install version of gcc which was used to build the target kernel +gccver=$(gcc --version |head -n1 |cut -d' ' -f3-) +kgccver=$(readelf -p .comment /usr/lib/debug/lib/modules/$(uname -r)/vmlinux |grep GCC: | tr -s ' ' | cut -d ' ' -f6-) +if [[ $gccver != $kgccver ]]; then + gver=$(echo $kgccver | awk '{print $1}') + grel=$(echo $kgccver | sed 's/.*-\(.*\))/\1/') + grel=$grel.$(rpm -q gcc |sed 's/.*\.\(.*\)\.x86_64/\1/') + install_rpms https://kojipkgs.fedoraproject.org/packages/gcc/$gver/$grel/x86_64/cpp-$gver-$grel.x86_64.rpm https://kojipkgs.fedoraproject.org/packages/gcc/$gver/$grel/x86_64/gcc-$gver-$grel.x86_64.rpm https://kojipkgs.fedoraproject.org/packages/gcc/$gver/$grel/x86_64/libgomp-$gver-$grel.x86_64.rpm +fi + +install_rpms ccache +ccache -M 5G diff --git a/test/integration/fedora-27/shadow-newpid-LOADED.test b/test/integration/fedora-27/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/fedora-27/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/fedora-27/shadow-newpid.patch.disabled b/test/integration/fedora-27/shadow-newpid.patch.disabled new file mode 100644 index 0000000..1a2c1b6 --- /dev/null +++ b/test/integration/fedora-27/shadow-newpid.patch.disabled @@ -0,0 +1,78 @@ +Index: src/fs/proc/array.c +=================================================================== +--- src.orig/fs/proc/array.c ++++ src/fs/proc/array.c +@@ -363,12 +363,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include "kpatch.h" + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = kpatch_shadow_get(p, "newpid"); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +Index: src/kernel/exit.c +=================================================================== +--- src.orig/kernel/exit.c ++++ src/kernel/exit.c +@@ -760,6 +760,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include "kpatch.h" + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -865,6 +866,8 @@ void __noreturn do_exit(long code) + exit_task_work(tsk); + exit_thread(tsk); + ++ kpatch_shadow_free(tsk, "newpid"); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +Index: src/kernel/fork.c +=================================================================== +--- src.orig/kernel/fork.c ++++ src/kernel/fork.c +@@ -2062,6 +2062,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include "kpatch.h" + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2074,6 +2075,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2100,6 +2103,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = kpatch_shadow_alloc(p, "newpid", sizeof(*newpid), ++ GFP_KERNEL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/fedora-27/warn-detect-FAIL.patch b/test/integration/fedora-27/warn-detect-FAIL.patch new file mode 100644 index 0000000..b178639 --- /dev/null +++ b/test/integration/fedora-27/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +diff --git a/net/core/dev.c b/net/core/dev.c +index ef0cc6ea5f8d..9a840ec54270 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1,3 +1,4 @@ ++ + /* + * NET3 Protocol independent device support routines. + * diff --git a/test/integration/kpatch-test b/test/integration/kpatch-test new file mode 100755 index 0000000..688ab1d --- /dev/null +++ b/test/integration/kpatch-test @@ -0,0 +1,398 @@ +#!/bin/bash +# +# kpatch integration test framework +# +# Copyright (C) 2014 Josh Poimboeuf +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, +# 02110-1301, USA. +# +# +# This is a basic integration test framework for kpatch, which tests building, +# loading, and unloading patches, as well as any other related custom tests. +# +# This script looks for test input files in the current directory. It expects +# certain file naming conventions: +# +# - foo.patch: patch that should build successfully +# +# - bar-FAIL.patch: patch that should fail to build +# +# - foo-LOADED.test: executable which tests whether the foo.patch module is +# loaded. It will be used to test that loading/unloading the patch module +# works as expected. +# +# Any other *.test files will be executed after all the patch modules have been +# built from the *.patch files. They can be used for more custom tests above +# and beyond the simple loading and unloading tests. + +shopt -s nullglob + +# shellcheck disable=SC2046 +SCRIPTDIR=$(readlink -f $(dirname $(type -p "$0"))) +ROOTDIR=$(readlink -f "$SCRIPTDIR/../..") +KPATCH="sudo $ROOTDIR/kpatch/kpatch" +unset CCACHE_HASHDIR +KPATCHBUILD="$ROOTDIR"/kpatch-build/kpatch-build +ERROR=0 +LOG=test.log +DYNDEBUG_CONTROL=/sys/kernel/debug/dynamic_debug/control +DYNDEBUG_ENABLED=1 +ARCHVERSION="$(uname -r)" +rm -f ./*.log + +PATCHDIR="${PATCHDIR:-$PWD}" +declare -a PATCH_LIST +declare -a TEST_LIST + +usage() { + echo "usage: $0 [options] [patch1 ... patchN]" >&2 + echo " patchN Pathnames of patches to test" >&2 + echo " -h, --help Show this help message" >&2 + echo " -c, --cached Don't rebuild patch modules" >&2 + echo " -d, --directory Patch directory" >&2 + echo " -q, --quick Test combined patch and -FAIL patches only" >&2 + echo " --system-kpatch-tools Use kpatch tools installed in the system" >&2 + echo " --kpatch-build-opts Additional options to pass to kpatch-build" >&2 +} + +options=$(getopt -o hcd:q -l "help,cached,directory,quick,system-kpatch-tools,kpatch-build-opts:" -- "$@") || exit 1 + +eval set -- "$options" + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -c|--cached) + SKIPBUILD=1 + ;; + -d|--directory) + PATCHDIR="$2" + shift + ;; + -q|--quick) + QUICK=1 + ;; + --system-kpatch-tools) + KPATCH="sudo kpatch" + KPATCHBUILD="kpatch-build" + ;; + --kpatch-build-opts) + KPATCHBUILD_OPTS=$2 + shift + ;; + *) + [[ "$1" = "--" ]] && shift && continue + PATCH_LIST+=("$1") + ;; + esac + shift +done + +if [[ ${#PATCH_LIST[@]} = 0 ]]; then + PATCH_LIST=("$PATCHDIR"/*.patch) + TEST_LIST=("$PATCHDIR"/*.test) + if [[ ${#PATCH_LIST[@]} = 0 ]]; then + echo "No patches found!" + exit 1 + fi +else + for file in "${PATCH_LIST[@]}"; do + prefix=${file%%.patch} + [[ -e "$prefix-FAIL.test" ]] && TEST_LIST+=("$prefix-FAIL.test") + [[ -e "$prefix-LOADED.test" ]] && TEST_LIST+=("$prefix-LOADED.test") + done +fi + +error() { + echo "ERROR: $*" |tee -a $LOG >&2 + ERROR=$((ERROR + 1)) +} + +log() { + echo "$@" |tee -a $LOG +} + +unload_all() { + $KPATCH unload --all +} + +build_module() { + file=$1 + prefix=$(basename "${file%%.patch}") + modname="test-$prefix" + module="${modname}.ko" + + if [[ $prefix =~ -FAIL ]]; then + shouldfail=1 + else + shouldfail=0 + fi + + if [[ $SKIPBUILD -eq 1 ]]; then + skip=0 + [[ $shouldfail -eq 1 ]] && skip=1 + [[ -e $module ]] && skip=1 + [[ $skip -eq 1 ]] && log "skipping build: $prefix" && return + fi + + log "build: $prefix" + + # shellcheck disable=SC2086 + # KPATCHBUILD_OPTS may contain several space-separated options, + # it should remain without quotes. + if ! $KPATCHBUILD $KPATCHBUILD_OPTS -n "$modname" "$file" >> $LOG 2>&1; then + if [[ $shouldfail -eq 0 ]]; then + error "$prefix: build failed" + cp "$HOME/.kpatch/build.log" "$prefix.log" + fi + else + [[ $shouldfail -eq 1 ]] && error "$prefix: build succeeded when it should have failed" + fi +} + +run_load_test() { + file=$1 + prefix=$(basename "${file%%.patch}") + modname="test-$prefix" + module="${modname}.ko" + testprog=$(dirname "$1")/"$prefix-LOADED.test" + + [[ $prefix =~ -FAIL ]] && return + + if [[ ! -e $module ]]; then + log "can't find $module, skipping" + return + fi + + if [[ -e $testprog ]]; then + log "load test: $prefix" + else + log "load test: $prefix (no test prog)" + fi + + + if [[ -e $testprog ]] && $testprog >> $LOG 2>&1; then + error "$prefix: $testprog succeeded before kpatch load" + return + fi + + if ! $KPATCH load "$module" >> $LOG 2>&1; then + error "$prefix: kpatch load failed" + return + fi + + if [[ -e $testprog ]] && ! $testprog >> $LOG 2>&1; then + error "$prefix: $testprog failed after kpatch load" + fi + + if ! $KPATCH unload "$module" >> $LOG 2>&1; then + error "$prefix: kpatch unload failed" + return + fi + + if [[ -e $testprog ]] && $testprog >> $LOG 2>&1; then + error "$prefix: $testprog succeeded after kpatch unload" + return + fi +} + +run_custom_test() { + testprog=$1 + prefix=$(basename "${testprog%%.test}") + + [[ $testprog = *-LOADED.test ]] && return + + log "custom test: $prefix" + + if ! $testprog >> $LOG 2>&1; then + error "$prefix: test failed" + fi +} + +build_combined_module() { + + if [[ $SKIPBUILD -eq 1 ]] && [[ -e test-COMBINED.ko ]]; then + log "skipping build: combined" + return + fi + + declare -a COMBINED_LIST + for file in "${PATCH_LIST[@]}"; do + [[ $file =~ -FAIL ]] && log "combine: skipping $file" && continue + COMBINED_LIST+=("$file") + done + if [[ ${#COMBINED_LIST[@]} -le 1 ]]; then + log "skipping build: combined (only ${#PATCH_LIST[@]} patch(es))" + return + fi + + log "build: combined module" + + # shellcheck disable=SC2086 + if ! $KPATCHBUILD $KPATCHBUILD_OPTS -n test-COMBINED "${COMBINED_LIST[@]}" >> $LOG 2>&1; then + error "combined build failed" + cp "$HOME/.kpatch/build.log" combined.log + fi +} + +run_combined_test() { + if [[ ! -e test-COMBINED.ko ]]; then + log "can't find test-COMBINED.ko, skipping" + return + fi + + log "load test: combined module" + + unload_all + + for testprog in "${TEST_LIST[@]}"; do + [[ $testprog != *-LOADED.test ]] && continue + if $testprog >> $LOG 2>&1; then + error "combined: $testprog succeeded before kpatch load" + return + fi + done + + if ! $KPATCH load test-COMBINED.ko >> $LOG 2>&1; then + error "combined: kpatch load failed" + return + fi + + for testprog in "${TEST_LIST[@]}"; do + [[ $testprog != *-LOADED.test ]] && continue + [ -e "${testprog/-LOADED.test/.patch.disabled}" ] && continue + if ! $testprog >> $LOG 2>&1; then + error "combined: $testprog failed after kpatch load" + fi + done + + if ! $KPATCH unload test-COMBINED.ko >> $LOG 2>&1; then + error "combined: kpatch unload failed" + return + fi + + for testprog in "${TEST_LIST[@]}"; do + [[ $testprog != *-LOADED.test ]] && continue + if $testprog >> $LOG 2>&1; then + error "combined: $testprog succeeded after kpatch unload" + return + fi + done + +} + +# save existing dmesg so we can detect new content +save_dmesg() { + SAVED_DMESG="$(dmesg | tail -n1)" +} + +# new dmesg entries since our saved entry +new_dmesg() { + if ! dmesg | awk -v last="$SAVED_DMESG" 'p; $0 == last{p=1} END {exit !p}'; then + error "dmesg overflow, try increasing kernel log buffer size" + fi +} + +kernel_version_gte() { + [ "${ARCHVERSION//-*/}" = "$(echo -e "${ARCHVERSION//-*}\\n$1" | sort -rV | head -n1)" ] +} + +support_klp_replace() +{ + if kernel_is_rhel; then + rhel_kernel_version_gte 4.18.0-193.el8 + else + kernel_version_gte 5.1.0 + fi +} + +kernel_is_rhel() { + [[ "$ARCHVERSION" =~ \.el[789] ]] +} + +rhel_kernel_version_gte() { + [ "${ARCHVERSION}" = "$(echo -e "${ARCHVERSION}\\n$1" | sort -rV | head -n1)" ] +} + +# shellcheck disable=SC1091 +source /etc/os-release +if [[ "${ID}" == "rhel" && "${VERSION_ID%%.*}" == "7" && "${VERSION_ID##*.}" -le "6" ]]; then + DYNDEBUG_ENABLED=0 + echo "Dynamic debug is not supported on '${PRETTY_NAME}', disabling." +fi + +if ! support_klp_replace ; then + KPATCHBUILD_OPTS="$KPATCHBUILD_OPTS -R" + echo "KLP replace is not supported on '${PRETTY_NAME}', disabling." +fi + +for file in "${PATCH_LIST[@]}"; do + if [[ $QUICK != 1 || "$file" =~ -FAIL ]]; then + build_module "$file" + fi +done + +build_combined_module + +unload_all + +save_dmesg + +if [ "${DYNDEBUG_ENABLED}" == "1" ]; then + prev_dyndebug=$(sudo sh -c "grep klp_try_switch_task ${DYNDEBUG_CONTROL}" | awk '{print $3;}') + sudo sh -c "echo 'func klp_try_switch_task +p' > ${DYNDEBUG_CONTROL} 2>/dev/null" +fi + +if [[ $QUICK != 1 ]]; then + for file in "${PATCH_LIST[@]}"; do + run_load_test "$file" + done +fi + +run_combined_test + +if [[ $QUICK != 1 ]]; then + for testprog in "${TEST_LIST[@]}"; do + if [[ ! $testprog =~ -FAIL ]]; then + unload_all + run_custom_test "$testprog" + fi + done +fi + + +unload_all + +if [ "${DYNDEBUG_ENABLED}" == "1" ]; then + sudo sh -c "echo \"func klp_try_switch_task ${prev_dyndebug}\" > ${DYNDEBUG_CONTROL} 2>/dev/null" +fi + +if new_dmesg | grep -q "Call Trace"; then + new_dmesg > dmesg.log + error "kernel error detected in printk buffer" +fi + +if [[ $ERROR -gt 0 ]]; then + log "$ERROR errors encountered" + echo "see test.log for more information" +else + log "SUCCESS" +fi + +exit $ERROR diff --git a/test/integration/lib.sh b/test/integration/lib.sh new file mode 100644 index 0000000..4ab2c92 --- /dev/null +++ b/test/integration/lib.sh @@ -0,0 +1,316 @@ +#!/bin/bash + +kpatch_set_ccache_max_size() +{ + local ccache_max_size=${1:-10G} + + ccache --max-size="${ccache_max_size}" +} + +kpatch_ubuntu_dependencies() +{ + sudo sed -i 's/# deb-src/deb-src/' /etc/apt/sources.list + sudo apt-get update + + sudo apt-get install -y make gcc libelf-dev elfutils + sudo apt-get install -y dpkg-dev devscripts + sudo apt-get build-dep -y linux + + sudo apt-get install -y ccache + + # Add ddebs repository + if ! grep -q 'ddebs.ubuntu.com' /etc/apt/sources.list.d/ddebs.list; then + local codename + codename=$(lsb_release -sc) + sudo tee /etc/apt/sources.list.d/ddebs.list <<-EOF + deb http://ddebs.ubuntu.com/ ${codename} main restricted universe multiverse + deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse + deb http://ddebs.ubuntu.com/ ${codename}-updates main restricted universe multiverse + deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse + EOF + + # add APT key + wget -Nq http://ddebs.ubuntu.com/dbgsym-release-key.asc -O- | sudo apt-key add - + sudo apt-get update + fi + sudo apt-get install -y "linux-image-$(uname -r)-dbgsym" +} + +kpatch_rhel_dependencies() +{ + local kernel_version + local arch + local rhel_major + local yum_utils_version + kernel_version=$(uname -r) + arch=$(uname -m) + rhel_major=${VERSION_ID%%.*} + + # kpatch-build dependencies + sudo yum install -y \ + elfutils \ + elfutils-devel \ + gcc \ + gcc-c++ \ + git \ + "kernel-devel-${kernel_version%.*}" \ + rpm-build \ + wget \ + yum-utils + sudo debuginfo-install -y "kernel-${kernel_version%.*}" + [[ "$arch" == "ppc64le" ]] && sudo yum install -y gcc-plugin-devel + + # kernel dependencies + yum_utils_version=$(rpm -q --queryformat="%{version}" yum-utils) + if [[ "${yum_utils_version}" = "$(echo -e "${yum_utils_version}\\n4.0.12" | sort -rV | head -n1)" ]]; then + sudo yum-builddep -y --skip-unavailable "kernel-${kernel_version%.*}" + else + sudo yum-builddep -y "kernel-${kernel_version%.*}" + fi + [[ "$arch" == "x86_64" ]] && sudo yum install -y pesign + + # ccache + if ! command -v ccache &> /dev/null; then + if ! sudo yum install -y ccache; then + sudo yum install -y "https://dl.fedoraproject.org/pub/epel/epel-release-latest-${rhel_major}.noarch.rpm" && \ + sudo yum install -y ccache && \ + sudo yum remove -y epel-release + fi + fi +} + +kpatch_centos_dependencies() +{ + kpatch_rhel_dependencies +} + +kpatch_fedora_dependencies() +{ + kpatch_rhel_dependencies +} + +kpatch_openEuler_dependencies() +{ + local kernel_version + local arch + kernel_version=$(uname -r) + arch=$(uname -m) + + sudo yum install -y make gcc patch bison flex openssl-devel dwarves \ + rpm-build dnf-plugins-core python3-devel openssl-devel ncurses-devel elfutils-libelf-devel + sudo yum install -y "kernel-source-${kernel_version%.*}" \ + "kernel-debuginfo-${kernel_version%.*}" "kernel-devel-${kernel_version%.*}" +} + +kpatch_dependencies() +{ + # shellcheck disable=SC1091 + source /etc/os-release + + eval "kpatch_${ID}_dependencies" || { echo "Unsupported distro: ${ID}"; exit 1; } +} + +kpatch_separate_partition_cache() +{ + local partition=${1} + local mountpoint=${2} + local reformat=${3} + local owner=${USER} + + if [[ "${reformat}" == "y" ]]; then + sudo mkfs.xfs -f "${partition}" + fi + + sudo mkdir -p "${mountpoint}" + sudo mount "${partition}" "${mountpoint}" + sudo chown "${owner}":"${owner}" "${mountpoint}" + + rm -rf "${mountpoint}/.ccache" + rm -rf "${mountpoint}/.kpatch" + mkdir "${mountpoint}/.ccache" + mkdir "${mountpoint}/.kpatch" + + rm -rf "${HOME}/.ccache" + rm -rf "${HOME}/.kpatch" + + ln -sv "${mountpoint}/.ccache" "${HOME}/.ccache" + ln -sv "${mountpoint}/.kpatch" "${HOME}/.kpatch" +} + +kpatch_separate_disk_cache() +{ + local device=${1} + local mountpoint=${2} + local partition="${device}1" + + echo -e "o\\nn\\np\\n1\\n\\n\\nw\\n" | sudo fdisk "${device}" + kpatch_separate_partition_cache "${partition}" "${mountpoint}" y +} + +kpatch_install_vagrant_centos() +{ + local image_path=${1} + + sudo yum group install -y "Development Tools" + sudo yum -y install qemu-kvm libvirt virt-install bridge-utils libvirt-devel libxslt-devel libxml2-devel libvirt-devel libguestfs-tools-c libvirt-client + + echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ipforward.conf + sudo sysctl -p /etc/sysctl.d/99-ipforward.conf + + sudo systemctl enable libvirtd + sudo systemctl start libvirtd || exit 1 + + if [[ -n "${image_path}" ]]; then + mkdir -p "${image_path}/libvirt/images" + virsh pool-define-as --target "${image_path}/libvirt/images" default dir || exit 1 + virsh pool-start default || exit 1 + fi + + sudo yum install -y https://releases.hashicorp.com/vagrant/2.1.2/vagrant_2.1.2_x86_64.rpm || exit 1 + + vagrant plugin install vagrant-libvirt +} + +kpatch_install_vagrant_rhel() +{ + local image_path=${1} + + kpatch_install_vagrant_centos "${image_path}" + + sudo systemctl enable nfs + sudo systemctl start nfs || exit 1 + +} + +kpatch_install_vagrant_fedora() +{ + local image_path=${1} + + echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ipforward.conf + sudo sysctl -p /etc/sysctl.d/99-ipforward.conf + + sudo dnf install -y libvirt virt-install libvirt-client nfs-utils vagrant vagrant-libvirt + + echo "[nfsd]" | sudo tee -a /etc/nfs.conf + echo "udp=y" | sudo tee -a /etc/nfs.conf + echo "vers3=y" | sudo tee -a /etc/nfs.conf + sudo systemctl restart nfs + + sudo systemctl enable libvirtd + sudo systemctl start libvirtd || exit 1 + + if [[ -n "${image_path}" ]]; then + mkdir -p "${image_path}/libvirt/images" + virsh pool-define-as --target "${image_path}/libvirt/images" default dir || exit 1 + virsh pool-start default || exit 1 + fi +} + +kpatch_install_vagrant() +{ + local image_path=${1} + + # shellcheck disable=SC1091 + source /etc/os-release + + eval "kpatch_install_vagrant_${ID} ${image_path}" || { echo "Unsupported distro: ${ID}"; exit 1; } +} + +kpatch_check_install_vagrant() +{ + local image_path=${1} + # shellcheck disable=SC2230 + [ "$(which vagrant)" == "" ] && kpatch_install_vagrant "${image_path}" + return 0 +} + +kpatch_write_vagrantfile_template() +{ + local target_distro=${1} + + local box_prefix="kpatch" + + cat >Vagrantfile < '40G' + libvirt.cpus = $(getconf _NPROCESSORS_ONLN) + libvirt.memory = $(awk '/MemTotal/ {printf("%d\n", ($2*0.8)/1024)}' /proc/meminfo) + libvirt.graphics_type = "none" + libvirt.disk_bus = 'virtio' + libvirt.disk_device = 'vda' + end + config.vm.box = "${box_prefix}/${target_distro}" + config.vm.synced_folder ".", "/vagrant", type: "nfs" +EOF +} + +kpatch_write_vagrantfile_centos_provision() +{ + cat >>Vagrantfile <>Vagrantfile +} + +kpatch_integration_tests_vagrant_distro() +{ + local target_distro=${1} + local test_script=${2} + local slowtest=${3} + + local testdir + local workdir + local logdir + + testdir="$(pwd)" + workdir="${target_distro}.vagrant" + rm -rf "${workdir}" + mkdir -p "${workdir}" + cd "${workdir}" || exit 1 + + kpatch_write_vagrantfile "${target_distro}" + + vagrant up || { vagrant destroy -f; exit 1; } + + local test_cmd="KPATCH_GIT=${KPATCH_GIT} KPATCH_REV=${KPATCH_REV} bash /vagrant/runtest.sh" + if [ "${slowtest}" == "1" ]; then + test_cmd="${test_cmd} --slow" + fi + + cp "${test_script}" ./runtest.sh + vagrant ssh -c "${test_cmd}" + local rc=$? + + if [ $rc -eq 0 ]; then + echo "${target_distro} PASS" + else + echo "${target_distro} FAIL" + fi + + logdir="${testdir}/${target_distro}_log" + rm -rf "${logdir}" + mkdir -p "${logdir}" + cp logs/* "${logdir}" + + vagrant destroy -f + + cd "${testdir}" || exit 1 + if [ $rc -eq 0 ]; then + rm -rf "${workdir}" + fi + + return "${rc}" +} diff --git a/test/integration/linux-5.10.11/data-new-LOADED.test b/test/integration/linux-5.10.11/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/linux-5.10.11/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/linux-5.10.11/data-new.patch b/test/integration/linux-5.10.11/data-new.patch new file mode 100644 index 0000000..927d088 --- /dev/null +++ b/test/integration/linux-5.10.11/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2021-01-28 04:47:10.916473090 -0500 ++++ src/fs/proc/meminfo.c 2021-01-28 04:47:11.459467821 -0500 +@@ -29,6 +29,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -139,6 +141,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "FilePmdMapped: ", + global_node_page_state(NR_FILE_PMDMAPPED) * HPAGE_PMD_NR); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + #ifdef CONFIG_CMA + show_val_kb(m, "CmaTotal: ", totalcma_pages); diff --git a/test/integration/linux-5.10.11/gcc-static-local-var-6.patch b/test/integration/linux-5.10.11/gcc-static-local-var-6.patch new file mode 100644 index 0000000..8254b6c --- /dev/null +++ b/test/integration/linux-5.10.11/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr linux-5.10.11.bak/net/ipv6/netfilter.c linux-5.10.11/net/ipv6/netfilter.c +--- linux-5.10.11.bak/net/ipv6/netfilter.c 2021-01-28 08:18:59.575109041 -0500 ++++ linux-5.10.11/net/ipv6/netfilter.c 2021-01-28 08:20:52.399053360 -0500 +@@ -89,6 +89,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + int __nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -102,6 +104,9 @@ int __nf_ip6_route(struct net *net, stru + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/linux-5.10.11/macro-callbacks.patch b/test/integration/linux-5.10.11/macro-callbacks.patch new file mode 100644 index 0000000..735ed9f --- /dev/null +++ b/test/integration/linux-5.10.11/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2021-01-28 04:45:56.883192829 -0500 ++++ src/drivers/input/joydev.c 2021-01-28 04:51:13.998114373 -0500 +@@ -1084,3 +1084,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2021-01-28 04:45:56.892192742 -0500 ++++ src/drivers/input/misc/pcspkr.c 2021-01-28 04:51:14.086113519 -0500 +@@ -133,3 +133,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2021-01-28 04:47:10.885473391 -0500 ++++ src/fs/aio.c 2021-01-28 04:51:14.115113237 -0500 +@@ -51,6 +51,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/linux-5.10.11/module-call-external.patch b/test/integration/linux-5.10.11/module-call-external.patch new file mode 100644 index 0000000..ee84137 --- /dev/null +++ b/test/integration/linux-5.10.11/module-call-external.patch @@ -0,0 +1,36 @@ +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index 21e404e7cb68..3a282338d6d9 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1234,6 +1234,9 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++extern char *kpatch_string(void); ++ ++__attribute__((optimize("-fno-optimize-sibling-calls"))) + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1243,6 +1246,7 @@ static int e_show(struct seq_file *m, void *p) + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index daca50d6bb12..a657a0e073f8 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2898,4 +2898,9 @@ static int __init netlink_proto_init(void) + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/linux-5.10.11/multiple.test b/test/integration/linux-5.10.11/multiple.test new file mode 100755 index 0000000..a7ea608 --- /dev/null +++ b/test/integration/linux-5.10.11/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-cmdline-rebuild-SLOW-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/linux-5.10.11/new-function.patch b/test/integration/linux-5.10.11/new-function.patch new file mode 100644 index 0000000..1b0e8a0 --- /dev/null +++ b/test/integration/linux-5.10.11/new-function.patch @@ -0,0 +1,27 @@ +diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c +index c2869489ba68..7a46fc7a88f1 100644 +--- a/drivers/tty/n_tty.c ++++ b/drivers/tty/n_tty.c +@@ -2295,7 +2295,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2382,6 +2382,13 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, + return (b - buf) ? b - buf : retval; + } + ++__attribute__((optimize("-fno-optimize-sibling-calls"))) ++static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/linux-5.10.11/new-globals.patch b/test/integration/linux-5.10.11/new-globals.patch new file mode 100644 index 0000000..18a234e --- /dev/null +++ b/test/integration/linux-5.10.11/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2021-01-28 04:47:10.915473099 -0500 ++++ src/fs/proc/cmdline.c 2021-01-28 05:04:23.106898578 -0500 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2021-01-28 04:47:10.916473090 -0500 ++++ src/fs/proc/meminfo.c 2021-01-28 05:04:23.141898268 -0500 +@@ -19,6 +19,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -55,6 +57,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B); + sunreclaim = global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/linux-5.10.11/syscall-LOADED.test b/test/integration/linux-5.10.11/syscall-LOADED.test new file mode 100755 index 0000000..3a2fd88 --- /dev/null +++ b/test/integration/linux-5.10.11/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/linux-5.10.11/syscall.patch b/test/integration/linux-5.10.11/syscall.patch new file mode 100644 index 0000000..f86d695 --- /dev/null +++ b/test/integration/linux-5.10.11/syscall.patch @@ -0,0 +1,21 @@ +diff --git a/kernel/sys.c b/kernel/sys.c +index 2e2e3f378d97..05271fe26c89 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1250,13 +1250,15 @@ static int override_release(char __user *release, size_t len) + return ret; + } + +-SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) ++#include "kpatch-syscall.h" ++KPATCH_SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + { + struct new_utsname tmp; + + down_read(&uts_sem); + memcpy(&tmp, utsname(), sizeof(tmp)); + up_read(&uts_sem); ++ strcat(tmp.sysname, ".kpatch"); + if (copy_to_user(name, &tmp, sizeof(tmp))) + return -EFAULT; + diff --git a/test/integration/linux-5.10.11/warn-detect-FAIL.patch b/test/integration/linux-5.10.11/warn-detect-FAIL.patch new file mode 100644 index 0000000..940590f --- /dev/null +++ b/test/integration/linux-5.10.11/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +diff -Nupr linux-5.10.11.bak/net/core/dev.c linux-5.10.11/net/core/dev.c +--- linux-5.10.11.bak/net/core/dev.c 2021-01-28 08:18:59.936105663 -0500 ++++ linux-5.10.11/net/core/dev.c 2021-01-28 08:34:03.120655935 -0500 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-or-later ++ + /* + * NET3 Protocol independent device support routines. + * diff --git a/test/integration/linux-5.18.0/data-new-LOADED.test b/test/integration/linux-5.18.0/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/linux-5.18.0/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/linux-5.18.0/data-new.patch b/test/integration/linux-5.18.0/data-new.patch new file mode 100644 index 0000000..27df4ac --- /dev/null +++ b/test/integration/linux-5.18.0/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-06-15 10:04:10.540915757 -0400 ++++ src/fs/proc/meminfo.c 2022-06-15 10:04:12.104923244 -0400 +@@ -29,6 +29,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -145,6 +147,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/linux-5.18.0/gcc-static-local-var-6.patch b/test/integration/linux-5.18.0/gcc-static-local-var-6.patch new file mode 100644 index 0000000..896dfd7 --- /dev/null +++ b/test/integration/linux-5.18.0/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2022-06-15 10:04:10.984917882 -0400 ++++ src/net/ipv6/netfilter.c 2022-06-15 10:04:25.898989277 -0400 +@@ -97,6 +97,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + int __nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -110,6 +112,9 @@ int __nf_ip6_route(struct net *net, stru + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/linux-5.18.0/macro-callbacks.patch b/test/integration/linux-5.18.0/macro-callbacks.patch new file mode 100644 index 0000000..0a72181 --- /dev/null +++ b/test/integration/linux-5.18.0/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2022-06-15 10:04:09.640911448 -0400 ++++ src/drivers/input/joydev.c 2022-06-15 10:04:37.199043370 -0400 +@@ -1096,3 +1096,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2022-06-15 10:04:09.649911492 -0400 ++++ src/drivers/input/misc/pcspkr.c 2022-06-15 10:04:37.200043375 -0400 +@@ -134,3 +134,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2022-06-15 10:04:10.537915743 -0400 ++++ src/fs/aio.c 2022-06-15 10:04:37.201043380 -0400 +@@ -50,6 +50,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/linux-5.18.0/module-LOADED.test b/test/integration/linux-5.18.0/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/linux-5.18.0/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/linux-5.18.0/module.patch b/test/integration/linux-5.18.0/module.patch new file mode 100644 index 0000000..af01b1c --- /dev/null +++ b/test/integration/linux-5.18.0/module.patch @@ -0,0 +1,79 @@ +kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf + +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2022-06-15 10:04:10.582915958 -0400 ++++ src/fs/nfsd/export.c 2022-06-15 10:04:48.309096555 -0400 +@@ -1294,6 +1294,10 @@ static void exp_flags(struct seq_file *m + } + } + ++#include ++extern char *kpatch_string(void); ++ ++__attribute__((optimize("-fno-optimize-sibling-calls"))) + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1301,12 +1305,36 @@ static int e_show(struct seq_file *m, vo + struct cache_detail *cd = m->private; + bool export_stats = is_export_stats_file(m); + ++#ifdef CONFIG_X86_64 ++ alternative("ud2", "call single_task_running", X86_FEATURE_ALWAYS); ++ alternative("call single_task_running", "ud2", X86_FEATURE_IA64); ++ ++ slow_down_io(); /* paravirt call */ ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&memcg_kmem_enabled_key)) ++ printk("kpatch: memcg_kmem_enabled_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif ++ + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + if (export_stats) + seq_puts(m, "# Path Client Start-time\n#\tStats\n"); + else + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2022-06-15 10:04:11.011918011 -0400 ++++ src/net/netlink/af_netlink.c 2022-06-15 10:04:48.312096569 -0400 +@@ -2908,4 +2908,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/linux-5.18.0/multiple.test b/test/integration/linux-5.18.0/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/linux-5.18.0/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/linux-5.18.0/new-function.patch b/test/integration/linux-5.18.0/new-function.patch new file mode 100644 index 0000000..c29fd4e --- /dev/null +++ b/test/integration/linux-5.18.0/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2022-06-15 10:04:10.454915345 -0400 ++++ src/drivers/tty/n_tty.c 2022-06-15 10:04:59.142148413 -0400 +@@ -2213,7 +2213,7 @@ more_to_be_read: + * (note that the process_output*() functions take this lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2300,6 +2300,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/linux-5.18.0/new-globals.patch b/test/integration/linux-5.18.0/new-globals.patch new file mode 100644 index 0000000..04d3c55 --- /dev/null +++ b/test/integration/linux-5.18.0/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2022-06-15 10:04:10.539915752 -0400 ++++ src/fs/proc/cmdline.c 2022-06-15 10:05:10.193201315 -0400 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-06-15 10:04:10.540915757 -0400 ++++ src/fs/proc/meminfo.c 2022-06-15 10:05:10.193201315 -0400 +@@ -19,6 +19,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -55,6 +57,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B); + sunreclaim = global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/linux-5.18.0/shadow-newpid-LOADED.test b/test/integration/linux-5.18.0/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/linux-5.18.0/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/linux-5.18.0/shadow-newpid.patch b/test/integration/linux-5.18.0/shadow-newpid.patch new file mode 100644 index 0000000..8b543d7 --- /dev/null +++ b/test/integration/linux-5.18.0/shadow-newpid.patch @@ -0,0 +1,75 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2022-06-15 10:04:10.539915752 -0400 ++++ src/fs/proc/array.c 2022-06-15 10:05:21.025253168 -0400 +@@ -394,12 +394,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2022-06-15 10:04:10.881917389 -0400 ++++ src/kernel/exit.c 2022-06-15 10:05:21.025253168 -0400 +@@ -733,6 +733,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -795,6 +796,8 @@ void __noreturn do_exit(long code) + exit_task_work(tsk); + exit_thread(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2022-06-15 10:04:10.879917380 -0400 ++++ src/kernel/fork.c 2022-06-15 10:05:21.026253173 -0400 +@@ -2595,6 +2595,7 @@ struct task_struct *create_io_thread(int + * + * args->exit_signal is expected to be checked for sanity by the caller. + */ ++#include + pid_t kernel_clone(struct kernel_clone_args *args) + { + u64 clone_flags = args->flags; +@@ -2603,6 +2604,8 @@ pid_t kernel_clone(struct kernel_clone_a + struct task_struct *p; + int trace = 0; + pid_t nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * For legacy clone() calls, CLONE_PIDFD uses the parent_tid argument +@@ -2642,6 +2645,11 @@ pid_t kernel_clone(struct kernel_clone_a + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/linux-5.18.0/special-static.patch b/test/integration/linux-5.18.0/special-static.patch new file mode 100644 index 0000000..b19093e --- /dev/null +++ b/test/integration/linux-5.18.0/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2022-06-15 10:04:10.879917380 -0400 ++++ src/kernel/fork.c 2022-06-15 10:05:31.745304485 -0400 +@@ -1676,10 +1676,18 @@ static void posix_cpu_timers_init_group( + posix_cputimers_group_init(pct, cpu_limit); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/linux-5.18.0/symvers-disagreement-FAIL.patch b/test/integration/linux-5.18.0/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..9dac457 --- /dev/null +++ b/test/integration/linux-5.18.0/symvers-disagreement-FAIL.patch @@ -0,0 +1,46 @@ +From 2d6b7bce089e52563bd9c67df62f48e90b48047d Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff -Nupr src.orig/drivers/base/core.c src/drivers/base/core.c +--- src.orig/drivers/base/core.c 2022-06-15 10:04:08.806907456 -0400 ++++ src/drivers/base/core.c 2022-06-15 10:05:42.555356233 -0400 +@@ -34,6 +34,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff -Nupr src.orig/drivers/usb/core/usb.c src/drivers/usb/core/usb.c +--- src.orig/drivers/usb/core/usb.c 2022-06-15 10:04:10.469915417 -0400 ++++ src/drivers/usb/core/usb.c 2022-06-15 10:05:42.556356238 -0400 +@@ -697,6 +697,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; diff --git a/test/integration/linux-5.18.0/syscall-LOADED.test b/test/integration/linux-5.18.0/syscall-LOADED.test new file mode 100755 index 0000000..3a2fd88 --- /dev/null +++ b/test/integration/linux-5.18.0/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/linux-5.18.0/syscall.patch b/test/integration/linux-5.18.0/syscall.patch new file mode 100644 index 0000000..57a65ab --- /dev/null +++ b/test/integration/linux-5.18.0/syscall.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2022-06-15 10:04:10.881917389 -0400 ++++ src/kernel/sys.c 2022-06-15 10:05:53.708409623 -0400 +@@ -1278,13 +1278,15 @@ static int override_release(char __user + return ret; + } + +-SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) ++#include "kpatch-syscall.h" ++KPATCH_SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + { + struct new_utsname tmp; + + down_read(&uts_sem); + memcpy(&tmp, utsname(), sizeof(tmp)); + up_read(&uts_sem); ++ strcat(tmp.sysname, ".kpatch"); + if (copy_to_user(name, &tmp, sizeof(tmp))) + return -EFAULT; + diff --git a/test/integration/linux-5.18.0/warn-detect-FAIL.patch b/test/integration/linux-5.18.0/warn-detect-FAIL.patch new file mode 100644 index 0000000..3a27c2e --- /dev/null +++ b/test/integration/linux-5.18.0/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2022-06-15 10:04:08.731907097 -0400 ++++ src/arch/x86/kvm/x86.c 2022-06-15 10:06:04.480461189 -0400 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rebase-patches b/test/integration/rebase-patches new file mode 100755 index 0000000..64faed6 --- /dev/null +++ b/test/integration/rebase-patches @@ -0,0 +1,56 @@ +#!/bin/bash +# +# rebase a set of integration test patches +# +# Example: +# +# 1 - Extract kernel sources: +# +# % (rpm -ivh kernel-3.10.0-1127.el7.src.rpm; \ +# cd ~/rpmbuild/SPECS; \ +# rpmbuild --nodeps -bp kernel.spec) +# +# +# 2 - Rebase from previous release tests: +# +# % cd test/integration +# % SRCDIR="$HOME/rpmbuild/BUILD/kernel-3.10.0-1127.el7/linux-3.10.0-1127.el7.x86_64" \ +# ID=rhel VERSION_ID=7.8 ./rebase-patches rhel-7.7/*{.patch,.disabled} +# % cp rhel-7.7/*.test rhel-7.8/ + +OUTDIR=$(pwd)/${ID}-${VERSION_ID} +mkdir -p "$OUTDIR" + +echo "* Making backup copy of kernel sources" +rm -rf "${SRCDIR}.orig" +cp -r "$SRCDIR" "${SRCDIR}.orig" + +for P in "$@"; do + + echo + echo "* Patch: $(basename "$P")" + + echo "** dry run..." + if ! patch -d "$SRCDIR" --dry-run --quiet -p1 < "$P"; then + echo "*** Skipping! ***" && continue + fi + + echo "** patching..." + patch -d "$SRCDIR" -p1 --no-backup-if-mismatch < "$P" + + echo "** generating new $(basename "$P")..." + NEWP="$OUTDIR"/$(basename "$P") + awk '/^Index|^diff|^patch/{exit} {print $LF}' "$P" > "$NEWP" + diff -Nupr "$SRCDIR.orig" "${SRCDIR}" >> "$NEWP" + sed -i "s#$SRCDIR#src#g" "$NEWP" + + echo "** reversing patch to restore tree..." + patch -d "$SRCDIR" -p1 -R < "$NEWP" + +done + +echo "*** Removing backup copy of kernel sources" +rm -rf "${SRCDIR}.orig" + +echo +echo "*** Done" diff --git a/test/integration/rhel-7.4/bug-table-section.patch b/test/integration/rhel-7.4/bug-table-section.patch new file mode 100644 index 0000000..71f8c1b --- /dev/null +++ b/test/integration/rhel-7.4/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.769056469 -0400 +@@ -266,6 +266,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.4/cmdline-string-LOADED.test b/test/integration/rhel-7.4/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.4/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.4/cmdline-string.patch b/test/integration/rhel-7.4/cmdline-string.patch new file mode 100644 index 0000000..749861f --- /dev/null +++ b/test/integration/rhel-7.4/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:22.955061380 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.4/data-new-LOADED.test b/test/integration/rhel-7.4/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.4/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.4/data-new.patch b/test/integration/rhel-7.4/data-new.patch new file mode 100644 index 0000000..879828c --- /dev/null +++ b/test/integration/rhel-7.4/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:24.102066130 -0400 +@@ -20,6 +20,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -106,6 +108,7 @@ static int meminfo_proc_show(struct seq_ + #ifdef CONFIG_TRANSPARENT_HUGEPAGE + "AnonHugePages: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -167,6 +170,7 @@ static int meminfo_proc_show(struct seq_ + ,K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * + HPAGE_PMD_NR) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.4/data-read-mostly.patch.disabled b/test/integration/rhel-7.4/data-read-mostly.patch.disabled new file mode 100644 index 0000000..611662f --- /dev/null +++ b/test/integration/rhel-7.4/data-read-mostly.patch.disabled @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2017-09-22 15:27:21.759056428 -0400 ++++ src/net/core/dev.c 2017-09-22 15:27:25.244070859 -0400 +@@ -4012,6 +4012,7 @@ ncls: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.4/fixup-section.patch b/test/integration/rhel-7.4/fixup-section.patch new file mode 100644 index 0000000..0e2022e --- /dev/null +++ b/test/integration/rhel-7.4/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index fee38e04fae4..bce1e5ce74e5 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -166,6 +166,7 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.4/gcc-constprop.patch b/test/integration/rhel-7.4/gcc-constprop.patch new file mode 100644 index 0000000..63ba45d --- /dev/null +++ b/test/integration/rhel-7.4/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/time/timekeeping.c 2017-09-22 15:27:27.522080292 -0400 +@@ -877,6 +877,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.4/gcc-isra.patch b/test/integration/rhel-7.4/gcc-isra.patch new file mode 100644 index 0000000..a869797 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/proc_sysctl.c 2017-09-22 15:27:28.670085046 -0400 +@@ -24,6 +24,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.4/gcc-mangled-3.patch b/test/integration/rhel-7.4/gcc-mangled-3.patch new file mode 100644 index 0000000..5828680 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2017-09-22 15:27:21.618055844 -0400 ++++ src/mm/slub.c 2017-09-22 15:27:29.830089850 -0400 +@@ -5528,6 +5528,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.4/gcc-static-local-var-2.patch b/test/integration/rhel-7.4/gcc-static-local-var-2.patch new file mode 100644 index 0000000..4f653d7 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2017-09-22 15:27:21.618055844 -0400 ++++ src/mm/mmap.c 2017-09-22 15:27:31.024094794 -0400 +@@ -1687,6 +1688,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.4/gcc-static-local-var-3.patch b/test/integration/rhel-7.4/gcc-static-local-var-3.patch new file mode 100644 index 0000000..d87677b --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2017-09-22 15:27:21.601055773 -0400 ++++ src/kernel/sys.c 2017-09-22 15:27:32.170099540 -0400 +@@ -554,8 +554,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.4/gcc-static-local-var-4.patch b/test/integration/rhel-7.4/gcc-static-local-var-4.patch new file mode 100644 index 0000000..e22ead7 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2017-09-22 15:27:21.702056192 -0400 ++++ src/fs/aio.c 2017-09-22 15:27:33.299104215 -0400 +@@ -219,9 +219,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.4/gcc-static-local-var-4.test b/test/integration/rhel-7.4/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.4/gcc-static-local-var-5.patch b/test/integration/rhel-7.4/gcc-static-local-var-5.patch new file mode 100644 index 0000000..540affa --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/audit.c 2017-09-22 15:27:34.429108894 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.4/gcc-static-local-var-6.patch b/test/integration/rhel-7.4/gcc-static-local-var-6.patch new file mode 100644 index 0000000..bd5493c --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index a9d587a..23336ed 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -106,6 +106,8 @@ static int nf_ip6_reroute(struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -119,6 +121,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.4/gcc-static-local-var.patch b/test/integration/rhel-7.4/gcc-static-local-var.patch new file mode 100644 index 0000000..2dab9db --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/arch/x86/kernel/ldt.c src/arch/x86/kernel/ldt.c +--- src.orig/arch/x86/kernel/ldt.c 2017-09-22 15:27:20.847052651 -0400 ++++ src/arch/x86/kernel/ldt.c 2017-09-22 15:27:35.573113632 -0400 +@@ -98,6 +98,12 @@ static inline int copy_ldt(mm_context_t + return 0; + } + ++void hi_there(void) ++{ ++ if (!jiffies) ++ printk("hi there\n"); ++} ++ + /* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. +@@ -107,6 +113,8 @@ int init_new_context(struct task_struct + struct mm_struct *old_mm; + int retval = 0; + ++ hi_there(); ++ + mutex_init(&mm->context.lock); + mm->context.size = 0; + old_mm = current->mm; diff --git a/test/integration/rhel-7.4/macro-callbacks.patch b/test/integration/rhel-7.4/macro-callbacks.patch new file mode 100644 index 0000000..0d6831b --- /dev/null +++ b/test/integration/rhel-7.4/macro-callbacks.patch @@ -0,0 +1,160 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +--- src.old/fs/aio.c 2018-02-26 11:07:51.522610407 -0500 ++++ src/fs/aio.c 2018-03-05 11:17:21.560015449 -0500 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +--- src.old/drivers/input/joydev.c 2018-02-26 11:07:49.470610407 -0500 ++++ src/drivers/input/joydev.c 2018-03-05 11:18:13.998015449 -0500 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +--- src.old/drivers/input/misc/pcspkr.c 2018-02-26 11:07:49.477610407 -0500 ++++ src/drivers/input/misc/pcspkr.c 2018-03-05 11:18:23.411015449 -0500 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/rhel-7.4/macro-printk.patch b/test/integration/rhel-7.4/macro-printk.patch new file mode 100644 index 0000000..9f591f4 --- /dev/null +++ b/test/integration/rhel-7.4/macro-printk.patch @@ -0,0 +1,147 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2017-09-22 16:52:10.646110299 -0400 ++++ src/net/ipv4/fib_frontend.c 2017-09-22 16:55:14.395870305 -0400 +@@ -633,6 +633,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -651,6 +652,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2017-09-22 16:52:10.645110295 -0400 ++++ src/net/ipv4/fib_semantics.c 2017-09-22 16:54:05.175584004 -0400 +@@ -925,6 +925,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -949,6 +950,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -969,6 +971,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -980,6 +983,7 @@ struct fib_info *fib_create_info(struct + } else + fi->fib_metrics = (u32 *) dst_default_metrics; + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); + + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; +@@ -996,8 +1000,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1048,6 +1054,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1065,6 +1072,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1087,6 +1095,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1099,6 +1108,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1110,6 +1120,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1133,6 +1144,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1143,6 +1155,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2017-09-22 16:52:10.645110295 -0400 ++++ src/net/ipv4/fib_trie.c 2017-09-22 16:55:39.940975963 -0400 +@@ -1191,6 +1191,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1216,11 +1217,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority) : NULL; diff --git a/test/integration/rhel-7.4/meminfo-init-FAIL.patch b/test/integration/rhel-7.4/meminfo-init-FAIL.patch new file mode 100644 index 0000000..5df5225 --- /dev/null +++ b/test/integration/rhel-7.4/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:40.130132502 -0400 +@@ -191,6 +191,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.4/meminfo-init2-FAIL.patch b/test/integration/rhel-7.4/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..c030f61 --- /dev/null +++ b/test/integration/rhel-7.4/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:38.972127707 -0400 +@@ -30,6 +30,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -191,6 +192,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.4/meminfo-string-LOADED.test b/test/integration/rhel-7.4/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.4/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.4/meminfo-string.patch b/test/integration/rhel-7.4/meminfo-string.patch new file mode 100644 index 0000000..afdc5d0 --- /dev/null +++ b/test/integration/rhel-7.4/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:41.274137239 -0400 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/rhel-7.4/module-call-external.patch b/test/integration/rhel-7.4/module-call-external.patch new file mode 100644 index 0000000..754d725 --- /dev/null +++ b/test/integration/rhel-7.4/module-call-external.patch @@ -0,0 +1,33 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2017-09-22 15:27:21.705056204 -0400 ++++ src/fs/nfsd/export.c 2017-09-22 15:27:42.411141948 -0400 +@@ -1184,6 +1184,8 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1193,6 +1195,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2017-09-22 15:27:21.754056407 -0400 ++++ src/net/netlink/af_netlink.c 2017-09-22 15:27:42.412141952 -0400 +@@ -3260,4 +3260,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.4/module-kvm-fixup.patch b/test/integration/rhel-7.4/module-kvm-fixup.patch new file mode 100644 index 0000000..174ad65 --- /dev/null +++ b/test/integration/rhel-7.4/module-kvm-fixup.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2017-09-22 15:27:20.853052676 -0400 ++++ src/arch/x86/kvm/vmx.c 2017-09-22 15:27:43.583146801 -0400 +@@ -10597,6 +10597,8 @@ static int vmx_check_intercept(struct kv + struct x86_instruction_info *info, + enum x86_intercept_stage stage) + { ++ if (!jiffies) ++ printk("kpatch vmx_check_intercept\n"); + return X86EMUL_CONTINUE; + } + diff --git a/test/integration/rhel-7.4/module-shadow.patch b/test/integration/rhel-7.4/module-shadow.patch new file mode 100644 index 0000000..c7da353 --- /dev/null +++ b/test/integration/rhel-7.4/module-shadow.patch @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2017-09-22 15:27:20.853052676 -0400 ++++ src/arch/x86/kvm/vmx.c 2017-09-22 15:27:44.742151601 -0400 +@@ -10581,10 +10581,20 @@ static void vmx_leave_nested(struct kvm_ + * It should only be called before L2 actually succeeded to run, and when + * vmcs01 is current (it doesn't leave_guest_mode() or switch vmcss). + */ ++#include "kpatch.h" + static void nested_vmx_entry_failure(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12, + u32 reason, unsigned long qualification) + { ++ int *kpatch; ++ ++ kpatch = kpatch_shadow_alloc(vcpu, "kpatch", sizeof(*kpatch), ++ GFP_KERNEL); ++ if (kpatch) { ++ kpatch_shadow_get(vcpu, "kpatch"); ++ kpatch_shadow_free(vcpu, "kpatch"); ++ } ++ + load_vmcs12_host_state(vcpu, vmcs12); + vmcs12->vm_exit_reason = reason | VMX_EXIT_REASONS_FAILED_VMENTRY; + vmcs12->exit_qualification = qualification; diff --git a/test/integration/rhel-7.4/multiple.test b/test/integration/rhel-7.4/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.4/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.4/new-function.patch b/test/integration/rhel-7.4/new-function.patch new file mode 100644 index 0000000..cf47c83 --- /dev/null +++ b/test/integration/rhel-7.4/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2017-09-22 15:27:21.084053633 -0400 ++++ src/drivers/tty/n_tty.c 2017-09-22 15:27:45.888156346 -0400 +@@ -2016,7 +2016,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2098,6 +2098,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.4/new-globals.patch b/test/integration/rhel-7.4/new-globals.patch new file mode 100644 index 0000000..3d9d349 --- /dev/null +++ b/test/integration/rhel-7.4/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:47.028161067 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:47.029161071 -0400 +@@ -16,6 +16,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.4/parainstructions-section.patch b/test/integration/rhel-7.4/parainstructions-section.patch new file mode 100644 index 0000000..809dce4 --- /dev/null +++ b/test/integration/rhel-7.4/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/generic.c 2017-09-22 15:27:48.190165879 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.4/replace-section-references.patch b/test/integration/rhel-7.4/replace-section-references.patch new file mode 100644 index 0000000..e41bba9 --- /dev/null +++ b/test/integration/rhel-7.4/replace-section-references.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:49.362170732 -0400 +@@ -248,6 +248,8 @@ static void shared_msr_update(unsigned s + + void kvm_define_shared_msr(unsigned slot, u32 msr) + { ++ if (!jiffies) ++ printk("kpatch kvm define shared msr\n"); + BUG_ON(slot >= KVM_NR_SHARED_MSRS); + shared_msrs_global.msrs[slot] = msr; + if (slot >= shared_msrs_global.nr) diff --git a/test/integration/rhel-7.4/shadow-newpid-LOADED.test b/test/integration/rhel-7.4/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.4/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.4/shadow-newpid.patch b/test/integration/rhel-7.4/shadow-newpid.patch new file mode 100644 index 0000000..cde1810 --- /dev/null +++ b/test/integration/rhel-7.4/shadow-newpid.patch @@ -0,0 +1,69 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2017-09-22 16:52:10.597110096 -0400 ++++ src/fs/proc/array.c 2017-09-22 16:59:40.799972178 -0400 +@@ -359,13 +359,20 @@ static inline void task_seccomp(struct s + #endif + } + ++#include "kpatch.h" + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = kpatch_shadow_get(p, "newpid"); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2017-09-22 16:52:10.506109720 -0400 ++++ src/kernel/exit.c 2017-09-22 16:59:40.799972178 -0400 +@@ -715,6 +715,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include "kpatch.h" + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -812,6 +813,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ kpatch_shadow_free(tsk, "newpid"); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2017-09-22 16:52:10.504109711 -0400 ++++ src/kernel/fork.c 2017-09-22 17:00:44.938237460 -0400 +@@ -1700,6 +1700,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include "kpatch.h" + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1737,6 +1738,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = kpatch_shadow_alloc(p, "newpid", sizeof(*newpid), ++ GFP_KERNEL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.4/smp-locks-section.patch b/test/integration/rhel-7.4/smp-locks-section.patch new file mode 100644 index 0000000..6f39d53 --- /dev/null +++ b/test/integration/rhel-7.4/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2017-09-22 15:27:21.077053604 -0400 ++++ src/drivers/tty/tty_buffer.c 2017-09-22 15:27:50.542175618 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.4/special-static-2.patch b/test/integration/rhel-7.4/special-static-2.patch new file mode 100644 index 0000000..146d5b5 --- /dev/null +++ b/test/integration/rhel-7.4/special-static-2.patch @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:51.744180596 -0400 +@@ -2093,12 +2093,20 @@ static void record_steal_time(struct kvm + &vcpu->arch.st.steal, sizeof(struct kvm_steal_time)); + } + ++void kpatch_kvm_x86_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch kvm x86 foo\n"); ++} ++ + int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + { + bool pr = false; + u32 msr = msr_info->index; + u64 data = msr_info->data; + ++ kpatch_kvm_x86_foo(); ++ + switch (msr) { + case MSR_AMD64_NB_CFG: + case MSR_IA32_UCODE_REV: diff --git a/test/integration/rhel-7.4/special-static.patch b/test/integration/rhel-7.4/special-static.patch new file mode 100644 index 0000000..84647ec --- /dev/null +++ b/test/integration/rhel-7.4/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2017-09-22 15:27:21.600055769 -0400 ++++ src/kernel/fork.c 2017-09-22 15:27:53.052186012 -0400 +@@ -1129,10 +1129,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.4/tracepoints-section.patch b/test/integration/rhel-7.4/tracepoints-section.patch new file mode 100644 index 0000000..b770f9e --- /dev/null +++ b/test/integration/rhel-7.4/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/timer.c src/kernel/timer.c +--- src.orig/kernel/timer.c 2017-09-22 15:27:21.600055769 -0400 ++++ src/kernel/timer.c 2017-09-22 15:27:54.288191131 -0400 +@@ -1390,6 +1390,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.4/warn-detect-FAIL.patch b/test/integration/rhel-7.4/warn-detect-FAIL.patch new file mode 100644 index 0000000..8efa782 --- /dev/null +++ b/test/integration/rhel-7.4/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:55.489196104 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.5/bug-table-section.patch b/test/integration/rhel-7.5/bug-table-section.patch new file mode 100644 index 0000000..71f8c1b --- /dev/null +++ b/test/integration/rhel-7.5/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.769056469 -0400 +@@ -266,6 +266,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.5/cmdline-string-LOADED.test b/test/integration/rhel-7.5/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.5/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.5/cmdline-string.patch b/test/integration/rhel-7.5/cmdline-string.patch new file mode 100644 index 0000000..749861f --- /dev/null +++ b/test/integration/rhel-7.5/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:22.955061380 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.5/data-new-LOADED.test b/test/integration/rhel-7.5/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.5/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.5/data-new.patch b/test/integration/rhel-7.5/data-new.patch new file mode 100644 index 0000000..879828c --- /dev/null +++ b/test/integration/rhel-7.5/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:24.102066130 -0400 +@@ -20,6 +20,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -106,6 +108,7 @@ static int meminfo_proc_show(struct seq_ + #ifdef CONFIG_TRANSPARENT_HUGEPAGE + "AnonHugePages: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -167,6 +170,7 @@ static int meminfo_proc_show(struct seq_ + ,K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * + HPAGE_PMD_NR) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.5/data-read-mostly.patch.disabled b/test/integration/rhel-7.5/data-read-mostly.patch.disabled new file mode 100644 index 0000000..611662f --- /dev/null +++ b/test/integration/rhel-7.5/data-read-mostly.patch.disabled @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2017-09-22 15:27:21.759056428 -0400 ++++ src/net/core/dev.c 2017-09-22 15:27:25.244070859 -0400 +@@ -4012,6 +4012,7 @@ ncls: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.5/fixup-section.patch b/test/integration/rhel-7.5/fixup-section.patch new file mode 100644 index 0000000..18d13b5 --- /dev/null +++ b/test/integration/rhel-7.5/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index febd02dfbe2d..064db7bd70d0 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.5/gcc-constprop.patch b/test/integration/rhel-7.5/gcc-constprop.patch new file mode 100644 index 0000000..63ba45d --- /dev/null +++ b/test/integration/rhel-7.5/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/time/timekeeping.c 2017-09-22 15:27:27.522080292 -0400 +@@ -877,6 +877,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.5/gcc-isra.patch b/test/integration/rhel-7.5/gcc-isra.patch new file mode 100644 index 0000000..a869797 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/proc_sysctl.c 2017-09-22 15:27:28.670085046 -0400 +@@ -24,6 +24,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.5/gcc-mangled-3.patch b/test/integration/rhel-7.5/gcc-mangled-3.patch new file mode 100644 index 0000000..5828680 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2017-09-22 15:27:21.618055844 -0400 ++++ src/mm/slub.c 2017-09-22 15:27:29.830089850 -0400 +@@ -5528,6 +5528,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.5/gcc-static-local-var-2.patch b/test/integration/rhel-7.5/gcc-static-local-var-2.patch new file mode 100644 index 0000000..4f653d7 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2017-09-22 15:27:21.618055844 -0400 ++++ src/mm/mmap.c 2017-09-22 15:27:31.024094794 -0400 +@@ -1687,6 +1688,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.5/gcc-static-local-var-3.patch b/test/integration/rhel-7.5/gcc-static-local-var-3.patch new file mode 100644 index 0000000..d87677b --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2017-09-22 15:27:21.601055773 -0400 ++++ src/kernel/sys.c 2017-09-22 15:27:32.170099540 -0400 +@@ -554,8 +554,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.5/gcc-static-local-var-4.patch b/test/integration/rhel-7.5/gcc-static-local-var-4.patch new file mode 100644 index 0000000..e22ead7 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2017-09-22 15:27:21.702056192 -0400 ++++ src/fs/aio.c 2017-09-22 15:27:33.299104215 -0400 +@@ -219,9 +219,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.5/gcc-static-local-var-4.test b/test/integration/rhel-7.5/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.5/gcc-static-local-var-5.patch b/test/integration/rhel-7.5/gcc-static-local-var-5.patch new file mode 100644 index 0000000..540affa --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/audit.c 2017-09-22 15:27:34.429108894 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.5/gcc-static-local-var-6.patch b/test/integration/rhel-7.5/gcc-static-local-var-6.patch new file mode 100644 index 0000000..bd5493c --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index a9d587a..23336ed 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -106,6 +106,8 @@ static int nf_ip6_reroute(struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -119,6 +121,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.5/gcc-static-local-var.patch b/test/integration/rhel-7.5/gcc-static-local-var.patch new file mode 100644 index 0000000..2dab9db --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/arch/x86/kernel/ldt.c src/arch/x86/kernel/ldt.c +--- src.orig/arch/x86/kernel/ldt.c 2017-09-22 15:27:20.847052651 -0400 ++++ src/arch/x86/kernel/ldt.c 2017-09-22 15:27:35.573113632 -0400 +@@ -98,6 +98,12 @@ static inline int copy_ldt(mm_context_t + return 0; + } + ++void hi_there(void) ++{ ++ if (!jiffies) ++ printk("hi there\n"); ++} ++ + /* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. +@@ -107,6 +113,8 @@ int init_new_context(struct task_struct + struct mm_struct *old_mm; + int retval = 0; + ++ hi_there(); ++ + mutex_init(&mm->context.lock); + mm->context.size = 0; + old_mm = current->mm; diff --git a/test/integration/rhel-7.5/macro-callbacks.patch b/test/integration/rhel-7.5/macro-callbacks.patch new file mode 100644 index 0000000..0d6831b --- /dev/null +++ b/test/integration/rhel-7.5/macro-callbacks.patch @@ -0,0 +1,160 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +--- src.old/fs/aio.c 2018-02-26 11:07:51.522610407 -0500 ++++ src/fs/aio.c 2018-03-05 11:17:21.560015449 -0500 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +--- src.old/drivers/input/joydev.c 2018-02-26 11:07:49.470610407 -0500 ++++ src/drivers/input/joydev.c 2018-03-05 11:18:13.998015449 -0500 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +--- src.old/drivers/input/misc/pcspkr.c 2018-02-26 11:07:49.477610407 -0500 ++++ src/drivers/input/misc/pcspkr.c 2018-03-05 11:18:23.411015449 -0500 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/rhel-7.5/macro-printk.patch b/test/integration/rhel-7.5/macro-printk.patch new file mode 100644 index 0000000..9f591f4 --- /dev/null +++ b/test/integration/rhel-7.5/macro-printk.patch @@ -0,0 +1,147 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2017-09-22 16:52:10.646110299 -0400 ++++ src/net/ipv4/fib_frontend.c 2017-09-22 16:55:14.395870305 -0400 +@@ -633,6 +633,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -651,6 +652,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2017-09-22 16:52:10.645110295 -0400 ++++ src/net/ipv4/fib_semantics.c 2017-09-22 16:54:05.175584004 -0400 +@@ -925,6 +925,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -949,6 +950,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -969,6 +971,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -980,6 +983,7 @@ struct fib_info *fib_create_info(struct + } else + fi->fib_metrics = (u32 *) dst_default_metrics; + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); + + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; +@@ -996,8 +1000,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1048,6 +1054,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1065,6 +1072,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1087,6 +1095,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1099,6 +1108,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1110,6 +1120,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1133,6 +1144,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1143,6 +1155,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2017-09-22 16:52:10.645110295 -0400 ++++ src/net/ipv4/fib_trie.c 2017-09-22 16:55:39.940975963 -0400 +@@ -1191,6 +1191,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1216,11 +1217,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority) : NULL; diff --git a/test/integration/rhel-7.5/meminfo-init-FAIL.patch b/test/integration/rhel-7.5/meminfo-init-FAIL.patch new file mode 100644 index 0000000..5df5225 --- /dev/null +++ b/test/integration/rhel-7.5/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:40.130132502 -0400 +@@ -191,6 +191,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.5/meminfo-init2-FAIL.patch b/test/integration/rhel-7.5/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..c030f61 --- /dev/null +++ b/test/integration/rhel-7.5/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:38.972127707 -0400 +@@ -30,6 +30,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -191,6 +192,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.5/meminfo-string-LOADED.test b/test/integration/rhel-7.5/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.5/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.5/meminfo-string.patch b/test/integration/rhel-7.5/meminfo-string.patch new file mode 100644 index 0000000..afdc5d0 --- /dev/null +++ b/test/integration/rhel-7.5/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:41.274137239 -0400 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/rhel-7.5/module-call-external.patch b/test/integration/rhel-7.5/module-call-external.patch new file mode 100644 index 0000000..754d725 --- /dev/null +++ b/test/integration/rhel-7.5/module-call-external.patch @@ -0,0 +1,33 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2017-09-22 15:27:21.705056204 -0400 ++++ src/fs/nfsd/export.c 2017-09-22 15:27:42.411141948 -0400 +@@ -1184,6 +1184,8 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1193,6 +1195,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2017-09-22 15:27:21.754056407 -0400 ++++ src/net/netlink/af_netlink.c 2017-09-22 15:27:42.412141952 -0400 +@@ -3260,4 +3260,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.5/module-kvm-fixup.patch b/test/integration/rhel-7.5/module-kvm-fixup.patch new file mode 100644 index 0000000..174ad65 --- /dev/null +++ b/test/integration/rhel-7.5/module-kvm-fixup.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2017-09-22 15:27:20.853052676 -0400 ++++ src/arch/x86/kvm/vmx.c 2017-09-22 15:27:43.583146801 -0400 +@@ -10597,6 +10597,8 @@ static int vmx_check_intercept(struct kv + struct x86_instruction_info *info, + enum x86_intercept_stage stage) + { ++ if (!jiffies) ++ printk("kpatch vmx_check_intercept\n"); + return X86EMUL_CONTINUE; + } + diff --git a/test/integration/rhel-7.5/module-shadow.patch.disabled b/test/integration/rhel-7.5/module-shadow.patch.disabled new file mode 100644 index 0000000..e4821c9 --- /dev/null +++ b/test/integration/rhel-7.5/module-shadow.patch.disabled @@ -0,0 +1,25 @@ +Index: src/arch/x86/kvm/vmx.c +=================================================================== +--- src.orig/arch/x86/kvm/vmx.c ++++ src/arch/x86/kvm/vmx.c +@@ -11168,10 +11168,20 @@ static void vmx_leave_nested(struct kvm_ + * It should only be called before L2 actually succeeded to run, and when + * vmcs01 is current (it doesn't leave_guest_mode() or switch vmcss). + */ ++#include + static void nested_vmx_entry_failure(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12, + u32 reason, unsigned long qualification) + { ++ int *kpatch; ++ ++ kpatch = klp_shadow_alloc(vcpu, 0, NULL, sizeof(*kpatch), ++ GFP_KERNEL); ++ if (kpatch) { ++ klp_shadow_get(vcpu, 0); ++ klp_shadow_free(vcpu, 0); ++ } ++ + load_vmcs12_host_state(vcpu, vmcs12); + vmcs12->vm_exit_reason = reason | VMX_EXIT_REASONS_FAILED_VMENTRY; + vmcs12->exit_qualification = qualification; diff --git a/test/integration/rhel-7.5/multiple.test b/test/integration/rhel-7.5/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.5/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.5/new-function.patch b/test/integration/rhel-7.5/new-function.patch new file mode 100644 index 0000000..bef6803 --- /dev/null +++ b/test/integration/rhel-7.5/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2017-09-22 15:27:21.084053633 -0400 ++++ src/drivers/tty/n_tty.c 2017-09-22 15:27:45.888156346 -0400 +@@ -2016,7 +2016,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2098,6 +2098,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.5/new-globals.patch b/test/integration/rhel-7.5/new-globals.patch new file mode 100644 index 0000000..3d9d349 --- /dev/null +++ b/test/integration/rhel-7.5/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:47.028161067 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:47.029161071 -0400 +@@ -16,6 +16,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.5/parainstructions-section.patch b/test/integration/rhel-7.5/parainstructions-section.patch new file mode 100644 index 0000000..809dce4 --- /dev/null +++ b/test/integration/rhel-7.5/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/generic.c 2017-09-22 15:27:48.190165879 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.5/replace-section-references.patch b/test/integration/rhel-7.5/replace-section-references.patch new file mode 100644 index 0000000..e41bba9 --- /dev/null +++ b/test/integration/rhel-7.5/replace-section-references.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:49.362170732 -0400 +@@ -248,6 +248,8 @@ static void shared_msr_update(unsigned s + + void kvm_define_shared_msr(unsigned slot, u32 msr) + { ++ if (!jiffies) ++ printk("kpatch kvm define shared msr\n"); + BUG_ON(slot >= KVM_NR_SHARED_MSRS); + shared_msrs_global.msrs[slot] = msr; + if (slot >= shared_msrs_global.nr) diff --git a/test/integration/rhel-7.5/shadow-newpid-LOADED.test b/test/integration/rhel-7.5/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.5/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.5/shadow-newpid.patch b/test/integration/rhel-7.5/shadow-newpid.patch new file mode 100644 index 0000000..ff2019c --- /dev/null +++ b/test/integration/rhel-7.5/shadow-newpid.patch @@ -0,0 +1,72 @@ +Index: src/fs/proc/array.c +=================================================================== +--- src.orig/fs/proc/array.c ++++ src/fs/proc/array.c +@@ -394,13 +394,20 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +Index: src/kernel/exit.c +=================================================================== +--- src.orig/kernel/exit.c ++++ src/kernel/exit.c +@@ -715,6 +715,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -812,6 +813,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +Index: src/kernel/fork.c +=================================================================== +--- src.orig/kernel/fork.c ++++ src/kernel/fork.c +@@ -1751,6 +1751,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1788,6 +1789,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, NULL, sizeof(*newpid), ++ GFP_KERNEL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.5/smp-locks-section.patch b/test/integration/rhel-7.5/smp-locks-section.patch new file mode 100644 index 0000000..6f39d53 --- /dev/null +++ b/test/integration/rhel-7.5/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2017-09-22 15:27:21.077053604 -0400 ++++ src/drivers/tty/tty_buffer.c 2017-09-22 15:27:50.542175618 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.5/special-static-2.patch b/test/integration/rhel-7.5/special-static-2.patch new file mode 100644 index 0000000..146d5b5 --- /dev/null +++ b/test/integration/rhel-7.5/special-static-2.patch @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:51.744180596 -0400 +@@ -2093,12 +2093,20 @@ static void record_steal_time(struct kvm + &vcpu->arch.st.steal, sizeof(struct kvm_steal_time)); + } + ++void kpatch_kvm_x86_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch kvm x86 foo\n"); ++} ++ + int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + { + bool pr = false; + u32 msr = msr_info->index; + u64 data = msr_info->data; + ++ kpatch_kvm_x86_foo(); ++ + switch (msr) { + case MSR_AMD64_NB_CFG: + case MSR_IA32_UCODE_REV: diff --git a/test/integration/rhel-7.5/special-static.patch b/test/integration/rhel-7.5/special-static.patch new file mode 100644 index 0000000..84647ec --- /dev/null +++ b/test/integration/rhel-7.5/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2017-09-22 15:27:21.600055769 -0400 ++++ src/kernel/fork.c 2017-09-22 15:27:53.052186012 -0400 +@@ -1129,10 +1129,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.5/tracepoints-section.patch b/test/integration/rhel-7.5/tracepoints-section.patch new file mode 100644 index 0000000..b770f9e --- /dev/null +++ b/test/integration/rhel-7.5/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/timer.c src/kernel/timer.c +--- src.orig/kernel/timer.c 2017-09-22 15:27:21.600055769 -0400 ++++ src/kernel/timer.c 2017-09-22 15:27:54.288191131 -0400 +@@ -1390,6 +1390,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.5/warn-detect-FAIL.patch b/test/integration/rhel-7.5/warn-detect-FAIL.patch new file mode 100644 index 0000000..8efa782 --- /dev/null +++ b/test/integration/rhel-7.5/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:55.489196104 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.6/bug-table-section.patch b/test/integration/rhel-7.6/bug-table-section.patch new file mode 100644 index 0000000..f75e398 --- /dev/null +++ b/test/integration/rhel-7.6/bug-table-section.patch @@ -0,0 +1,13 @@ +Index: kernel-rhel7/fs/proc/proc_sysctl.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/proc_sysctl.c ++++ kernel-rhel7/fs/proc/proc_sysctl.c +@@ -301,6 +301,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.6/cmdline-string-LOADED.test b/test/integration/rhel-7.6/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.6/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.6/cmdline-string.patch b/test/integration/rhel-7.6/cmdline-string.patch new file mode 100644 index 0000000..749861f --- /dev/null +++ b/test/integration/rhel-7.6/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:22.955061380 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.6/data-new-LOADED.test b/test/integration/rhel-7.6/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.6/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.6/data-new.patch b/test/integration/rhel-7.6/data-new.patch new file mode 100644 index 0000000..e2f9333 --- /dev/null +++ b/test/integration/rhel-7.6/data-new.patch @@ -0,0 +1,29 @@ +Index: kernel-rhel7/fs/proc/meminfo.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/meminfo.c ++++ kernel-rhel7/fs/proc/meminfo.c +@@ -20,6 +20,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -110,6 +112,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -175,6 +178,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.6/data-read-mostly.patch.disabled b/test/integration/rhel-7.6/data-read-mostly.patch.disabled new file mode 100644 index 0000000..42b02cb --- /dev/null +++ b/test/integration/rhel-7.6/data-read-mostly.patch.disabled @@ -0,0 +1,12 @@ +Index: kernel-rhel7/net/core/dev.c +=================================================================== +--- kernel-rhel7.orig/net/core/dev.c ++++ kernel-rhel7/net/core/dev.c +@@ -4199,6 +4199,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.6/fixup-section.patch b/test/integration/rhel-7.6/fixup-section.patch new file mode 100644 index 0000000..18d13b5 --- /dev/null +++ b/test/integration/rhel-7.6/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index febd02dfbe2d..064db7bd70d0 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.6/gcc-constprop.patch.disabled b/test/integration/rhel-7.6/gcc-constprop.patch.disabled new file mode 100644 index 0000000..63ba45d --- /dev/null +++ b/test/integration/rhel-7.6/gcc-constprop.patch.disabled @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/time/timekeeping.c 2017-09-22 15:27:27.522080292 -0400 +@@ -877,6 +877,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.6/gcc-isra.patch b/test/integration/rhel-7.6/gcc-isra.patch new file mode 100644 index 0000000..1eab35c --- /dev/null +++ b/test/integration/rhel-7.6/gcc-isra.patch @@ -0,0 +1,12 @@ +Index: kernel-rhel7/fs/proc/proc_sysctl.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/proc_sysctl.c ++++ kernel-rhel7/fs/proc/proc_sysctl.c +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.6/gcc-mangled-3.patch b/test/integration/rhel-7.6/gcc-mangled-3.patch new file mode 100644 index 0000000..92ba80e --- /dev/null +++ b/test/integration/rhel-7.6/gcc-mangled-3.patch @@ -0,0 +1,14 @@ +Index: kernel-rhel7/mm/slub.c +=================================================================== +--- kernel-rhel7.orig/mm/slub.c ++++ kernel-rhel7/mm/slub.c +@@ -5611,6 +5611,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.6/gcc-static-local-var-2.patch b/test/integration/rhel-7.6/gcc-static-local-var-2.patch new file mode 100644 index 0000000..7153968 --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-2.patch @@ -0,0 +1,14 @@ +Index: kernel-rhel7/mm/mmap.c +=================================================================== +--- kernel-rhel7.orig/mm/mmap.c ++++ kernel-rhel7/mm/mmap.c +@@ -1715,6 +1715,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.6/gcc-static-local-var-3.patch b/test/integration/rhel-7.6/gcc-static-local-var-3.patch new file mode 100644 index 0000000..d751fc6 --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-3.patch @@ -0,0 +1,20 @@ +Index: kernel-rhel7/kernel/sys.c +=================================================================== +--- kernel-rhel7.orig/kernel/sys.c ++++ kernel-rhel7/kernel/sys.c +@@ -559,8 +559,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.6/gcc-static-local-var-4.patch b/test/integration/rhel-7.6/gcc-static-local-var-4.patch new file mode 100644 index 0000000..e25d343 --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-4.patch @@ -0,0 +1,21 @@ +Index: kernel-rhel7/fs/aio.c +=================================================================== +--- kernel-rhel7.orig/fs/aio.c ++++ kernel-rhel7/fs/aio.c +@@ -223,9 +223,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.6/gcc-static-local-var-4.test b/test/integration/rhel-7.6/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.6/gcc-static-local-var-5.patch b/test/integration/rhel-7.6/gcc-static-local-var-5.patch new file mode 100644 index 0000000..540affa --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/audit.c 2017-09-22 15:27:34.429108894 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.6/gcc-static-local-var-6.patch b/test/integration/rhel-7.6/gcc-static-local-var-6.patch new file mode 100644 index 0000000..bd5493c --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index a9d587a..23336ed 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -106,6 +106,8 @@ static int nf_ip6_reroute(struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -119,6 +121,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.6/macro-callbacks.patch b/test/integration/rhel-7.6/macro-callbacks.patch new file mode 100644 index 0000000..0d6831b --- /dev/null +++ b/test/integration/rhel-7.6/macro-callbacks.patch @@ -0,0 +1,160 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +--- src.old/fs/aio.c 2018-02-26 11:07:51.522610407 -0500 ++++ src/fs/aio.c 2018-03-05 11:17:21.560015449 -0500 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +--- src.old/drivers/input/joydev.c 2018-02-26 11:07:49.470610407 -0500 ++++ src/drivers/input/joydev.c 2018-03-05 11:18:13.998015449 -0500 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +--- src.old/drivers/input/misc/pcspkr.c 2018-02-26 11:07:49.477610407 -0500 ++++ src/drivers/input/misc/pcspkr.c 2018-03-05 11:18:23.411015449 -0500 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/rhel-7.6/macro-printk.patch b/test/integration/rhel-7.6/macro-printk.patch new file mode 100644 index 0000000..e837173 --- /dev/null +++ b/test/integration/rhel-7.6/macro-printk.patch @@ -0,0 +1,151 @@ +Index: kernel-rhel7/net/ipv4/fib_frontend.c +=================================================================== +--- kernel-rhel7.orig/net/ipv4/fib_frontend.c ++++ kernel-rhel7/net/ipv4/fib_frontend.c +@@ -685,6 +685,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -703,6 +704,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +Index: kernel-rhel7/net/ipv4/fib_semantics.c +=================================================================== +--- kernel-rhel7.orig/net/ipv4/fib_semantics.c ++++ kernel-rhel7/net/ipv4/fib_semantics.c +@@ -969,6 +969,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -993,6 +994,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1013,6 +1015,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -1028,6 +1031,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1043,8 +1048,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1095,6 +1102,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1112,6 +1120,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1134,6 +1143,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1146,6 +1156,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1157,6 +1168,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1180,6 +1192,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1190,6 +1203,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +Index: kernel-rhel7/net/ipv4/fib_trie.c +=================================================================== +--- kernel-rhel7.orig/net/ipv4/fib_trie.c ++++ kernel-rhel7/net/ipv4/fib_trie.c +@@ -1105,6 +1105,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1130,11 +1131,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-7.6/meminfo-init-FAIL.patch b/test/integration/rhel-7.6/meminfo-init-FAIL.patch new file mode 100644 index 0000000..3b8672d --- /dev/null +++ b/test/integration/rhel-7.6/meminfo-init-FAIL.patch @@ -0,0 +1,12 @@ +Index: kernel-rhel7/fs/proc/meminfo.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/meminfo.c ++++ kernel-rhel7/fs/proc/meminfo.c +@@ -199,6 +199,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.6/meminfo-init2-FAIL.patch b/test/integration/rhel-7.6/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..cd70ef9 --- /dev/null +++ b/test/integration/rhel-7.6/meminfo-init2-FAIL.patch @@ -0,0 +1,20 @@ +Index: kernel-rhel7/fs/proc/meminfo.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/meminfo.c ++++ kernel-rhel7/fs/proc/meminfo.c +@@ -30,6 +30,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -199,6 +200,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.6/meminfo-string-LOADED.test b/test/integration/rhel-7.6/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.6/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.6/meminfo-string.patch b/test/integration/rhel-7.6/meminfo-string.patch new file mode 100644 index 0000000..afdc5d0 --- /dev/null +++ b/test/integration/rhel-7.6/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:41.274137239 -0400 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/rhel-7.6/module-call-external.patch b/test/integration/rhel-7.6/module-call-external.patch new file mode 100644 index 0000000..f149edf --- /dev/null +++ b/test/integration/rhel-7.6/module-call-external.patch @@ -0,0 +1,40 @@ +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index 27459a453bb8..2247255877be 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1184,7 +1184,13 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++extern char *kpatch_string(void); ++ ++#ifdef CONFIG_PPC64 ++static int __attribute__((optimize("-fno-optimize-sibling-calls"))) e_show(struct seq_file *m, void *p) ++#else + static int e_show(struct seq_file *m, void *p) ++#endif + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); +@@ -1193,6 +1199,7 @@ static int e_show(struct seq_file *m, void *p) + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 592f64643491..21b87cb948c9 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2568,4 +2568,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.6/multiple.test b/test/integration/rhel-7.6/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.6/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.6/new-function.patch b/test/integration/rhel-7.6/new-function.patch new file mode 100644 index 0000000..a9a0724 --- /dev/null +++ b/test/integration/rhel-7.6/new-function.patch @@ -0,0 +1,26 @@ +Index: kernel-rhel7/drivers/tty/n_tty.c +=================================================================== +--- kernel-rhel7.orig/drivers/tty/n_tty.c ++++ kernel-rhel7/drivers/tty/n_tty.c +@@ -2128,7 +2128,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2210,6 +2210,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.6/new-globals.patch b/test/integration/rhel-7.6/new-globals.patch new file mode 100644 index 0000000..3d9d349 --- /dev/null +++ b/test/integration/rhel-7.6/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:47.028161067 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:47.029161071 -0400 +@@ -16,6 +16,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.6/parainstructions-section.patch b/test/integration/rhel-7.6/parainstructions-section.patch new file mode 100644 index 0000000..809dce4 --- /dev/null +++ b/test/integration/rhel-7.6/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/generic.c 2017-09-22 15:27:48.190165879 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.6/shadow-newpid-LOADED.test b/test/integration/rhel-7.6/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.6/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.6/shadow-newpid.patch b/test/integration/rhel-7.6/shadow-newpid.patch new file mode 100644 index 0000000..17bf527 --- /dev/null +++ b/test/integration/rhel-7.6/shadow-newpid.patch @@ -0,0 +1,72 @@ +diff --git a/fs/proc/array.c b/fs/proc/array.c +index 39684c79e8e2..138b60d1314d 100644 +--- a/fs/proc/array.c ++++ b/fs/proc/array.c +@@ -394,13 +394,20 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p) + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff --git a/kernel/exit.c b/kernel/exit.c +index 148a7842928d..e1dbab71b37d 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -791,6 +791,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +889,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff --git a/kernel/fork.c b/kernel/fork.c +index 9bff3b28c357..529a2c943d7c 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1757,6 +1757,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1794,6 +1795,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.6/smp-locks-section.patch b/test/integration/rhel-7.6/smp-locks-section.patch new file mode 100644 index 0000000..6f39d53 --- /dev/null +++ b/test/integration/rhel-7.6/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2017-09-22 15:27:21.077053604 -0400 ++++ src/drivers/tty/tty_buffer.c 2017-09-22 15:27:50.542175618 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.6/special-static.patch b/test/integration/rhel-7.6/special-static.patch new file mode 100644 index 0000000..7fa529f --- /dev/null +++ b/test/integration/rhel-7.6/special-static.patch @@ -0,0 +1,23 @@ +Index: kernel-rhel7/kernel/fork.c +=================================================================== +--- kernel-rhel7.orig/kernel/fork.c ++++ kernel-rhel7/kernel/fork.c +@@ -1146,10 +1146,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.6/symvers-disagreement-FAIL.patch b/test/integration/rhel-7.6/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..d8ef754 --- /dev/null +++ b/test/integration/rhel-7.6/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From bcb86fa4e9c31379a9e2716eae29cd53ccca064f Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index b9a71137208..4af27e069c2 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index f9db3660999..bece0bf4f5a 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -691,6 +691,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.1 + diff --git a/test/integration/rhel-7.6/tracepoints-section.patch b/test/integration/rhel-7.6/tracepoints-section.patch new file mode 100644 index 0000000..512e512 --- /dev/null +++ b/test/integration/rhel-7.6/tracepoints-section.patch @@ -0,0 +1,14 @@ +Index: kernel-rhel7/kernel/timer.c +=================================================================== +--- kernel-rhel7.orig/kernel/timer.c ++++ kernel-rhel7/kernel/timer.c +@@ -1454,6 +1454,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.6/warn-detect-FAIL.patch b/test/integration/rhel-7.6/warn-detect-FAIL.patch new file mode 100644 index 0000000..8efa782 --- /dev/null +++ b/test/integration/rhel-7.6/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:55.489196104 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.7/bug-table-section.patch b/test/integration/rhel-7.7/bug-table-section.patch new file mode 100644 index 0000000..34342c0 --- /dev/null +++ b/test/integration/rhel-7.7/bug-table-section.patch @@ -0,0 +1,13 @@ +Index: kernel/fs/proc/proc_sysctl.c +=================================================================== +--- kernel.orig/fs/proc/proc_sysctl.c ++++ kernel/fs/proc/proc_sysctl.c +@@ -301,6 +301,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.7/cmdline-string-LOADED.test b/test/integration/rhel-7.7/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.7/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.7/cmdline-string.patch b/test/integration/rhel-7.7/cmdline-string.patch new file mode 100644 index 0000000..f1f12b3 --- /dev/null +++ b/test/integration/rhel-7.7/cmdline-string.patch @@ -0,0 +1,13 @@ +Index: kernel/fs/proc/cmdline.c +=================================================================== +--- kernel.orig/fs/proc/cmdline.c ++++ kernel/fs/proc/cmdline.c +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.7/data-new-LOADED.test b/test/integration/rhel-7.7/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.7/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.7/data-new.patch b/test/integration/rhel-7.7/data-new.patch new file mode 100644 index 0000000..4f9abcf --- /dev/null +++ b/test/integration/rhel-7.7/data-new.patch @@ -0,0 +1,29 @@ +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -20,6 +20,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -110,6 +112,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -175,6 +178,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.7/data-read-mostly.patch.disabled b/test/integration/rhel-7.7/data-read-mostly.patch.disabled new file mode 100644 index 0000000..5e55826 --- /dev/null +++ b/test/integration/rhel-7.7/data-read-mostly.patch.disabled @@ -0,0 +1,12 @@ +Index: kernel/net/core/dev.c +=================================================================== +--- kernel.orig/net/core/dev.c ++++ kernel/net/core/dev.c +@@ -4327,6 +4327,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.7/fixup-section.patch b/test/integration/rhel-7.7/fixup-section.patch new file mode 100644 index 0000000..27c01cf --- /dev/null +++ b/test/integration/rhel-7.7/fixup-section.patch @@ -0,0 +1,12 @@ +Index: kernel/fs/readdir.c +=================================================================== +--- kernel.orig/fs/readdir.c ++++ kernel/fs/readdir.c +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.7/gcc-constprop.patch.disabled b/test/integration/rhel-7.7/gcc-constprop.patch.disabled new file mode 100644 index 0000000..63ba45d --- /dev/null +++ b/test/integration/rhel-7.7/gcc-constprop.patch.disabled @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/time/timekeeping.c 2017-09-22 15:27:27.522080292 -0400 +@@ -877,6 +877,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.7/gcc-isra.patch b/test/integration/rhel-7.7/gcc-isra.patch new file mode 100644 index 0000000..ce5434a --- /dev/null +++ b/test/integration/rhel-7.7/gcc-isra.patch @@ -0,0 +1,12 @@ +Index: kernel/fs/proc/proc_sysctl.c +=================================================================== +--- kernel.orig/fs/proc/proc_sysctl.c ++++ kernel/fs/proc/proc_sysctl.c +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.7/gcc-mangled-3.patch b/test/integration/rhel-7.7/gcc-mangled-3.patch new file mode 100644 index 0000000..7dbfc62 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-mangled-3.patch @@ -0,0 +1,14 @@ +Index: kernel/mm/slub.c +=================================================================== +--- kernel.orig/mm/slub.c ++++ kernel/mm/slub.c +@@ -5675,6 +5675,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.7/gcc-static-local-var-2.patch b/test/integration/rhel-7.7/gcc-static-local-var-2.patch new file mode 100644 index 0000000..d1b70f1 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-2.patch @@ -0,0 +1,14 @@ +Index: kernel/mm/mmap.c +=================================================================== +--- kernel.orig/mm/mmap.c ++++ kernel/mm/mmap.c +@@ -1716,6 +1716,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.7/gcc-static-local-var-3.patch b/test/integration/rhel-7.7/gcc-static-local-var-3.patch new file mode 100644 index 0000000..584c869 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-3.patch @@ -0,0 +1,20 @@ +Index: kernel/kernel/sys.c +=================================================================== +--- kernel.orig/kernel/sys.c ++++ kernel/kernel/sys.c +@@ -559,8 +559,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.7/gcc-static-local-var-4.patch b/test/integration/rhel-7.7/gcc-static-local-var-4.patch new file mode 100644 index 0000000..85e4df5 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-4.patch @@ -0,0 +1,21 @@ +Index: kernel/fs/aio.c +=================================================================== +--- kernel.orig/fs/aio.c ++++ kernel/fs/aio.c +@@ -223,9 +223,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.7/gcc-static-local-var-4.test b/test/integration/rhel-7.7/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.7/gcc-static-local-var-5.patch b/test/integration/rhel-7.7/gcc-static-local-var-5.patch new file mode 100644 index 0000000..3028676 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-5.patch @@ -0,0 +1,46 @@ +Index: kernel/kernel/audit.c +=================================================================== +--- kernel.orig/kernel/audit.c ++++ kernel/kernel/audit.c +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.7/gcc-static-local-var-6.patch b/test/integration/rhel-7.7/gcc-static-local-var-6.patch new file mode 100644 index 0000000..53c3946 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +Index: kernel/net/ipv6/netfilter.c +=================================================================== +--- kernel.orig/net/ipv6/netfilter.c ++++ kernel/net/ipv6/netfilter.c +@@ -112,6 +112,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -125,6 +127,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.7/macro-callbacks.patch b/test/integration/rhel-7.7/macro-callbacks.patch new file mode 100644 index 0000000..d6757e0 --- /dev/null +++ b/test/integration/rhel-7.7/macro-callbacks.patch @@ -0,0 +1,166 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +Index: kernel/fs/aio.c +=================================================================== +--- kernel.orig/fs/aio.c ++++ kernel/fs/aio.c +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +Index: kernel/drivers/input/joydev.c +=================================================================== +--- kernel.orig/drivers/input/joydev.c ++++ kernel/drivers/input/joydev.c +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +Index: kernel/drivers/input/misc/pcspkr.c +=================================================================== +--- kernel.orig/drivers/input/misc/pcspkr.c ++++ kernel/drivers/input/misc/pcspkr.c +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/rhel-7.7/macro-printk.patch b/test/integration/rhel-7.7/macro-printk.patch new file mode 100644 index 0000000..a4ff62c --- /dev/null +++ b/test/integration/rhel-7.7/macro-printk.patch @@ -0,0 +1,151 @@ +Index: kernel/net/ipv4/fib_frontend.c +=================================================================== +--- kernel.orig/net/ipv4/fib_frontend.c ++++ kernel/net/ipv4/fib_frontend.c +@@ -686,6 +686,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -704,6 +705,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +Index: kernel/net/ipv4/fib_semantics.c +=================================================================== +--- kernel.orig/net/ipv4/fib_semantics.c ++++ kernel/net/ipv4/fib_semantics.c +@@ -985,6 +985,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -1009,6 +1010,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1029,6 +1031,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -1044,6 +1047,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1059,8 +1064,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1111,6 +1118,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1128,6 +1136,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1150,6 +1159,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1162,6 +1172,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1173,6 +1184,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1196,6 +1208,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1206,6 +1219,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +Index: kernel/net/ipv4/fib_trie.c +=================================================================== +--- kernel.orig/net/ipv4/fib_trie.c ++++ kernel/net/ipv4/fib_trie.c +@@ -1105,6 +1105,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1130,11 +1131,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-7.7/meminfo-init-FAIL.patch b/test/integration/rhel-7.7/meminfo-init-FAIL.patch new file mode 100644 index 0000000..f3e6c73 --- /dev/null +++ b/test/integration/rhel-7.7/meminfo-init-FAIL.patch @@ -0,0 +1,12 @@ +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -199,6 +199,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.7/meminfo-init2-FAIL.patch b/test/integration/rhel-7.7/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..e225294 --- /dev/null +++ b/test/integration/rhel-7.7/meminfo-init2-FAIL.patch @@ -0,0 +1,20 @@ +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -30,6 +30,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -199,6 +200,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.7/meminfo-string-LOADED.test b/test/integration/rhel-7.7/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.7/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.7/meminfo-string.patch b/test/integration/rhel-7.7/meminfo-string.patch new file mode 100644 index 0000000..23ff7a7 --- /dev/null +++ b/test/integration/rhel-7.7/meminfo-string.patch @@ -0,0 +1,13 @@ +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/rhel-7.7/module-call-external.patch b/test/integration/rhel-7.7/module-call-external.patch new file mode 100644 index 0000000..f149edf --- /dev/null +++ b/test/integration/rhel-7.7/module-call-external.patch @@ -0,0 +1,40 @@ +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index 27459a453bb8..2247255877be 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1184,7 +1184,13 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++extern char *kpatch_string(void); ++ ++#ifdef CONFIG_PPC64 ++static int __attribute__((optimize("-fno-optimize-sibling-calls"))) e_show(struct seq_file *m, void *p) ++#else + static int e_show(struct seq_file *m, void *p) ++#endif + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); +@@ -1193,6 +1199,7 @@ static int e_show(struct seq_file *m, void *p) + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 592f64643491..21b87cb948c9 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2568,4 +2568,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.7/multiple.test b/test/integration/rhel-7.7/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.7/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.7/new-function.patch b/test/integration/rhel-7.7/new-function.patch new file mode 100644 index 0000000..604dbe5 --- /dev/null +++ b/test/integration/rhel-7.7/new-function.patch @@ -0,0 +1,26 @@ +Index: kernel/drivers/tty/n_tty.c +=================================================================== +--- kernel.orig/drivers/tty/n_tty.c ++++ kernel/drivers/tty/n_tty.c +@@ -2175,7 +2175,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2264,6 +2264,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.7/new-globals.patch b/test/integration/rhel-7.7/new-globals.patch new file mode 100644 index 0000000..e18d09d --- /dev/null +++ b/test/integration/rhel-7.7/new-globals.patch @@ -0,0 +1,36 @@ +Index: kernel/fs/proc/cmdline.c +=================================================================== +--- kernel.orig/fs/proc/cmdline.c ++++ kernel/fs/proc/cmdline.c +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -16,6 +16,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.7/parainstructions-section.patch b/test/integration/rhel-7.7/parainstructions-section.patch new file mode 100644 index 0000000..151d263 --- /dev/null +++ b/test/integration/rhel-7.7/parainstructions-section.patch @@ -0,0 +1,12 @@ +Index: kernel/fs/proc/generic.c +=================================================================== +--- kernel.orig/fs/proc/generic.c ++++ kernel/fs/proc/generic.c +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.7/shadow-newpid-LOADED.test b/test/integration/rhel-7.7/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.7/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.7/shadow-newpid.patch b/test/integration/rhel-7.7/shadow-newpid.patch new file mode 100644 index 0000000..cae690d --- /dev/null +++ b/test/integration/rhel-7.7/shadow-newpid.patch @@ -0,0 +1,72 @@ +Index: kernel/fs/proc/array.c +=================================================================== +--- kernel.orig/fs/proc/array.c ++++ kernel/fs/proc/array.c +@@ -395,13 +395,20 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +Index: kernel/kernel/exit.c +=================================================================== +--- kernel.orig/kernel/exit.c ++++ kernel/kernel/exit.c +@@ -791,6 +791,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +889,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +Index: kernel/kernel/fork.c +=================================================================== +--- kernel.orig/kernel/fork.c ++++ kernel/kernel/fork.c +@@ -1760,6 +1760,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1797,6 +1798,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.7/smp-locks-section.patch b/test/integration/rhel-7.7/smp-locks-section.patch new file mode 100644 index 0000000..7aa1c31 --- /dev/null +++ b/test/integration/rhel-7.7/smp-locks-section.patch @@ -0,0 +1,15 @@ +Index: kernel/drivers/tty/tty_buffer.c +=================================================================== +--- kernel.orig/drivers/tty/tty_buffer.c ++++ kernel/drivers/tty/tty_buffer.c +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.7/special-static.patch b/test/integration/rhel-7.7/special-static.patch new file mode 100644 index 0000000..8a757ba --- /dev/null +++ b/test/integration/rhel-7.7/special-static.patch @@ -0,0 +1,23 @@ +Index: kernel/kernel/fork.c +=================================================================== +--- kernel.orig/kernel/fork.c ++++ kernel/kernel/fork.c +@@ -1146,10 +1146,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.7/symvers-disagreement-FAIL.patch b/test/integration/rhel-7.7/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..e940645 --- /dev/null +++ b/test/integration/rhel-7.7/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From d5513eae5155c6e7e884554d5e3e2c65a7b39cbe Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index b9a71137208..4af27e069c2 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index d6337db2164..0ff9722dfa2 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -693,6 +693,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-7.7/tracepoints-section.patch b/test/integration/rhel-7.7/tracepoints-section.patch new file mode 100644 index 0000000..b992e14 --- /dev/null +++ b/test/integration/rhel-7.7/tracepoints-section.patch @@ -0,0 +1,14 @@ +Index: kernel/kernel/timer.c +=================================================================== +--- kernel.orig/kernel/timer.c ++++ kernel/kernel/timer.c +@@ -1454,6 +1454,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.7/warn-detect-FAIL.patch b/test/integration/rhel-7.7/warn-detect-FAIL.patch new file mode 100644 index 0000000..801fa4c --- /dev/null +++ b/test/integration/rhel-7.7/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +Index: kernel/arch/x86/kvm/x86.c +=================================================================== +--- kernel.orig/arch/x86/kvm/x86.c ++++ kernel/arch/x86/kvm/x86.c +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.8/bug-table-section.patch b/test/integration/rhel-7.8/bug-table-section.patch new file mode 100644 index 0000000..864ad9b --- /dev/null +++ b/test/integration/rhel-7.8/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-03-10 10:35:57.040558842 -0400 +@@ -331,6 +331,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.8/cmdline-string-LOADED.test b/test/integration/rhel-7.8/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.8/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.8/cmdline-string.patch b/test/integration/rhel-7.8/cmdline-string.patch new file mode 100644 index 0000000..0c691b1 --- /dev/null +++ b/test/integration/rhel-7.8/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-03-10 10:35:54.567563836 -0400 ++++ src/fs/proc/cmdline.c 2020-03-10 10:36:03.207546389 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.8/data-new-LOADED.test b/test/integration/rhel-7.8/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.8/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.8/data-new.patch b/test/integration/rhel-7.8/data-new.patch new file mode 100644 index 0000000..e213f82 --- /dev/null +++ b/test/integration/rhel-7.8/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/meminfo.c 2020-03-10 10:36:07.968536775 -0400 +@@ -21,6 +21,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -112,6 +114,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -178,6 +181,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.8/data-read-mostly.patch.disabled b/test/integration/rhel-7.8/data-read-mostly.patch.disabled new file mode 100644 index 0000000..12f3b0c --- /dev/null +++ b/test/integration/rhel-7.8/data-read-mostly.patch.disabled @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2020-03-10 10:35:55.176562607 -0400 ++++ src/net/core/dev.c 2020-03-10 10:37:54.458302249 -0400 +@@ -4327,6 +4327,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.8/fixup-section.patch b/test/integration/rhel-7.8/fixup-section.patch new file mode 100644 index 0000000..65b3ed5 --- /dev/null +++ b/test/integration/rhel-7.8/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2020-03-10 10:35:54.574563822 -0400 ++++ src/fs/readdir.c 2020-03-10 10:36:12.621527378 -0400 +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.8/gcc-constprop.patch.disabled b/test/integration/rhel-7.8/gcc-constprop.patch.disabled new file mode 100644 index 0000000..609f52d --- /dev/null +++ b/test/integration/rhel-7.8/gcc-constprop.patch.disabled @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2020-03-10 10:35:55.091562778 -0400 ++++ src/kernel/time/timekeeping.c 2020-03-10 10:37:59.105290886 -0400 +@@ -852,6 +852,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.8/gcc-isra.patch b/test/integration/rhel-7.8/gcc-isra.patch new file mode 100644 index 0000000..dc58bdb --- /dev/null +++ b/test/integration/rhel-7.8/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-03-10 10:36:17.393517742 -0400 +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.8/gcc-mangled-3.patch b/test/integration/rhel-7.8/gcc-mangled-3.patch new file mode 100644 index 0000000..29477f1 --- /dev/null +++ b/test/integration/rhel-7.8/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2020-03-10 10:35:55.138562683 -0400 ++++ src/mm/slub.c 2020-03-10 10:36:22.189508057 -0400 +@@ -5716,6 +5716,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.8/gcc-static-local-var-2.patch b/test/integration/rhel-7.8/gcc-static-local-var-2.patch new file mode 100644 index 0000000..34087aa --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2020-03-10 10:35:55.133562693 -0400 ++++ src/mm/mmap.c 2020-03-10 10:36:26.787498772 -0400 +@@ -1721,6 +1721,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.8/gcc-static-local-var-3.patch b/test/integration/rhel-7.8/gcc-static-local-var-3.patch new file mode 100644 index 0000000..3f9723b --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2020-03-10 10:35:55.088562784 -0400 ++++ src/kernel/sys.c 2020-03-10 10:36:31.420489416 -0400 +@@ -559,8 +559,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.8/gcc-static-local-var-4.patch b/test/integration/rhel-7.8/gcc-static-local-var-4.patch new file mode 100644 index 0000000..0533a9f --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-03-10 10:35:54.403564168 -0400 ++++ src/fs/aio.c 2020-03-10 10:36:36.025480117 -0400 +@@ -223,9 +223,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.8/gcc-static-local-var-4.test b/test/integration/rhel-7.8/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.8/gcc-static-local-var-5.patch b/test/integration/rhel-7.8/gcc-static-local-var-5.patch new file mode 100644 index 0000000..12a072a --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2020-03-10 10:35:55.059562843 -0400 ++++ src/kernel/audit.c 2020-03-10 10:36:41.387468209 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.8/gcc-static-local-var-6.patch b/test/integration/rhel-7.8/gcc-static-local-var-6.patch new file mode 100644 index 0000000..81e7ad1 --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2020-03-10 10:35:55.213562532 -0400 ++++ src/net/ipv6/netfilter.c 2020-03-10 10:36:47.062455553 -0400 +@@ -112,6 +112,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -125,6 +127,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.8/macro-callbacks.patch b/test/integration/rhel-7.8/macro-callbacks.patch new file mode 100644 index 0000000..0ca7098 --- /dev/null +++ b/test/integration/rhel-7.8/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2020-03-10 10:35:52.635567738 -0400 ++++ src/drivers/input/joydev.c 2020-03-10 10:36:51.651445319 -0400 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2020-03-10 10:35:52.650567707 -0400 ++++ src/drivers/input/misc/pcspkr.c 2020-03-10 10:36:51.652445316 -0400 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-03-10 10:35:54.403564168 -0400 ++++ src/fs/aio.c 2020-03-10 10:36:51.651445319 -0400 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-7.8/macro-printk.patch b/test/integration/rhel-7.8/macro-printk.patch new file mode 100644 index 0000000..f360d82 --- /dev/null +++ b/test/integration/rhel-7.8/macro-printk.patch @@ -0,0 +1,148 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2020-03-10 10:35:55.194562570 -0400 ++++ src/net/ipv4/fib_frontend.c 2020-03-10 10:36:56.379434774 -0400 +@@ -690,6 +690,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -708,6 +709,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2020-03-10 10:35:55.194562570 -0400 ++++ src/net/ipv4/fib_semantics.c 2020-03-10 10:36:56.380434772 -0400 +@@ -985,6 +985,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -1009,6 +1010,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1029,6 +1031,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -1044,6 +1047,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1059,8 +1064,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1111,6 +1118,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1128,6 +1136,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1150,6 +1159,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1162,6 +1172,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1173,6 +1184,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1196,6 +1208,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1206,6 +1219,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2020-03-10 10:35:55.195562568 -0400 ++++ src/net/ipv4/fib_trie.c 2020-03-10 10:36:56.381434770 -0400 +@@ -1105,6 +1105,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1130,11 +1131,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-7.8/meminfo-init-FAIL.patch b/test/integration/rhel-7.8/meminfo-init-FAIL.patch new file mode 100644 index 0000000..05f0c37 --- /dev/null +++ b/test/integration/rhel-7.8/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/meminfo.c 2020-03-10 10:37:07.161410729 -0400 +@@ -202,6 +202,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.8/meminfo-init2-FAIL.patch b/test/integration/rhel-7.8/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..ad0664b --- /dev/null +++ b/test/integration/rhel-7.8/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/meminfo.c 2020-03-10 10:37:00.986424500 -0400 +@@ -31,6 +31,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -202,6 +203,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.8/meminfo-string-LOADED.test b/test/integration/rhel-7.8/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.8/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.8/meminfo-string.patch b/test/integration/rhel-7.8/meminfo-string.patch new file mode 100644 index 0000000..33e3a17 --- /dev/null +++ b/test/integration/rhel-7.8/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr linux-3.10.0-1127.fc30.x86_64.orig/fs/proc/meminfo.c linux-3.10.0-1127.fc30.x86_64/fs/proc/meminfo.c +--- linux-3.10.0-1127.fc30.x86_64.orig/fs/proc/meminfo.c 2020-03-10 09:40:37.849666782 -0400 ++++ linux-3.10.0-1127.fc30.x86_64/fs/proc/meminfo.c 2020-03-10 09:50:26.871990501 -0400 +@@ -100,7 +100,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + "Percpu: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" diff --git a/test/integration/rhel-7.8/module-call-external.patch b/test/integration/rhel-7.8/module-call-external.patch new file mode 100644 index 0000000..b1c594d --- /dev/null +++ b/test/integration/rhel-7.8/module-call-external.patch @@ -0,0 +1,38 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2020-03-10 10:35:54.521563929 -0400 ++++ src/fs/nfsd/export.c 2020-03-10 10:37:11.704400597 -0400 +@@ -1184,7 +1184,13 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ ++#ifdef CONFIG_PPC64 ++static int __attribute__((optimize("-fno-optimize-sibling-calls"))) e_show(struct seq_file *m, void *p) ++#else + static int e_show(struct seq_file *m, void *p) ++#endif + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); +@@ -1193,6 +1199,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2020-03-10 10:35:56.447560040 -0400 ++++ src/net/netlink/af_netlink.c 2020-03-10 10:37:11.705400594 -0400 +@@ -2568,4 +2568,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.8/multiple.test b/test/integration/rhel-7.8/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.8/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.8/new-function.patch b/test/integration/rhel-7.8/new-function.patch new file mode 100644 index 0000000..bcd6643 --- /dev/null +++ b/test/integration/rhel-7.8/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2020-03-10 10:35:54.135564709 -0400 ++++ src/drivers/tty/n_tty.c 2020-03-10 10:37:16.551389787 -0400 +@@ -2175,7 +2175,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2264,6 +2264,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.8/new-globals.patch b/test/integration/rhel-7.8/new-globals.patch new file mode 100644 index 0000000..49e98e6 --- /dev/null +++ b/test/integration/rhel-7.8/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-03-10 10:35:54.567563836 -0400 ++++ src/fs/proc/cmdline.c 2020-03-10 10:37:21.219379377 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/meminfo.c 2020-03-10 10:37:21.220379374 -0400 +@@ -17,6 +17,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -54,6 +56,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.8/parainstructions-section.patch b/test/integration/rhel-7.8/parainstructions-section.patch new file mode 100644 index 0000000..5f801c4 --- /dev/null +++ b/test/integration/rhel-7.8/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2020-03-10 10:35:54.567563836 -0400 ++++ src/fs/proc/generic.c 2020-03-10 10:37:25.973368774 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.8/shadow-newpid-LOADED.test b/test/integration/rhel-7.8/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.8/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.8/shadow-newpid.patch b/test/integration/rhel-7.8/shadow-newpid.patch new file mode 100644 index 0000000..e8a4d78 --- /dev/null +++ b/test/integration/rhel-7.8/shadow-newpid.patch @@ -0,0 +1,69 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2020-03-10 10:35:54.566563838 -0400 ++++ src/fs/proc/array.c 2020-03-10 10:37:30.688358259 -0400 +@@ -395,13 +395,20 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2020-03-10 10:35:55.068562825 -0400 ++++ src/kernel/exit.c 2020-03-10 10:37:30.689358257 -0400 +@@ -791,6 +791,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +889,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-03-10 10:35:55.068562825 -0400 ++++ src/kernel/fork.c 2020-03-10 10:37:30.690358255 -0400 +@@ -1784,6 +1784,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1821,6 +1822,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.8/smp-locks-section.patch b/test/integration/rhel-7.8/smp-locks-section.patch new file mode 100644 index 0000000..6f9700d --- /dev/null +++ b/test/integration/rhel-7.8/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2020-03-10 10:35:54.155564668 -0400 ++++ src/drivers/tty/tty_buffer.c 2020-03-10 10:37:35.446347648 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.8/special-static.patch b/test/integration/rhel-7.8/special-static.patch new file mode 100644 index 0000000..02bd105 --- /dev/null +++ b/test/integration/rhel-7.8/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-03-10 10:35:55.068562825 -0400 ++++ src/kernel/fork.c 2020-03-10 10:37:40.182337086 -0400 +@@ -1153,10 +1153,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.8/symvers-disagreement-FAIL.patch b/test/integration/rhel-7.8/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..300f8b9 --- /dev/null +++ b/test/integration/rhel-7.8/symvers-disagreement-FAIL.patch @@ -0,0 +1,49 @@ +From da109d66a890373d0aa831f97b49aaffcc4eeb45 Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes a function referencing a function whose CRC has changed, + causing the symbol and the new CRC to be included in the __version + section of the final module. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index b9a71137208..4af27e069c2 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 8b1c4a5ee78..1b7997b58c0 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -693,6 +693,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-7.8/tracepoints-section.patch b/test/integration/rhel-7.8/tracepoints-section.patch new file mode 100644 index 0000000..20e9b79 --- /dev/null +++ b/test/integration/rhel-7.8/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/timer.c src/kernel/timer.c +--- src.orig/kernel/timer.c 2020-03-10 10:35:55.092562776 -0400 ++++ src/kernel/timer.c 2020-03-10 10:37:44.918325578 -0400 +@@ -1454,6 +1454,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.8/warn-detect-FAIL.patch b/test/integration/rhel-7.8/warn-detect-FAIL.patch new file mode 100644 index 0000000..d2792db --- /dev/null +++ b/test/integration/rhel-7.8/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2020-03-10 10:35:51.514570002 -0400 ++++ src/arch/x86/kvm/x86.c 2020-03-10 10:37:49.745313774 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.9/bug-table-section.patch b/test/integration/rhel-7.9/bug-table-section.patch new file mode 100644 index 0000000..92e66d4 --- /dev/null +++ b/test/integration/rhel-7.9/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-09-03 11:48:31.009727724 -0400 +@@ -331,6 +331,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.9/cmdline-string-LOADED.test b/test/integration/rhel-7.9/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.9/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.9/cmdline-string.patch b/test/integration/rhel-7.9/cmdline-string.patch new file mode 100644 index 0000000..719445d --- /dev/null +++ b/test/integration/rhel-7.9/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-09-03 11:48:30.496726119 -0400 ++++ src/fs/proc/cmdline.c 2020-09-03 11:48:33.073734181 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.9/data-new-LOADED.test b/test/integration/rhel-7.9/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.9/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.9/data-new.patch b/test/integration/rhel-7.9/data-new.patch new file mode 100644 index 0000000..2e4e824 --- /dev/null +++ b/test/integration/rhel-7.9/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:48:35.069740426 -0400 +@@ -21,6 +21,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -112,6 +114,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -178,6 +181,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.9/data-read-mostly.patch.disabled b/test/integration/rhel-7.9/data-read-mostly.patch.disabled new file mode 100644 index 0000000..50f4c9b --- /dev/null +++ b/test/integration/rhel-7.9/data-read-mostly.patch.disabled @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2020-09-03 11:48:30.763726955 -0400 ++++ src/net/core/dev.c 2020-09-03 11:49:21.514885728 -0400 +@@ -4327,6 +4327,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.9/fixup-section.patch b/test/integration/rhel-7.9/fixup-section.patch new file mode 100644 index 0000000..465e23a --- /dev/null +++ b/test/integration/rhel-7.9/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2020-09-03 11:48:30.499726129 -0400 ++++ src/fs/readdir.c 2020-09-03 11:48:37.119746839 -0400 +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.9/gcc-constprop.patch b/test/integration/rhel-7.9/gcc-constprop.patch new file mode 100644 index 0000000..4ab8c1b --- /dev/null +++ b/test/integration/rhel-7.9/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2020-09-03 11:48:30.726726839 -0400 ++++ src/kernel/time/timekeeping.c 2020-09-03 11:49:23.433891731 -0400 +@@ -852,6 +852,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.9/gcc-isra.patch b/test/integration/rhel-7.9/gcc-isra.patch new file mode 100644 index 0000000..6996eeb --- /dev/null +++ b/test/integration/rhel-7.9/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-09-03 11:48:39.089753002 -0400 +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.9/gcc-mangled-3.patch b/test/integration/rhel-7.9/gcc-mangled-3.patch new file mode 100644 index 0000000..5a7904e --- /dev/null +++ b/test/integration/rhel-7.9/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2020-09-03 11:48:30.747726905 -0400 ++++ src/mm/slub.c 2020-09-03 11:48:41.106759312 -0400 +@@ -5716,6 +5716,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.9/gcc-static-local-var-2.patch b/test/integration/rhel-7.9/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a3e8366 --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2020-09-03 11:48:30.745726898 -0400 ++++ src/mm/mmap.c 2020-09-03 11:48:43.078765482 -0400 +@@ -1721,6 +1721,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.9/gcc-static-local-var-3.patch b/test/integration/rhel-7.9/gcc-static-local-var-3.patch new file mode 100644 index 0000000..6d8ae91 --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2020-09-03 11:48:30.725726836 -0400 ++++ src/kernel/sys.c 2020-09-03 11:48:45.101771811 -0400 +@@ -559,8 +559,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.9/gcc-static-local-var-4.patch b/test/integration/rhel-7.9/gcc-static-local-var-4.patch new file mode 100644 index 0000000..d63af5b --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-09-03 11:48:30.426725900 -0400 ++++ src/fs/aio.c 2020-09-03 11:48:47.163778261 -0400 +@@ -223,9 +223,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.9/gcc-static-local-var-4.test b/test/integration/rhel-7.9/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.9/gcc-static-local-var-5.patch b/test/integration/rhel-7.9/gcc-static-local-var-5.patch new file mode 100644 index 0000000..52d7f8b --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2020-09-03 11:48:30.713726798 -0400 ++++ src/kernel/audit.c 2020-09-03 11:48:49.166784528 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.9/gcc-static-local-var-6.patch b/test/integration/rhel-7.9/gcc-static-local-var-6.patch new file mode 100644 index 0000000..2b33da4 --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2020-09-03 11:48:30.779727005 -0400 ++++ src/net/ipv6/netfilter.c 2020-09-03 11:48:51.172790803 -0400 +@@ -112,6 +112,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -125,6 +127,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.9/macro-callbacks.patch b/test/integration/rhel-7.9/macro-callbacks.patch new file mode 100644 index 0000000..96c1a3f --- /dev/null +++ b/test/integration/rhel-7.9/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2020-09-03 11:48:29.019721499 -0400 ++++ src/drivers/input/joydev.c 2020-09-03 11:48:53.152796998 -0400 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2020-09-03 11:48:29.025721517 -0400 ++++ src/drivers/input/misc/pcspkr.c 2020-09-03 11:48:53.152796998 -0400 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-09-03 11:48:30.426725900 -0400 ++++ src/fs/aio.c 2020-09-03 11:48:53.153797001 -0400 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-7.9/macro-printk.patch b/test/integration/rhel-7.9/macro-printk.patch new file mode 100644 index 0000000..28e618d --- /dev/null +++ b/test/integration/rhel-7.9/macro-printk.patch @@ -0,0 +1,148 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2020-09-03 11:48:30.771726980 -0400 ++++ src/net/ipv4/fib_frontend.c 2020-09-03 11:48:55.130803186 -0400 +@@ -690,6 +690,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -708,6 +709,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2020-09-03 11:48:30.771726980 -0400 ++++ src/net/ipv4/fib_semantics.c 2020-09-03 11:48:55.130803186 -0400 +@@ -985,6 +985,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -1009,6 +1010,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1029,6 +1031,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -1044,6 +1047,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1059,8 +1064,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1111,6 +1118,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1128,6 +1136,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1150,6 +1159,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1162,6 +1172,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1173,6 +1184,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1196,6 +1208,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1206,6 +1219,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2020-09-03 11:48:30.771726980 -0400 ++++ src/net/ipv4/fib_trie.c 2020-09-03 11:48:55.131803189 -0400 +@@ -1105,6 +1105,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1130,11 +1131,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-7.9/meminfo-init-FAIL.patch b/test/integration/rhel-7.9/meminfo-init-FAIL.patch new file mode 100644 index 0000000..ad677ca --- /dev/null +++ b/test/integration/rhel-7.9/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:48:59.106815625 -0400 +@@ -202,6 +202,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.9/meminfo-init2-FAIL.patch b/test/integration/rhel-7.9/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..792028e --- /dev/null +++ b/test/integration/rhel-7.9/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:48:57.163809546 -0400 +@@ -31,6 +31,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -202,6 +203,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.9/meminfo-string-LOADED.test b/test/integration/rhel-7.9/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.9/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.9/meminfo-string.patch b/test/integration/rhel-7.9/meminfo-string.patch new file mode 100644 index 0000000..7c8114e --- /dev/null +++ b/test/integration/rhel-7.9/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:49:01.546823258 -0400 +@@ -100,7 +100,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + "Percpu: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" diff --git a/test/integration/rhel-7.9/module-call-external.patch b/test/integration/rhel-7.9/module-call-external.patch new file mode 100644 index 0000000..e346edc --- /dev/null +++ b/test/integration/rhel-7.9/module-call-external.patch @@ -0,0 +1,38 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2020-09-03 11:48:30.477726060 -0400 ++++ src/fs/nfsd/export.c 2020-09-03 11:49:03.743830132 -0400 +@@ -1184,7 +1184,13 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ ++#ifdef CONFIG_PPC64 ++static int __attribute__((optimize("-fno-optimize-sibling-calls"))) e_show(struct seq_file *m, void *p) ++#else + static int e_show(struct seq_file *m, void *p) ++#endif + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); +@@ -1193,6 +1199,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2020-09-03 11:48:30.802727077 -0400 ++++ src/net/netlink/af_netlink.c 2020-09-03 11:49:03.743830132 -0400 +@@ -2573,4 +2573,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.9/multiple.test b/test/integration/rhel-7.9/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.9/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.9/new-function.patch b/test/integration/rhel-7.9/new-function.patch new file mode 100644 index 0000000..e83487a --- /dev/null +++ b/test/integration/rhel-7.9/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2020-09-03 11:48:29.609723344 -0400 ++++ src/drivers/tty/n_tty.c 2020-09-03 11:49:05.751836414 -0400 +@@ -2175,7 +2175,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2264,6 +2264,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.9/new-globals.patch b/test/integration/rhel-7.9/new-globals.patch new file mode 100644 index 0000000..4840c97 --- /dev/null +++ b/test/integration/rhel-7.9/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-09-03 11:48:30.496726119 -0400 ++++ src/fs/proc/cmdline.c 2020-09-03 11:49:07.740842636 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:49:07.740842636 -0400 +@@ -17,6 +17,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -54,6 +56,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.9/parainstructions-section.patch b/test/integration/rhel-7.9/parainstructions-section.patch new file mode 100644 index 0000000..d7a02f9 --- /dev/null +++ b/test/integration/rhel-7.9/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2020-09-03 11:48:30.496726119 -0400 ++++ src/fs/proc/generic.c 2020-09-03 11:49:09.715848815 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.9/shadow-newpid-LOADED.test b/test/integration/rhel-7.9/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.9/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.9/shadow-newpid.patch b/test/integration/rhel-7.9/shadow-newpid.patch new file mode 100644 index 0000000..2c4ae45 --- /dev/null +++ b/test/integration/rhel-7.9/shadow-newpid.patch @@ -0,0 +1,69 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2020-09-03 11:48:30.496726119 -0400 ++++ src/fs/proc/array.c 2020-09-03 11:49:11.696855012 -0400 +@@ -395,13 +395,20 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2020-09-03 11:48:30.717726811 -0400 ++++ src/kernel/exit.c 2020-09-03 11:49:11.696855012 -0400 +@@ -791,6 +791,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +889,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-09-03 11:48:30.717726811 -0400 ++++ src/kernel/fork.c 2020-09-03 11:49:11.697855015 -0400 +@@ -1784,6 +1784,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1821,6 +1822,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.9/smp-locks-section.patch b/test/integration/rhel-7.9/smp-locks-section.patch new file mode 100644 index 0000000..a044e41 --- /dev/null +++ b/test/integration/rhel-7.9/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2020-09-03 11:48:29.616723366 -0400 ++++ src/drivers/tty/tty_buffer.c 2020-09-03 11:49:13.626861050 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.9/special-static.patch b/test/integration/rhel-7.9/special-static.patch new file mode 100644 index 0000000..c098331 --- /dev/null +++ b/test/integration/rhel-7.9/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-09-03 11:48:30.717726811 -0400 ++++ src/kernel/fork.c 2020-09-03 11:49:15.602867232 -0400 +@@ -1153,10 +1153,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.9/symvers-disagreement-FAIL.patch b/test/integration/rhel-7.9/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..300f8b9 --- /dev/null +++ b/test/integration/rhel-7.9/symvers-disagreement-FAIL.patch @@ -0,0 +1,49 @@ +From da109d66a890373d0aa831f97b49aaffcc4eeb45 Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes a function referencing a function whose CRC has changed, + causing the symbol and the new CRC to be included in the __version + section of the final module. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index b9a71137208..4af27e069c2 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 8b1c4a5ee78..1b7997b58c0 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -693,6 +693,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-7.9/syscall-LOADED.test b/test/integration/rhel-7.9/syscall-LOADED.test new file mode 100755 index 0000000..3a2fd88 --- /dev/null +++ b/test/integration/rhel-7.9/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/rhel-7.9/syscall.patch b/test/integration/rhel-7.9/syscall.patch new file mode 100644 index 0000000..729340d --- /dev/null +++ b/test/integration/rhel-7.9/syscall.patch @@ -0,0 +1,26 @@ +diff --git a/kernel/sys.c b/kernel/sys.c +index 1fbf388279832..b5186aa83adfa 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1392,14 +1392,18 @@ static int override_release(char __user *release, size_t len) + return ret; + } + +-SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) ++#include "kpatch-syscall.h" ++KPATCH_SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + { ++ struct new_utsname tmp; + int errno = 0; + + down_read(&uts_sem); +- if (copy_to_user(name, utsname(), sizeof *name)) +- errno = -EFAULT; ++ memcpy(&tmp, utsname(), sizeof(tmp)); + up_read(&uts_sem); ++ strcat(tmp.sysname, ".kpatch"); ++ if (copy_to_user(name, &tmp, sizeof(tmp))) ++ errno = -EFAULT; + + if (!errno && override_release(name->release, sizeof(name->release))) + errno = -EFAULT; diff --git a/test/integration/rhel-7.9/tracepoints-section.patch b/test/integration/rhel-7.9/tracepoints-section.patch new file mode 100644 index 0000000..33a15ed --- /dev/null +++ b/test/integration/rhel-7.9/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/timer.c src/kernel/timer.c +--- src.orig/kernel/timer.c 2020-09-03 11:48:30.726726839 -0400 ++++ src/kernel/timer.c 2020-09-03 11:49:17.588873445 -0400 +@@ -1454,6 +1454,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.9/warn-detect-FAIL.patch b/test/integration/rhel-7.9/warn-detect-FAIL.patch new file mode 100644 index 0000000..2f51ba7 --- /dev/null +++ b/test/integration/rhel-7.9/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2020-09-03 11:48:30.386725775 -0400 ++++ src/arch/x86/kvm/x86.c 2020-09-03 11:49:19.587879699 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.0/README b/test/integration/rhel-8.0/README new file mode 100644 index 0000000..d46fbcb --- /dev/null +++ b/test/integration/rhel-8.0/README @@ -0,0 +1 @@ +4.18.0-80.el8 diff --git a/test/integration/rhel-8.0/bug-table-section.patch b/test/integration/rhel-8.0/bug-table-section.patch new file mode 100644 index 0000000..842ae4a --- /dev/null +++ b/test/integration/rhel-8.0/bug-table-section.patch @@ -0,0 +1,13 @@ +diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c +index 89921a0..ac129b6 100644 +--- a/fs/proc/proc_sysctl.c ++++ b/fs/proc/proc_sysctl.c +@@ -333,6 +333,8 @@ static void start_unregistering(struct ctl_table_header *p) + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.0/cmdline-string-LOADED.test b/test/integration/rhel-8.0/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.0/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.0/cmdline-string.patch b/test/integration/rhel-8.0/cmdline-string.patch new file mode 100644 index 0000000..665c763 --- /dev/null +++ b/test/integration/rhel-8.0/cmdline-string.patch @@ -0,0 +1,14 @@ +diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c +index fa762c5..bd66027 100644 +--- a/fs/proc/cmdline.c ++++ b/fs/proc/cmdline.c +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.0/data-new-LOADED.test b/test/integration/rhel-8.0/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.0/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.0/data-new.patch b/test/integration/rhel-8.0/data-new.patch new file mode 100644 index 0000000..78d1b97 --- /dev/null +++ b/test/integration/rhel-8.0/data-new.patch @@ -0,0 +1,21 @@ +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..79ead86 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -30,6 +30,8 @@ static void show_val_kb(struct seq_file *m, const char *s, unsigned long num) + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -141,6 +143,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.0/data-read-mostly.patch.disabled b/test/integration/rhel-8.0/data-read-mostly.patch.disabled new file mode 100644 index 0000000..ed4eef0 --- /dev/null +++ b/test/integration/rhel-8.0/data-read-mostly.patch.disabled @@ -0,0 +1,14 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff --git a/net/core/dev.c b/net/core/dev.c +index b6f9647..b376a48 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -4858,6 +4858,7 @@ static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.0/fixup-section.patch b/test/integration/rhel-8.0/fixup-section.patch new file mode 100644 index 0000000..78ab6dd --- /dev/null +++ b/test/integration/rhel-8.0/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index d97f548..58863b6 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.0/gcc-constprop.patch.disabled b/test/integration/rhel-8.0/gcc-constprop.patch.disabled new file mode 100644 index 0000000..3fbc577 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-constprop.patch.disabled @@ -0,0 +1,14 @@ +diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c +index 4786df9..c73a687 100644 +--- a/kernel/time/timekeeping.c ++++ b/kernel/time/timekeeping.c +@@ -1211,6 +1211,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.0/gcc-isra.patch b/test/integration/rhel-8.0/gcc-isra.patch new file mode 100644 index 0000000..0903f24 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-isra.patch @@ -0,0 +1,12 @@ +diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c +index 89921a0..199b1d7 100644 +--- a/fs/proc/proc_sysctl.c ++++ b/fs/proc/proc_sysctl.c +@@ -48,6 +48,7 @@ void proc_sys_poll_notify(struct ctl_table_poll *poll) + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.0/gcc-mangled-3.patch b/test/integration/rhel-8.0/gcc-mangled-3.patch new file mode 100644 index 0000000..bcb5e62 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-mangled-3.patch @@ -0,0 +1,14 @@ +diff --git a/mm/slub.c b/mm/slub.c +index 51258ef..3cb5264 100644 +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -5852,6 +5852,9 @@ void get_slabinfo(struct kmem_cache *s, struct slabinfo *sinfo) + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.0/gcc-static-local-var-2.patch b/test/integration/rhel-8.0/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a884bfa --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-2.patch @@ -0,0 +1,14 @@ +diff --git a/mm/mmap.c b/mm/mmap.c +index bf46096..4bc085c 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -1684,6 +1684,9 @@ unsigned long mmap_region(struct file *file, unsigned long addr, + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.0/gcc-static-local-var-3.patch b/test/integration/rhel-8.0/gcc-static-local-var-3.patch new file mode 100644 index 0000000..da2acc0 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-3.patch @@ -0,0 +1,20 @@ +diff --git a/kernel/reboot.c b/kernel/reboot.c +index e4ced88..0c0b5a2 100644 +--- a/kernel/reboot.c ++++ b/kernel/reboot.c +@@ -393,8 +393,15 @@ void kernel_power_off(void) + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.0/gcc-static-local-var-4.patch.disabled b/test/integration/rhel-8.0/gcc-static-local-var-4.patch.disabled new file mode 100644 index 0000000..17e269d --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-4.patch.disabled @@ -0,0 +1,25 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff --git a/fs/aio.c b/fs/aio.c +index e1f8f01..4dfb05c 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -263,11 +263,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.0/gcc-static-local-var-4.test.disabled b/test/integration/rhel-8.0/gcc-static-local-var-4.test.disabled new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-4.test.disabled @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.0/gcc-static-local-var-5.patch b/test/integration/rhel-8.0/gcc-static-local-var-5.patch new file mode 100644 index 0000000..0e3e2d3 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-5.patch @@ -0,0 +1,46 @@ +diff --git a/kernel/audit.c b/kernel/audit.c +index e7478cb..ed2546a 100644 +--- a/kernel/audit.c ++++ b/kernel/audit.c +@@ -325,6 +325,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -335,6 +341,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -354,6 +361,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -400,6 +412,8 @@ static int audit_log_config_change(char *function_name, u32 new, u32 old, + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.0/gcc-static-local-var-6.patch b/test/integration/rhel-8.0/gcc-static-local-var-6.patch new file mode 100644 index 0000000..91c72b6 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index 531d695..a536c74 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -84,6 +84,8 @@ static int nf_ip6_reroute(struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -97,6 +99,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.0/macro-callbacks.patch b/test/integration/rhel-8.0/macro-callbacks.patch new file mode 100644 index 0000000..b7f0077 --- /dev/null +++ b/test/integration/rhel-8.0/macro-callbacks.patch @@ -0,0 +1,158 @@ +diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c +index 4c1e427..7e46aaf 100644 +--- a/drivers/input/joydev.c ++++ b/drivers/input/joydev.c +@@ -1068,3 +1068,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c +index 56ddba2..d188b12 100644 +--- a/drivers/input/misc/pcspkr.c ++++ b/drivers/input/misc/pcspkr.c +@@ -138,3 +138,46 @@ static void pcspkr_shutdown(struct platform_device *dev) + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff --git a/fs/aio.c b/fs/aio.c +index e1f8f01..5efe496 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -49,6 +49,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.0/macro-printk.patch.disabled b/test/integration/rhel-8.0/macro-printk.patch.disabled new file mode 100644 index 0000000..dfe0586 --- /dev/null +++ b/test/integration/rhel-8.0/macro-printk.patch.disabled @@ -0,0 +1,151 @@ +diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c +index 0113993..0ea4967 100644 +--- a/net/ipv4/fib_frontend.c ++++ b/net/ipv4/fib_frontend.c +@@ -767,6 +767,7 @@ static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -788,6 +789,7 @@ static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c +index 446204c..5b73a01 100644 +--- a/net/ipv4/fib_semantics.c ++++ b/net/ipv4/fib_semantics.c +@@ -1025,6 +1025,7 @@ static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc) + fi->fib_metrics->metrics); + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1058,6 +1059,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1078,6 +1080,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1093,6 +1096,8 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1109,8 +1114,10 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1172,6 +1179,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1193,6 +1201,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1231,6 +1240,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1240,6 +1250,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1251,6 +1262,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1274,6 +1286,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1284,6 +1297,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c +index 5bc0c89..d57b327 100644 +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -1121,6 +1121,7 @@ static bool fib_valid_key_len(u32 key, u8 plen, struct netlink_ext_ack *extack) + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1143,11 +1144,14 @@ int fib_table_insert(struct net *net, struct fib_table *tb, + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.0/meminfo-init-FAIL.patch b/test/integration/rhel-8.0/meminfo-init-FAIL.patch new file mode 100644 index 0000000..a3361ed --- /dev/null +++ b/test/integration/rhel-8.0/meminfo-init-FAIL.patch @@ -0,0 +1,12 @@ +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..b981fab 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -151,6 +151,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.0/meminfo-init2-FAIL.patch b/test/integration/rhel-8.0/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..28f8445 --- /dev/null +++ b/test/integration/rhel-8.0/meminfo-init2-FAIL.patch @@ -0,0 +1,20 @@ +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..eb61884 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -39,6 +39,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -151,6 +152,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.0/meminfo-string-LOADED.test b/test/integration/rhel-8.0/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.0/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.0/meminfo-string.patch b/test/integration/rhel-8.0/meminfo-string.patch new file mode 100644 index 0000000..e20210f --- /dev/null +++ b/test/integration/rhel-8.0/meminfo-string.patch @@ -0,0 +1,13 @@ +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..804bc35 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -120,7 +120,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + + #ifdef CONFIG_MEMORY_FAILURE + seq_printf(m, "HardwareCorrupted: %5lu kB\n", diff --git a/test/integration/rhel-8.0/module-call-external.patch b/test/integration/rhel-8.0/module-call-external.patch new file mode 100644 index 0000000..44e546f --- /dev/null +++ b/test/integration/rhel-8.0/module-call-external.patch @@ -0,0 +1,35 @@ +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index a1143f7..950403a 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1196,6 +1196,8 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1205,6 +1207,7 @@ static int e_show(struct seq_file *m, void *p) + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 56704d9..851d41d 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2779,4 +2779,9 @@ static int __init netlink_proto_init(void) + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.0/multiple.test b/test/integration/rhel-8.0/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.0/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.0/new-function.patch b/test/integration/rhel-8.0/new-function.patch new file mode 100644 index 0000000..f1f21a7 --- /dev/null +++ b/test/integration/rhel-8.0/new-function.patch @@ -0,0 +1,26 @@ +diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c +index 3ad4602..f3cda7e 100644 +--- a/drivers/tty/n_tty.c ++++ b/drivers/tty/n_tty.c +@@ -2296,7 +2296,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2383,6 +2383,12 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.0/new-globals.patch b/test/integration/rhel-8.0/new-globals.patch new file mode 100644 index 0000000..38892a4 --- /dev/null +++ b/test/integration/rhel-8.0/new-globals.patch @@ -0,0 +1,36 @@ +diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c +index fa762c5..cc67970 100644 +--- a/fs/proc/cmdline.c ++++ b/fs/proc/cmdline.c +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void) + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..7dd8350 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -20,6 +20,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + + available = si_mem_available(); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.0/parainstructions-section.patch b/test/integration/rhel-8.0/parainstructions-section.patch new file mode 100644 index 0000000..d3af7a6 --- /dev/null +++ b/test/integration/rhel-8.0/parainstructions-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/proc/generic.c b/fs/proc/generic.c +index bb1c162..2b3f7ec 100644 +--- a/fs/proc/generic.c ++++ b/fs/proc/generic.c +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.0/shadow-newpid-LOADED.test b/test/integration/rhel-8.0/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.0/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.0/shadow-newpid.patch.disabled b/test/integration/rhel-8.0/shadow-newpid.patch.disabled new file mode 100644 index 0000000..19109a3 --- /dev/null +++ b/test/integration/rhel-8.0/shadow-newpid.patch.disabled @@ -0,0 +1,80 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff --git a/fs/proc/array.c b/fs/proc/array.c +index 0ceb3b6..1b44c7f 100644 +--- a/fs/proc/array.c ++++ b/fs/proc/array.c +@@ -370,12 +370,19 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p) + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff --git a/kernel/exit.c b/kernel/exit.c +index deaa53a..01f5776 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -760,6 +760,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -865,6 +866,8 @@ void __noreturn do_exit(long code) + exit_task_work(tsk); + exit_thread(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff --git a/kernel/fork.c b/kernel/fork.c +index 3311231..2c12c1a 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -2093,6 +2093,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2105,6 +2106,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2131,6 +2134,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.0/smp-locks-section.patch b/test/integration/rhel-8.0/smp-locks-section.patch new file mode 100644 index 0000000..5166614 --- /dev/null +++ b/test/integration/rhel-8.0/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c +index ae3ce33..37727fe 100644 +--- a/drivers/tty/tty_buffer.c ++++ b/drivers/tty/tty_buffer.c +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.0/special-static.patch b/test/integration/rhel-8.0/special-static.patch new file mode 100644 index 0000000..22502df --- /dev/null +++ b/test/integration/rhel-8.0/special-static.patch @@ -0,0 +1,23 @@ +diff --git a/kernel/fork.c b/kernel/fork.c +index 3311231..f6e66b7 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1461,10 +1461,18 @@ static void posix_cpu_timers_init_group(struct signal_struct *sig) + static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { } + #endif + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.0/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.0/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..e3a672a --- /dev/null +++ b/test/integration/rhel-8.0/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From 7085a655b8d665b6314e8dab2f803bac0aea04ec Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index df3e1a44707a..15c9d6e2e1e0 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -29,6 +29,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 623be3174fb3..4ddd74ae0bb9 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -685,6 +685,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-8.0/tracepoints-section.patch b/test/integration/rhel-8.0/tracepoints-section.patch new file mode 100644 index 0000000..0dfea0c --- /dev/null +++ b/test/integration/rhel-8.0/tracepoints-section.patch @@ -0,0 +1,14 @@ +diff --git a/kernel/time/timer.c b/kernel/time/timer.c +index 786f8c0..1105697 100644 +--- a/kernel/time/timer.c ++++ b/kernel/time/timer.c +@@ -1692,6 +1692,9 @@ static __latent_entropy void run_timer_softirq(struct softirq_action *h) + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.0/warn-detect-FAIL.patch b/test/integration/rhel-8.0/warn-detect-FAIL.patch new file mode 100644 index 0000000..f57bd0e --- /dev/null +++ b/test/integration/rhel-8.0/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 4fa858b..ef1a710 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.1/bug-table-section.patch b/test/integration/rhel-8.1/bug-table-section.patch new file mode 100644 index 0000000..b37f545 --- /dev/null +++ b/test/integration/rhel-8.1/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src/fs/proc/proc_sysctl.c 2020-03-11 11:23:26.886602663 +0000 ++++ src/fs/proc/proc_sysctl.c 2020-03-11 11:23:39.822895153 +0000 +@@ -333,6 +333,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.1/cmdline-string-LOADED.test b/test/integration/rhel-8.1/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.1/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.1/cmdline-string.patch b/test/integration/rhel-8.1/cmdline-string.patch new file mode 100644 index 0000000..2ba1762 --- /dev/null +++ b/test/integration/rhel-8.1/cmdline-string.patch @@ -0,0 +1,13 @@ +diff -Nupr src/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src/fs/proc/cmdline.c 2020-03-11 11:23:26.878602482 +0000 ++++ src/fs/proc/cmdline.c 2020-03-11 11:24:37.984218859 +0000 +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.1/data-new-LOADED.test b/test/integration/rhel-8.1/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.1/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.1/data-new.patch b/test/integration/rhel-8.1/data-new.patch new file mode 100644 index 0000000..a33a233 --- /dev/null +++ b/test/integration/rhel-8.1/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:25:35.401538436 +0000 +@@ -30,6 +30,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -141,6 +143,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.1/data-read-mostly.patch.disabled b/test/integration/rhel-8.1/data-read-mostly.patch.disabled new file mode 100644 index 0000000..3569b7d --- /dev/null +++ b/test/integration/rhel-8.1/data-read-mostly.patch.disabled @@ -0,0 +1,13 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src/net/core/dev.c src/net/core/dev.c +--- src/net/core/dev.c 2020-03-11 11:23:32.550730639 +0000 ++++ src/net/core/dev.c 2020-03-11 11:43:53.348022834 +0000 +@@ -4893,6 +4893,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.1/fixup-section.patch b/test/integration/rhel-8.1/fixup-section.patch new file mode 100644 index 0000000..7446a29 --- /dev/null +++ b/test/integration/rhel-8.1/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src/fs/readdir.c src/fs/readdir.c +--- src/fs/readdir.c 2020-03-11 11:23:24.210542249 +0000 ++++ src/fs/readdir.c 2020-03-11 11:26:32.322857837 +0000 +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.1/gcc-constprop.patch.disabled b/test/integration/rhel-8.1/gcc-constprop.patch.disabled new file mode 100644 index 0000000..1c8ca71 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-constprop.patch.disabled @@ -0,0 +1,13 @@ +diff -Nupr src/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src/kernel/time/timekeeping.c 2020-03-11 11:23:30.538685163 +0000 ++++ src/kernel/time/timekeeping.c 2020-03-11 11:44:47.885367244 +0000 +@@ -1221,6 +1221,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.1/gcc-isra.patch b/test/integration/rhel-8.1/gcc-isra.patch new file mode 100644 index 0000000..3ae952f --- /dev/null +++ b/test/integration/rhel-8.1/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src/fs/proc/proc_sysctl.c 2020-03-11 11:23:26.886602663 +0000 ++++ src/fs/proc/proc_sysctl.c 2020-03-11 11:27:30.392214139 +0000 +@@ -48,6 +48,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.1/gcc-mangled-3.patch b/test/integration/rhel-8.1/gcc-mangled-3.patch new file mode 100644 index 0000000..65e757a --- /dev/null +++ b/test/integration/rhel-8.1/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src/mm/slub.c src/mm/slub.c +--- src/mm/slub.c 2020-03-11 11:23:32.406727384 +0000 ++++ src/mm/slub.c 2020-03-11 11:28:27.973568215 +0000 +@@ -5836,6 +5836,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.1/gcc-static-local-var-2.patch b/test/integration/rhel-8.1/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a17a1be --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src/mm/mmap.c src/mm/mmap.c +--- src/mm/mmap.c 2020-03-11 11:23:32.386726932 +0000 ++++ src/mm/mmap.c 2020-03-11 11:29:26.610955502 +0000 +@@ -1685,6 +1685,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.1/gcc-static-local-var-3.patch b/test/integration/rhel-8.1/gcc-static-local-var-3.patch new file mode 100644 index 0000000..81a0b32 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src/kernel/reboot.c src/kernel/reboot.c +--- src/kernel/reboot.c 2020-03-11 11:23:30.502684349 +0000 ++++ src/kernel/reboot.c 2020-03-11 11:30:24.412330397 +0000 +@@ -393,8 +393,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.1/gcc-static-local-var-4.patch.disabled b/test/integration/rhel-8.1/gcc-static-local-var-4.patch.disabled new file mode 100644 index 0000000..63481a7 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-4.patch.disabled @@ -0,0 +1,24 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src/fs/aio.c src/fs/aio.c +--- src/fs/aio.c 2020-03-11 11:23:24.150540895 +0000 ++++ src/fs/aio.c 2020-03-11 11:45:44.710769201 +0000 +@@ -251,11 +251,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.1/gcc-static-local-var-4.test.disabled b/test/integration/rhel-8.1/gcc-static-local-var-4.test.disabled new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-4.test.disabled @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.1/gcc-static-local-var-5.patch b/test/integration/rhel-8.1/gcc-static-local-var-5.patch new file mode 100644 index 0000000..3f639ac --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src/kernel/audit.c src/kernel/audit.c +--- src/kernel/audit.c 2020-03-11 11:23:30.394681909 +0000 ++++ src/kernel/audit.c 2020-03-11 11:46:41.484170922 +0000 +@@ -325,6 +325,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -335,6 +341,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -354,6 +361,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -400,6 +412,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.1/gcc-static-local-var-6.patch b/test/integration/rhel-8.1/gcc-static-local-var-6.patch new file mode 100644 index 0000000..b970dd8 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src/net/ipv6/netfilter.c 2020-03-11 11:23:32.702734076 +0000 ++++ src/net/ipv6/netfilter.c 2020-03-11 11:31:22.397716246 +0000 +@@ -87,6 +87,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -100,6 +102,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.1/macro-callbacks.patch b/test/integration/rhel-8.1/macro-callbacks.patch new file mode 100644 index 0000000..fd29ecf --- /dev/null +++ b/test/integration/rhel-8.1/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src/drivers/input/joydev.c src/drivers/input/joydev.c +--- src/drivers/input/joydev.c 2020-03-11 11:23:07.890174500 +0000 ++++ src/drivers/input/joydev.c 2020-03-11 11:32:20.859119425 +0000 +@@ -1068,3 +1068,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src/drivers/input/misc/pcspkr.c 2020-03-11 11:23:07.962176120 +0000 ++++ src/drivers/input/misc/pcspkr.c 2020-03-11 11:32:20.859119425 +0000 +@@ -138,3 +138,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src/fs/aio.c src/fs/aio.c +--- src/fs/aio.c 2020-03-11 11:23:24.150540895 +0000 ++++ src/fs/aio.c 2020-03-11 11:32:20.859119425 +0000 +@@ -49,6 +49,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.1/macro-printk.patch.disabled b/test/integration/rhel-8.1/macro-printk.patch.disabled new file mode 100644 index 0000000..02926af --- /dev/null +++ b/test/integration/rhel-8.1/macro-printk.patch.disabled @@ -0,0 +1,148 @@ +diff -Nupr src/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src/net/ipv4/fib_frontend.c 2020-03-11 11:23:32.622732267 +0000 ++++ src/net/ipv4/fib_frontend.c 2020-03-11 11:47:37.897564680 +0000 +@@ -777,6 +777,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -798,6 +799,7 @@ static int inet_rtm_newroute(struct sk_b + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src/net/ipv4/fib_semantics.c 2020-03-11 11:23:32.626732358 +0000 ++++ src/net/ipv4/fib_semantics.c 2020-03-11 11:47:37.897564680 +0000 +@@ -1025,6 +1025,7 @@ fib_convert_metrics(struct fib_info *fi, + fi->fib_metrics->metrics); + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1058,6 +1059,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1078,6 +1080,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1093,6 +1096,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1109,8 +1114,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1172,6 +1179,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1193,6 +1201,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1231,6 +1240,7 @@ struct fib_info *fib_create_info(struct + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1240,6 +1250,7 @@ struct fib_info *fib_create_info(struct + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1251,6 +1262,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1274,6 +1286,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1284,6 +1297,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src/net/ipv4/fib_trie.c 2020-03-11 11:23:32.626732358 +0000 ++++ src/net/ipv4/fib_trie.c 2020-03-11 11:47:37.897564680 +0000 +@@ -1121,6 +1121,7 @@ static bool fib_valid_key_len(u32 key, u + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1143,11 +1144,14 @@ int fib_table_insert(struct net *net, st + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.1/meminfo-init-FAIL.patch b/test/integration/rhel-8.1/meminfo-init-FAIL.patch new file mode 100644 index 0000000..2334a0e --- /dev/null +++ b/test/integration/rhel-8.1/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:34:17.189926874 +0000 +@@ -151,6 +151,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.1/meminfo-init2-FAIL.patch b/test/integration/rhel-8.1/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..937667b --- /dev/null +++ b/test/integration/rhel-8.1/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:33:19.732537882 +0000 +@@ -40,6 +40,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long sreclaimable, sunreclaim; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -151,6 +152,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.1/meminfo-string-LOADED.test b/test/integration/rhel-8.1/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.1/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.1/meminfo-string.patch b/test/integration/rhel-8.1/meminfo-string.patch new file mode 100644 index 0000000..e3e7509 --- /dev/null +++ b/test/integration/rhel-8.1/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:35:15.879349884 +0000 +@@ -120,7 +120,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + + #ifdef CONFIG_MEMORY_FAILURE + seq_printf(m, "HardwareCorrupted: %5lu kB\n", diff --git a/test/integration/rhel-8.1/module-LOADED.test b/test/integration/rhel-8.1/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/rhel-8.1/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.1/module.patch b/test/integration/rhel-8.1/module.patch new file mode 100644 index 0000000..8bfb621 --- /dev/null +++ b/test/integration/rhel-8.1/module.patch @@ -0,0 +1,90 @@ +From 08078d00ab1749a6f84148a00d8d26572af4ec97 Mon Sep 17 00:00:00 2001 +Message-Id: <08078d00ab1749a6f84148a00d8d26572af4ec97.1586900628.git.jpoimboe@redhat.com> +From: Josh Poimboeuf +Date: Tue, 14 Apr 2020 15:17:51 -0500 +Subject: [PATCH] kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf +--- + fs/nfsd/export.c | 30 ++++++++++++++++++++++++++++++ + net/netlink/af_netlink.c | 5 +++++ + 2 files changed, 35 insertions(+) + +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index a1143f7c2201..253c15ad82b2 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1196,15 +1196,45 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++#include ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); + struct cache_detail *cd = m->private; ++#ifdef CONFIG_X86_64 ++ unsigned long long sched_clock; ++ ++ alternative("ud2", "call yield", X86_FEATURE_ALWAYS); ++ alternative("call yield", "ud2", X86_FEATURE_IA64); ++ ++ sched_clock = paravirt_sched_clock(); ++ if (!jiffies) ++ printk("kpatch: sched_clock: %llu\n", sched_clock); ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&mcsafe_key)) ++ printk("kpatch: mcsafe_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif + + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 44fa4ea5df76..dc36ec2901b8 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2809,4 +2809,9 @@ static int __init netlink_proto_init(void) + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); +-- +2.21.1 + diff --git a/test/integration/rhel-8.1/multiple.test b/test/integration/rhel-8.1/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.1/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.1/new-function.patch b/test/integration/rhel-8.1/new-function.patch new file mode 100644 index 0000000..3e446df --- /dev/null +++ b/test/integration/rhel-8.1/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src/drivers/tty/n_tty.c 2020-03-11 11:23:23.062516341 +0000 ++++ src/drivers/tty/n_tty.c 2020-03-11 11:37:13.226206161 +0000 +@@ -2296,7 +2296,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2383,6 +2383,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.1/new-globals.patch b/test/integration/rhel-8.1/new-globals.patch new file mode 100644 index 0000000..07aafec --- /dev/null +++ b/test/integration/rhel-8.1/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src/fs/proc/cmdline.c 2020-03-11 11:23:26.878602482 +0000 ++++ src/fs/proc/cmdline.c 2020-03-11 11:38:10.295599825 +0000 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:38:10.295599825 +0000 +@@ -20,6 +20,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -56,6 +58,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE); + sunreclaim = global_node_page_state(NR_SLAB_UNRECLAIMABLE); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.1/parainstructions-section.patch b/test/integration/rhel-8.1/parainstructions-section.patch new file mode 100644 index 0000000..1183f45 --- /dev/null +++ b/test/integration/rhel-8.1/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src/fs/proc/generic.c src/fs/proc/generic.c +--- src/fs/proc/generic.c 2020-03-11 11:23:26.878602482 +0000 ++++ src/fs/proc/generic.c 2020-03-11 11:39:07.677003695 +0000 +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.1/shadow-newpid-LOADED.test.disabled b/test/integration/rhel-8.1/shadow-newpid-LOADED.test.disabled new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.1/shadow-newpid-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.1/shadow-newpid.patch.disabled b/test/integration/rhel-8.1/shadow-newpid.patch.disabled new file mode 100644 index 0000000..f571dc4 --- /dev/null +++ b/test/integration/rhel-8.1/shadow-newpid.patch.disabled @@ -0,0 +1,77 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src/fs/proc/array.c src/fs/proc/array.c +--- src/fs/proc/array.c 2020-03-11 11:23:26.874602392 +0000 ++++ src/fs/proc/array.c 2020-03-11 11:48:34.514964309 +0000 +@@ -370,12 +370,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src/kernel/exit.c src/kernel/exit.c +--- src/kernel/exit.c 2020-03-11 11:23:30.434682812 +0000 ++++ src/kernel/exit.c 2020-03-11 11:48:34.514964309 +0000 +@@ -762,6 +762,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -868,6 +869,8 @@ void __noreturn do_exit(long code) + exit_thread(tsk); + exit_umh(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src/kernel/fork.c src/kernel/fork.c +--- src/kernel/fork.c 2020-03-11 11:23:30.438682903 +0000 ++++ src/kernel/fork.c 2020-03-11 11:48:34.514964309 +0000 +@@ -2210,6 +2210,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2222,6 +2223,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2248,6 +2251,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.1/smp-locks-section.patch b/test/integration/rhel-8.1/smp-locks-section.patch new file mode 100644 index 0000000..f0794a1 --- /dev/null +++ b/test/integration/rhel-8.1/smp-locks-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src/drivers/tty/tty_buffer.c 2020-03-11 11:23:23.138518056 +0000 ++++ src/drivers/tty/tty_buffer.c 2020-03-11 11:40:04.174388205 +0000 +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(str + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.1/special-static.patch.disabled b/test/integration/rhel-8.1/special-static.patch.disabled new file mode 100644 index 0000000..992f8b9 --- /dev/null +++ b/test/integration/rhel-8.1/special-static.patch.disabled @@ -0,0 +1,22 @@ +diff -Nupr src/kernel/fork.c src/kernel/fork.c +--- src/kernel/fork.c 2020-03-11 11:23:30.438682903 +0000 ++++ src/kernel/fork.c 2020-03-11 11:41:01.567796732 +0000 +@@ -1524,10 +1524,18 @@ static void posix_cpu_timers_init_group( + static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { } + #endif + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.1/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.1/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..9527641 --- /dev/null +++ b/test/integration/rhel-8.1/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From c63702554e54b992793fe3598ea8c8c415bef908 Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index 2ab316d85651..2ef19920f6ab 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -29,6 +29,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 0a2b261a27c9..51a1868c9cea 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -685,6 +685,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-8.1/tracepoints-section.patch b/test/integration/rhel-8.1/tracepoints-section.patch new file mode 100644 index 0000000..ab5e316 --- /dev/null +++ b/test/integration/rhel-8.1/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src/kernel/time/timer.c src/kernel/time/timer.c +--- src/kernel/time/timer.c 2020-03-11 11:23:30.542685253 +0000 ++++ src/kernel/time/timer.c 2020-03-11 11:41:58.129186658 +0000 +@@ -1692,6 +1692,9 @@ static __latent_entropy void run_timer_s + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.1/warn-detect-FAIL.patch b/test/integration/rhel-8.1/warn-detect-FAIL.patch new file mode 100644 index 0000000..a00fe05 --- /dev/null +++ b/test/integration/rhel-8.1/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src/arch/x86/kvm/x86.c 2020-03-11 11:22:57.389938541 +0000 ++++ src/arch/x86/kvm/x86.c 2020-03-11 11:42:55.798605475 +0000 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.2/bug-table-section.patch b/test/integration/rhel-8.2/bug-table-section.patch new file mode 100644 index 0000000..5bc1e11 --- /dev/null +++ b/test/integration/rhel-8.2/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-05-12 11:14:36.220489794 -0400 +@@ -338,6 +338,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.2/cmdline-string-LOADED.test b/test/integration/rhel-8.2/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.2/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.2/cmdline-string.patch b/test/integration/rhel-8.2/cmdline-string.patch new file mode 100644 index 0000000..4838935 --- /dev/null +++ b/test/integration/rhel-8.2/cmdline-string.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/cmdline.c 2020-05-12 11:14:40.110321212 -0400 +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.2/data-new-LOADED.test b/test/integration/rhel-8.2/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.2/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.2/data-new.patch b/test/integration/rhel-8.2/data-new.patch new file mode 100644 index 0000000..6978e52 --- /dev/null +++ b/test/integration/rhel-8.2/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:14:43.210186867 -0400 +@@ -31,6 +31,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -143,6 +145,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.2/data-read-mostly.patch.disabled b/test/integration/rhel-8.2/data-read-mostly.patch.disabled new file mode 100644 index 0000000..5b1c87a --- /dev/null +++ b/test/integration/rhel-8.2/data-read-mostly.patch.disabled @@ -0,0 +1,13 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2020-05-12 11:14:29.800768017 -0400 ++++ src/net/core/dev.c 2020-05-12 11:15:38.827776462 -0400 +@@ -4893,6 +4893,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.2/fixup-section.patch b/test/integration/rhel-8.2/fixup-section.patch new file mode 100644 index 0000000..6632001 --- /dev/null +++ b/test/integration/rhel-8.2/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2020-05-12 11:14:29.170795319 -0400 ++++ src/fs/readdir.c 2020-05-12 11:14:46.280053823 -0400 +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.2/gcc-constprop.patch b/test/integration/rhel-8.2/gcc-constprop.patch new file mode 100644 index 0000000..1a17df4 --- /dev/null +++ b/test/integration/rhel-8.2/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/time/timekeeping.c 2020-05-12 11:15:41.897643417 -0400 +@@ -1221,6 +1221,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.2/gcc-isra.patch b/test/integration/rhel-8.2/gcc-isra.patch new file mode 100644 index 0000000..073b60f --- /dev/null +++ b/test/integration/rhel-8.2/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-05-12 11:14:49.359920345 -0400 +@@ -53,6 +53,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.2/gcc-mangled-3.patch b/test/integration/rhel-8.2/gcc-mangled-3.patch new file mode 100644 index 0000000..8d096e4 --- /dev/null +++ b/test/integration/rhel-8.2/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2020-05-12 11:14:32.110667908 -0400 ++++ src/mm/slub.c 2020-05-12 11:14:52.439786867 -0400 +@@ -5852,6 +5852,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.2/gcc-static-local-var-2.patch b/test/integration/rhel-8.2/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a735a1f --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2020-05-12 11:14:32.110667908 -0400 ++++ src/mm/mmap.c 2020-05-12 11:14:55.529652955 -0400 +@@ -1679,6 +1679,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.2/gcc-static-local-var-3.patch b/test/integration/rhel-8.2/gcc-static-local-var-3.patch new file mode 100644 index 0000000..2b7af0f --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/reboot.c src/kernel/reboot.c +--- src.orig/kernel/reboot.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/reboot.c 2020-05-12 11:14:58.579520777 -0400 +@@ -393,8 +393,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.2/gcc-static-local-var-4.patch.disabled b/test/integration/rhel-8.2/gcc-static-local-var-4.patch.disabled new file mode 100644 index 0000000..783f598 --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-4.patch.disabled @@ -0,0 +1,24 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-05-12 11:14:29.130797053 -0400 ++++ src/fs/aio.c 2020-05-12 11:15:44.967510372 -0400 +@@ -251,11 +251,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.2/gcc-static-local-var-4.test.disabled b/test/integration/rhel-8.2/gcc-static-local-var-4.test.disabled new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-4.test.disabled @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.2/gcc-static-local-var-5.patch b/test/integration/rhel-8.2/gcc-static-local-var-5.patch new file mode 100644 index 0000000..b6f492b --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2020-05-12 11:24:26.314915742 -0400 ++++ src/kernel/audit.c 2020-05-12 11:24:37.024451603 -0400 +@@ -321,6 +321,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -331,6 +337,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -350,6 +357,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -396,6 +408,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.2/gcc-static-local-var-6.patch b/test/integration/rhel-8.2/gcc-static-local-var-6.patch new file mode 100644 index 0000000..378433e --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2020-05-12 11:14:29.820767150 -0400 ++++ src/net/ipv6/netfilter.c 2020-05-12 11:15:01.659387299 -0400 +@@ -87,6 +87,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -100,6 +102,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.2/macro-callbacks.patch b/test/integration/rhel-8.2/macro-callbacks.patch new file mode 100644 index 0000000..e2c7df9 --- /dev/null +++ b/test/integration/rhel-8.2/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2020-05-12 11:14:32.580647540 -0400 ++++ src/drivers/input/joydev.c 2020-05-12 11:15:04.909246454 -0400 +@@ -1084,3 +1084,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2020-05-12 11:14:32.590647107 -0400 ++++ src/drivers/input/misc/pcspkr.c 2020-05-12 11:15:04.909246454 -0400 +@@ -133,3 +133,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-05-12 11:14:29.130797053 -0400 ++++ src/fs/aio.c 2020-05-12 11:15:04.909246454 -0400 +@@ -49,6 +49,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.2/macro-printk.patch b/test/integration/rhel-8.2/macro-printk.patch new file mode 100644 index 0000000..f2d2d4b --- /dev/null +++ b/test/integration/rhel-8.2/macro-printk.patch @@ -0,0 +1,148 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2020-05-12 11:14:29.710771918 -0400 ++++ src/net/ipv4/fib_frontend.c 2020-05-12 11:15:48.047376894 -0400 +@@ -789,6 +789,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -810,6 +811,7 @@ static int inet_rtm_newroute(struct sk_b + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2020-05-12 11:14:29.710771918 -0400 ++++ src/net/ipv4/fib_semantics.c 2020-05-12 11:15:48.047376894 -0400 +@@ -1025,6 +1025,7 @@ fib_convert_metrics(struct fib_info *fi, + fi->fib_metrics->metrics); + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1058,6 +1059,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1078,6 +1080,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1093,6 +1096,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1109,8 +1114,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1172,6 +1179,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1193,6 +1201,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1231,6 +1240,7 @@ struct fib_info *fib_create_info(struct + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1240,6 +1250,7 @@ struct fib_info *fib_create_info(struct + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1251,6 +1262,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1274,6 +1286,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1284,6 +1297,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2020-05-12 11:14:29.710771918 -0400 ++++ src/net/ipv4/fib_trie.c 2020-05-12 11:15:48.047376894 -0400 +@@ -1121,6 +1121,7 @@ static bool fib_valid_key_len(u32 key, u + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1143,11 +1144,14 @@ int fib_table_insert(struct net *net, st + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.2/meminfo-init-FAIL.patch b/test/integration/rhel-8.2/meminfo-init-FAIL.patch new file mode 100644 index 0000000..6f58adb --- /dev/null +++ b/test/integration/rhel-8.2/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:15:13.848859021 -0400 +@@ -153,6 +153,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.2/meminfo-init2-FAIL.patch b/test/integration/rhel-8.2/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..28ba5bc --- /dev/null +++ b/test/integration/rhel-8.2/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:15:10.778992066 -0400 +@@ -41,6 +41,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long sreclaimable, sunreclaim; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -153,6 +154,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.2/meminfo-string-LOADED.test b/test/integration/rhel-8.2/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.2/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.2/meminfo-string.patch b/test/integration/rhel-8.2/meminfo-string.patch new file mode 100644 index 0000000..278e0e5 --- /dev/null +++ b/test/integration/rhel-8.2/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:24:25.954931343 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:24:33.774592448 -0400 +@@ -121,7 +121,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + show_val_kb(m, "Percpu: ", pcpu_nr_pages()); + + #ifdef CONFIG_MEMORY_FAILURE diff --git a/test/integration/rhel-8.2/module-LOADED.test b/test/integration/rhel-8.2/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/rhel-8.2/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.2/module.patch b/test/integration/rhel-8.2/module.patch new file mode 100644 index 0000000..5dcee19 --- /dev/null +++ b/test/integration/rhel-8.2/module.patch @@ -0,0 +1,85 @@ +From 08078d00ab1749a6f84148a00d8d26572af4ec97 Mon Sep 17 00:00:00 2001 +Message-Id: <08078d00ab1749a6f84148a00d8d26572af4ec97.1586900628.git.jpoimboe@redhat.com> +From: Josh Poimboeuf +Date: Tue, 14 Apr 2020 15:17:51 -0500 +Subject: [PATCH] kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf +--- + fs/nfsd/export.c | 30 ++++++++++++++++++++++++++++++ + net/netlink/af_netlink.c | 5 +++++ + 2 files changed, 35 insertions(+) + +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2020-05-12 11:14:29.230792719 -0400 ++++ src/fs/nfsd/export.c 2020-05-12 11:15:17.078719042 -0400 +@@ -1196,15 +1196,45 @@ static void exp_flags(struct seq_file *m + } + } + ++#include ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); + struct cache_detail *cd = m->private; ++#ifdef CONFIG_X86_64 ++ unsigned long long sched_clock; ++ ++ alternative("ud2", "call yield", X86_FEATURE_ALWAYS); ++ alternative("call yield", "ud2", X86_FEATURE_IA64); ++ ++ sched_clock = paravirt_sched_clock(); ++ if (!jiffies) ++ printk("kpatch: sched_clock: %llu\n", sched_clock); ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&mcsafe_key)) ++ printk("kpatch: mcsafe_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif + + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2020-05-12 11:14:29.780768884 -0400 ++++ src/net/netlink/af_netlink.c 2020-05-12 11:15:17.078719042 -0400 +@@ -2788,4 +2788,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.2/multiple.test b/test/integration/rhel-8.2/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.2/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.2/new-function.patch b/test/integration/rhel-8.2/new-function.patch new file mode 100644 index 0000000..079065d --- /dev/null +++ b/test/integration/rhel-8.2/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2020-05-12 11:14:32.770639306 -0400 ++++ src/drivers/tty/n_tty.c 2020-05-12 11:15:20.398575163 -0400 +@@ -2296,7 +2296,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2383,6 +2383,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.2/new-globals.patch b/test/integration/rhel-8.2/new-globals.patch new file mode 100644 index 0000000..1f75685 --- /dev/null +++ b/test/integration/rhel-8.2/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/cmdline.c 2020-05-12 11:15:23.488441252 -0400 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:15:23.488441252 -0400 +@@ -21,6 +21,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -57,6 +59,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE); + sunreclaim = global_node_page_state(NR_SLAB_UNRECLAIMABLE); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.2/parainstructions-section.patch b/test/integration/rhel-8.2/parainstructions-section.patch new file mode 100644 index 0000000..185bf97 --- /dev/null +++ b/test/integration/rhel-8.2/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/generic.c 2020-05-12 11:15:26.558308207 -0400 +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.2/shadow-newpid-LOADED.test.disabled b/test/integration/rhel-8.2/shadow-newpid-LOADED.test.disabled new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.2/shadow-newpid-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.2/shadow-newpid.patch.disabled b/test/integration/rhel-8.2/shadow-newpid.patch.disabled new file mode 100644 index 0000000..18ae4c5 --- /dev/null +++ b/test/integration/rhel-8.2/shadow-newpid.patch.disabled @@ -0,0 +1,77 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/array.c 2020-05-12 11:15:51.127243416 -0400 +@@ -370,12 +370,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/exit.c 2020-05-12 11:15:51.127243416 -0400 +@@ -762,6 +762,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -868,6 +869,8 @@ void __noreturn do_exit(long code) + exit_thread(tsk); + exit_umh(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/fork.c 2020-05-12 11:15:51.127243416 -0400 +@@ -2206,6 +2206,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2218,6 +2219,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2244,6 +2247,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.2/smp-locks-section.patch b/test/integration/rhel-8.2/smp-locks-section.patch new file mode 100644 index 0000000..562be3e --- /dev/null +++ b/test/integration/rhel-8.2/smp-locks-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2020-05-12 11:14:32.780638873 -0400 ++++ src/drivers/tty/tty_buffer.c 2020-05-12 11:15:29.618175596 -0400 +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(str + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.2/special-static.patch.disabled b/test/integration/rhel-8.2/special-static.patch.disabled new file mode 100644 index 0000000..a58cbbc --- /dev/null +++ b/test/integration/rhel-8.2/special-static.patch.disabled @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/fork.c 2020-05-12 11:15:54.197110372 -0400 +@@ -1523,10 +1523,18 @@ static void posix_cpu_timers_init_group( + static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { } + #endif + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.2/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.2/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..798d21f --- /dev/null +++ b/test/integration/rhel-8.2/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From 2d6b7bce089e52563bd9c67df62f48e90b48047d Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index 26bae20f0553..506ebbf0a210 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -30,6 +30,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index f74e6bda1788..86f7d453549c 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -685,6 +685,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-8.2/tracepoints-section.patch b/test/integration/rhel-8.2/tracepoints-section.patch new file mode 100644 index 0000000..a15d065 --- /dev/null +++ b/test/integration/rhel-8.2/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timer.c src/kernel/time/timer.c +--- src.orig/kernel/time/timer.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/time/timer.c 2020-05-12 11:15:32.688042552 -0400 +@@ -1696,6 +1696,9 @@ static __latent_entropy void run_timer_s + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.2/warn-detect-FAIL.patch b/test/integration/rhel-8.2/warn-detect-FAIL.patch new file mode 100644 index 0000000..5686019 --- /dev/null +++ b/test/integration/rhel-8.2/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2020-05-12 11:14:30.610732914 -0400 ++++ src/arch/x86/kvm/x86.c 2020-05-12 11:15:35.767909073 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.3/bug-table-section.patch b/test/integration/rhel-8.3/bug-table-section.patch new file mode 100644 index 0000000..cc2cca0 --- /dev/null +++ b/test/integration/rhel-8.3/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-03-17 01:12:55.155895808 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-03-17 01:12:59.269209033 -0400 +@@ -338,6 +338,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.3/cmdline-string-LOADED.test b/test/integration/rhel-8.3/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.3/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.3/cmdline-string.patch b/test/integration/rhel-8.3/cmdline-string.patch new file mode 100644 index 0000000..06117f7 --- /dev/null +++ b/test/integration/rhel-8.3/cmdline-string.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-03-17 01:12:55.154895731 -0400 ++++ src/fs/proc/cmdline.c 2020-03-17 01:13:02.339442827 -0400 +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.3/data-new-LOADED.test b/test/integration/rhel-8.3/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.3/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.3/data-new.patch b/test/integration/rhel-8.3/data-new.patch new file mode 100644 index 0000000..773c6c0 --- /dev/null +++ b/test/integration/rhel-8.3/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-17 01:12:55.155895808 -0400 ++++ src/fs/proc/meminfo.c 2020-03-17 01:13:05.297668094 -0400 +@@ -31,6 +31,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -143,6 +145,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.3/data-read-mostly.patch.disabled b/test/integration/rhel-8.3/data-read-mostly.patch.disabled new file mode 100644 index 0000000..9ad7022 --- /dev/null +++ b/test/integration/rhel-8.3/data-read-mostly.patch.disabled @@ -0,0 +1,13 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2020-03-17 01:12:55.619931143 -0400 ++++ src/net/core/dev.c 2020-03-17 01:13:57.999681305 -0400 +@@ -4893,6 +4893,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.3/fixup-section.patch b/test/integration/rhel-8.3/fixup-section.patch new file mode 100644 index 0000000..845d27d --- /dev/null +++ b/test/integration/rhel-8.3/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2020-03-17 01:12:55.087890628 -0400 ++++ src/fs/readdir.c 2020-03-17 01:13:08.214890238 -0400 +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.3/gcc-constprop.patch b/test/integration/rhel-8.3/gcc-constprop.patch new file mode 100644 index 0000000..1524b3f --- /dev/null +++ b/test/integration/rhel-8.3/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2020-03-17 01:12:55.498921929 -0400 ++++ src/kernel/time/timekeeping.c 2020-03-17 01:14:00.935904896 -0400 +@@ -1221,6 +1221,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.3/gcc-isra.patch b/test/integration/rhel-8.3/gcc-isra.patch new file mode 100644 index 0000000..cabe41b --- /dev/null +++ b/test/integration/rhel-8.3/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-03-17 01:12:55.155895808 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-03-17 01:13:11.117111239 -0400 +@@ -53,6 +53,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.3/gcc-mangled-3.patch b/test/integration/rhel-8.3/gcc-mangled-3.patch new file mode 100644 index 0000000..4b53f1c --- /dev/null +++ b/test/integration/rhel-8.3/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2020-03-17 01:12:57.341062206 -0400 ++++ src/mm/slub.c 2020-03-17 01:13:14.023332546 -0400 +@@ -5852,6 +5852,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.3/gcc-static-local-var-2.patch b/test/integration/rhel-8.3/gcc-static-local-var-2.patch new file mode 100644 index 0000000..98a883e --- /dev/null +++ b/test/integration/rhel-8.3/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2020-03-17 01:12:57.337061902 -0400 ++++ src/mm/mmap.c 2020-03-17 01:13:16.949555374 -0400 +@@ -1679,6 +1679,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.3/gcc-static-local-var-3.patch b/test/integration/rhel-8.3/gcc-static-local-var-3.patch new file mode 100644 index 0000000..aa93ab8 --- /dev/null +++ b/test/integration/rhel-8.3/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/reboot.c src/kernel/reboot.c +--- src.orig/kernel/reboot.c 2020-03-17 01:12:55.495921700 -0400 ++++ src/kernel/reboot.c 2020-03-17 01:13:19.875778203 -0400 +@@ -393,8 +393,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.3/gcc-static-local-var-4.patch.disabled b/test/integration/rhel-8.3/gcc-static-local-var-4.patch.disabled new file mode 100644 index 0000000..1b4f072 --- /dev/null +++ b/test/integration/rhel-8.3/gcc-static-local-var-4.patch.disabled @@ -0,0 +1,24 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-03-17 01:12:55.092891009 -0400 ++++ src/fs/aio.c 2020-03-17 01:14:03.844126354 -0400 +@@ -251,11 +251,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.3/gcc-static-local-var-4.test.disabled b/test/integration/rhel-8.3/gcc-static-local-var-4.test.disabled new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.3/gcc-static-local-var-4.test.disabled @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.3/gcc-static-local-var-5.patch b/test/integration/rhel-8.3/gcc-static-local-var-5.patch new file mode 100644 index 0000000..23be980 --- /dev/null +++ b/test/integration/rhel-8.3/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2020-03-17 13:21:09.547745268 -0400 ++++ src/kernel/audit.c 2020-03-17 13:57:21.709885683 -0400 +@@ -321,6 +321,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -331,6 +337,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -350,6 +357,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -396,6 +408,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.3/gcc-static-local-var-6.patch b/test/integration/rhel-8.3/gcc-static-local-var-6.patch new file mode 100644 index 0000000..7c418df --- /dev/null +++ b/test/integration/rhel-8.3/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2020-10-21 11:19:40.261740227 -0400 ++++ src/net/ipv6/netfilter.c 2020-10-21 11:21:09.055020262 -0400 +@@ -86,6 +86,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + int __nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -99,6 +101,9 @@ int __nf_ip6_route(struct net *net, stru + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.3/macro-callbacks.patch b/test/integration/rhel-8.3/macro-callbacks.patch new file mode 100644 index 0000000..ff41d17 --- /dev/null +++ b/test/integration/rhel-8.3/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2020-03-17 01:12:57.827099217 -0400 ++++ src/drivers/input/joydev.c 2020-03-17 01:13:25.725223634 -0400 +@@ -1084,3 +1084,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2020-03-17 01:12:57.831099522 -0400 ++++ src/drivers/input/misc/pcspkr.c 2020-03-17 01:13:25.725223634 -0400 +@@ -133,3 +133,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-03-17 01:12:55.092891009 -0400 ++++ src/fs/aio.c 2020-03-17 01:13:25.726223710 -0400 +@@ -49,6 +49,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.3/macro-printk.patch b/test/integration/rhel-8.3/macro-printk.patch new file mode 100644 index 0000000..1713578 --- /dev/null +++ b/test/integration/rhel-8.3/macro-printk.patch @@ -0,0 +1,148 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2020-03-17 01:12:55.534924670 -0400 ++++ src/net/ipv4/fib_frontend.c 2020-03-17 01:14:06.756348118 -0400 +@@ -789,6 +789,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -810,6 +811,7 @@ static int inet_rtm_newroute(struct sk_b + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2020-03-17 01:12:55.534924670 -0400 ++++ src/net/ipv4/fib_semantics.c 2020-03-17 01:14:06.756348118 -0400 +@@ -1025,6 +1025,7 @@ fib_convert_metrics(struct fib_info *fi, + fi->fib_metrics->metrics); + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1058,6 +1059,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1078,6 +1080,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1093,6 +1096,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1109,8 +1114,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1172,6 +1179,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1193,6 +1201,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1231,6 +1240,7 @@ struct fib_info *fib_create_info(struct + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1240,6 +1250,7 @@ struct fib_info *fib_create_info(struct + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1251,6 +1262,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1274,6 +1286,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1284,6 +1297,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2020-03-17 01:12:55.541925203 -0400 ++++ src/net/ipv4/fib_trie.c 2020-03-17 01:14:06.756348118 -0400 +@@ -1121,6 +1121,7 @@ static bool fib_valid_key_len(u32 key, u + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1143,11 +1144,14 @@ int fib_table_insert(struct net *net, st + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.3/meminfo-init-FAIL.patch b/test/integration/rhel-8.3/meminfo-init-FAIL.patch new file mode 100644 index 0000000..8f4bf4a --- /dev/null +++ b/test/integration/rhel-8.3/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-17 01:12:55.155895808 -0400 ++++ src/fs/proc/meminfo.c 2020-03-17 01:13:31.599670967 -0400 +@@ -153,6 +153,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.3/meminfo-init2-FAIL.patch b/test/integration/rhel-8.3/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..8cc8748 --- /dev/null +++ b/test/integration/rhel-8.3/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-17 01:12:55.155895808 -0400 ++++ src/fs/proc/meminfo.c 2020-03-17 01:13:28.655446767 -0400 +@@ -41,6 +41,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long sreclaimable, sunreclaim; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -153,6 +154,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.3/meminfo-string-LOADED.test b/test/integration/rhel-8.3/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.3/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.3/meminfo-string.patch b/test/integration/rhel-8.3/meminfo-string.patch new file mode 100644 index 0000000..79c22de --- /dev/null +++ b/test/integration/rhel-8.3/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-17 13:21:08.363654606 -0400 ++++ src/fs/proc/meminfo.c 2020-03-17 13:22:47.153218616 -0400 +@@ -121,7 +121,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + show_val_kb(m, "Percpu: ", pcpu_nr_pages()); + + #ifdef CONFIG_MEMORY_FAILURE diff --git a/test/integration/rhel-8.3/module-LOADED.test b/test/integration/rhel-8.3/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/rhel-8.3/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.3/module.patch b/test/integration/rhel-8.3/module.patch new file mode 100644 index 0000000..5dcee19 --- /dev/null +++ b/test/integration/rhel-8.3/module.patch @@ -0,0 +1,85 @@ +From 08078d00ab1749a6f84148a00d8d26572af4ec97 Mon Sep 17 00:00:00 2001 +Message-Id: <08078d00ab1749a6f84148a00d8d26572af4ec97.1586900628.git.jpoimboe@redhat.com> +From: Josh Poimboeuf +Date: Tue, 14 Apr 2020 15:17:51 -0500 +Subject: [PATCH] kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf +--- + fs/nfsd/export.c | 30 ++++++++++++++++++++++++++++++ + net/netlink/af_netlink.c | 5 +++++ + 2 files changed, 35 insertions(+) + +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2020-05-12 11:14:29.230792719 -0400 ++++ src/fs/nfsd/export.c 2020-05-12 11:15:17.078719042 -0400 +@@ -1196,15 +1196,45 @@ static void exp_flags(struct seq_file *m + } + } + ++#include ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); + struct cache_detail *cd = m->private; ++#ifdef CONFIG_X86_64 ++ unsigned long long sched_clock; ++ ++ alternative("ud2", "call yield", X86_FEATURE_ALWAYS); ++ alternative("call yield", "ud2", X86_FEATURE_IA64); ++ ++ sched_clock = paravirt_sched_clock(); ++ if (!jiffies) ++ printk("kpatch: sched_clock: %llu\n", sched_clock); ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&mcsafe_key)) ++ printk("kpatch: mcsafe_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif + + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2020-05-12 11:14:29.780768884 -0400 ++++ src/net/netlink/af_netlink.c 2020-05-12 11:15:17.078719042 -0400 +@@ -2788,4 +2788,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.3/multiple.test b/test/integration/rhel-8.3/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.3/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.3/new-function.patch b/test/integration/rhel-8.3/new-function.patch new file mode 100644 index 0000000..dab2c63 --- /dev/null +++ b/test/integration/rhel-8.3/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2020-03-17 01:12:58.001112468 -0400 ++++ src/drivers/tty/n_tty.c 2020-03-17 01:13:37.546123784 -0400 +@@ -2296,7 +2296,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2383,6 +2383,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.3/new-globals.patch b/test/integration/rhel-8.3/new-globals.patch new file mode 100644 index 0000000..085d8be --- /dev/null +++ b/test/integration/rhel-8.3/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-03-17 01:12:55.154895731 -0400 ++++ src/fs/proc/cmdline.c 2020-03-17 01:13:40.492348137 -0400 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-17 01:12:55.155895808 -0400 ++++ src/fs/proc/meminfo.c 2020-03-17 01:13:40.492348137 -0400 +@@ -21,6 +21,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -57,6 +59,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE); + sunreclaim = global_node_page_state(NR_SLAB_UNRECLAIMABLE); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.3/parainstructions-section.patch b/test/integration/rhel-8.3/parainstructions-section.patch new file mode 100644 index 0000000..5f03a97 --- /dev/null +++ b/test/integration/rhel-8.3/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2020-03-17 01:12:55.154895731 -0400 ++++ src/fs/proc/generic.c 2020-03-17 01:13:43.430571880 -0400 +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.3/shadow-newpid-LOADED.test.disabled b/test/integration/rhel-8.3/shadow-newpid-LOADED.test.disabled new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.3/shadow-newpid-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.3/shadow-newpid.patch.disabled b/test/integration/rhel-8.3/shadow-newpid.patch.disabled new file mode 100644 index 0000000..d737bfb --- /dev/null +++ b/test/integration/rhel-8.3/shadow-newpid.patch.disabled @@ -0,0 +1,77 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2020-03-17 01:12:55.154895731 -0400 ++++ src/fs/proc/array.c 2020-03-17 01:14:09.668569881 -0400 +@@ -370,12 +370,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2020-03-17 01:12:55.490921320 -0400 ++++ src/kernel/exit.c 2020-03-17 01:14:09.668569881 -0400 +@@ -762,6 +762,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -868,6 +869,8 @@ void __noreturn do_exit(long code) + exit_thread(tsk); + exit_umh(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-03-17 01:12:55.500922081 -0400 ++++ src/kernel/fork.c 2020-03-17 01:14:09.668569881 -0400 +@@ -2206,6 +2206,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2218,6 +2219,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2244,6 +2247,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.3/smp-locks-section.patch b/test/integration/rhel-8.3/smp-locks-section.patch new file mode 100644 index 0000000..664ec7e --- /dev/null +++ b/test/integration/rhel-8.3/smp-locks-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2020-03-17 01:12:58.012113306 -0400 ++++ src/drivers/tty/tty_buffer.c 2020-03-17 01:13:46.342793643 -0400 +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(str + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.3/special-static.patch.disabled b/test/integration/rhel-8.3/special-static.patch.disabled new file mode 100644 index 0000000..695f0a1 --- /dev/null +++ b/test/integration/rhel-8.3/special-static.patch.disabled @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-03-17 01:12:55.500922081 -0400 ++++ src/kernel/fork.c 2020-03-17 01:13:49.230013502 -0400 +@@ -1523,10 +1523,18 @@ static void posix_cpu_timers_init_group( + static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { } + #endif + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.3/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.3/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..798d21f --- /dev/null +++ b/test/integration/rhel-8.3/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From 2d6b7bce089e52563bd9c67df62f48e90b48047d Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index 26bae20f0553..506ebbf0a210 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -30,6 +30,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index f74e6bda1788..86f7d453549c 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -685,6 +685,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-8.3/tracepoints-section.patch b/test/integration/rhel-8.3/tracepoints-section.patch new file mode 100644 index 0000000..8b77551 --- /dev/null +++ b/test/integration/rhel-8.3/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timer.c src/kernel/time/timer.c +--- src.orig/kernel/time/timer.c 2020-03-17 01:12:55.499922005 -0400 ++++ src/kernel/time/timer.c 2020-03-17 01:13:52.157236408 -0400 +@@ -1696,6 +1696,9 @@ static __latent_entropy void run_timer_s + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.3/warn-detect-FAIL.patch b/test/integration/rhel-8.3/warn-detect-FAIL.patch new file mode 100644 index 0000000..e9518a9 --- /dev/null +++ b/test/integration/rhel-8.3/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2020-03-17 01:12:56.596005471 -0400 ++++ src/arch/x86/kvm/x86.c 2020-03-17 01:13:55.095460151 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.4/bug-table-section.patch b/test/integration/rhel-8.4/bug-table-section.patch new file mode 100644 index 0000000..f1e2af8 --- /dev/null +++ b/test/integration/rhel-8.4/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/proc_sysctl.c 2021-04-20 11:04:27.636102900 -0400 +@@ -338,6 +338,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.4/cmdline-string-LOADED.test b/test/integration/rhel-8.4/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.4/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.4/cmdline-string.patch b/test/integration/rhel-8.4/cmdline-string.patch new file mode 100644 index 0000000..0f01b59 --- /dev/null +++ b/test/integration/rhel-8.4/cmdline-string.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/cmdline.c 2021-04-20 11:04:30.118109128 -0400 +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.4/data-new-LOADED.test b/test/integration/rhel-8.4/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.4/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.4/data-new.patch b/test/integration/rhel-8.4/data-new.patch new file mode 100644 index 0000000..23e1bdd --- /dev/null +++ b/test/integration/rhel-8.4/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/meminfo.c 2021-04-20 11:04:32.584115315 -0400 +@@ -31,6 +31,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -146,6 +148,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.4/data-read-mostly.patch b/test/integration/rhel-8.4/data-read-mostly.patch new file mode 100644 index 0000000..482d997 --- /dev/null +++ b/test/integration/rhel-8.4/data-read-mostly.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2021-04-20 11:04:27.355102195 -0400 ++++ src/net/core/dev.c 2021-04-20 11:04:34.800120875 -0400 +@@ -5058,6 +5058,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.4/fixup-section.patch b/test/integration/rhel-8.4/fixup-section.patch new file mode 100644 index 0000000..6321af8 --- /dev/null +++ b/test/integration/rhel-8.4/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2021-04-20 11:04:26.675100489 -0400 ++++ src/fs/readdir.c 2021-04-20 11:04:36.984126354 -0400 +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.4/gcc-constprop.patch b/test/integration/rhel-8.4/gcc-constprop.patch new file mode 100644 index 0000000..dd62372 --- /dev/null +++ b/test/integration/rhel-8.4/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2021-04-20 11:04:27.325102120 -0400 ++++ src/kernel/time/timekeeping.c 2021-04-20 11:04:39.253132047 -0400 +@@ -1231,6 +1231,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.4/gcc-isra.patch b/test/integration/rhel-8.4/gcc-isra.patch new file mode 100644 index 0000000..51fd423 --- /dev/null +++ b/test/integration/rhel-8.4/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/proc_sysctl.c 2021-04-20 11:04:41.824138498 -0400 +@@ -53,6 +53,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.4/gcc-mangled-3.patch b/test/integration/rhel-8.4/gcc-mangled-3.patch new file mode 100644 index 0000000..699eb1a --- /dev/null +++ b/test/integration/rhel-8.4/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2021-04-20 11:04:27.343102165 -0400 ++++ src/mm/slub.c 2021-04-20 11:04:44.205144472 -0400 +@@ -5749,6 +5749,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.4/gcc-static-local-var-2.patch b/test/integration/rhel-8.4/gcc-static-local-var-2.patch new file mode 100644 index 0000000..f847008 --- /dev/null +++ b/test/integration/rhel-8.4/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2021-04-20 11:04:27.341102160 -0400 ++++ src/mm/mmap.c 2021-04-20 11:04:46.880151184 -0400 +@@ -1690,6 +1690,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.4/gcc-static-local-var-3.patch b/test/integration/rhel-8.4/gcc-static-local-var-3.patch new file mode 100644 index 0000000..8b9ec4f --- /dev/null +++ b/test/integration/rhel-8.4/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/reboot.c src/kernel/reboot.c +--- src.orig/kernel/reboot.c 2021-04-20 11:04:27.316102097 -0400 ++++ src/kernel/reboot.c 2021-04-20 11:04:49.155156892 -0400 +@@ -393,8 +393,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.4/gcc-static-local-var-4.patch b/test/integration/rhel-8.4/gcc-static-local-var-4.patch new file mode 100644 index 0000000..cdef516 --- /dev/null +++ b/test/integration/rhel-8.4/gcc-static-local-var-4.patch @@ -0,0 +1,23 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2021-04-20 11:04:26.671100479 -0400 ++++ src/fs/aio.c 2021-04-20 11:04:51.420162575 -0400 +@@ -248,11 +248,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + +-static void put_aio_ring_file(struct kioctx *ctx) ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ ++__always_inline static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.4/gcc-static-local-var-4.test b/test/integration/rhel-8.4/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.4/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.4/gcc-static-local-var-5.patch b/test/integration/rhel-8.4/gcc-static-local-var-5.patch new file mode 100644 index 0000000..1ba0205 --- /dev/null +++ b/test/integration/rhel-8.4/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2021-04-20 11:04:27.312102087 -0400 ++++ src/kernel/audit.c 2021-04-20 11:04:53.691168273 -0400 +@@ -327,6 +327,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -337,6 +343,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -356,6 +363,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -402,6 +414,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.4/gcc-static-local-var-6.patch b/test/integration/rhel-8.4/gcc-static-local-var-6.patch new file mode 100644 index 0000000..9163e02 --- /dev/null +++ b/test/integration/rhel-8.4/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2021-04-20 11:04:27.369102230 -0400 ++++ src/net/ipv6/netfilter.c 2021-04-20 11:04:56.097174309 -0400 +@@ -86,6 +86,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + int __nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -99,6 +101,9 @@ int __nf_ip6_route(struct net *net, stru + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.4/macro-callbacks.patch b/test/integration/rhel-8.4/macro-callbacks.patch new file mode 100644 index 0000000..68033a3 --- /dev/null +++ b/test/integration/rhel-8.4/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2021-04-20 11:04:26.086099011 -0400 ++++ src/drivers/input/joydev.c 2021-04-20 11:04:58.399180085 -0400 +@@ -1084,3 +1084,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2021-04-20 11:04:26.090099021 -0400 ++++ src/drivers/input/misc/pcspkr.c 2021-04-20 11:04:58.399180085 -0400 +@@ -133,3 +133,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2021-04-20 11:04:26.671100479 -0400 ++++ src/fs/aio.c 2021-04-20 11:04:58.399180085 -0400 +@@ -49,6 +49,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.4/macro-printk.patch b/test/integration/rhel-8.4/macro-printk.patch new file mode 100644 index 0000000..f5f9004 --- /dev/null +++ b/test/integration/rhel-8.4/macro-printk.patch @@ -0,0 +1,149 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2021-04-20 11:04:27.363102215 -0400 ++++ src/net/ipv4/fib_frontend.c 2021-04-20 11:05:00.587185575 -0400 +@@ -790,6 +790,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -811,6 +812,7 @@ static int inet_rtm_newroute(struct sk_b + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2021-04-20 11:04:27.363102215 -0400 ++++ src/net/ipv4/fib_semantics.c 2021-04-20 11:05:00.588185577 -0400 +@@ -1023,6 +1023,7 @@ static bool fib_valid_prefsrc(struct fib + return true; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1056,6 +1057,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1076,6 +1078,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1089,6 +1092,8 @@ struct fib_info *fib_create_info(struct + } + + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1144,9 +1149,11 @@ struct fib_info *fib_create_info(struct + "LWT encap type not specified"); + goto err_inval; + } ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + err = lwtunnel_build_state(cfg->fc_encap_type, + cfg->fc_encap, AF_INET, cfg, + &lwtstate, extack); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1164,6 +1171,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1185,6 +1193,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1223,6 +1232,7 @@ struct fib_info *fib_create_info(struct + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1232,6 +1242,7 @@ struct fib_info *fib_create_info(struct + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1243,6 +1254,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1266,6 +1278,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1276,6 +1289,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2021-04-20 11:04:27.363102215 -0400 ++++ src/net/ipv4/fib_trie.c 2021-04-20 11:05:00.588185577 -0400 +@@ -1174,6 +1174,7 @@ static void fib_remove_alias(struct trie + struct key_vector *l, struct fib_alias *old); + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1195,11 +1196,14 @@ int fib_table_insert(struct net *net, st + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.4/meminfo-init-FAIL.patch b/test/integration/rhel-8.4/meminfo-init-FAIL.patch new file mode 100644 index 0000000..abd02dc --- /dev/null +++ b/test/integration/rhel-8.4/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/meminfo.c 2021-04-20 11:05:05.090196873 -0400 +@@ -156,6 +156,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.4/meminfo-init2-FAIL.patch b/test/integration/rhel-8.4/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..b261bab --- /dev/null +++ b/test/integration/rhel-8.4/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/meminfo.c 2021-04-20 11:05:02.874191313 -0400 +@@ -41,6 +41,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long sreclaimable, sunreclaim; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -156,6 +157,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.4/meminfo-string-LOADED.test b/test/integration/rhel-8.4/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.4/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.4/meminfo-string.patch b/test/integration/rhel-8.4/meminfo-string.patch new file mode 100644 index 0000000..d07429a --- /dev/null +++ b/test/integration/rhel-8.4/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/meminfo.c 2021-04-20 11:05:07.263202325 -0400 +@@ -120,7 +120,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + show_val_kb(m, "Percpu: ", pcpu_nr_pages()); + + #ifdef CONFIG_MEMORY_FAILURE diff --git a/test/integration/rhel-8.4/module-LOADED.test b/test/integration/rhel-8.4/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/rhel-8.4/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.4/module.patch b/test/integration/rhel-8.4/module.patch new file mode 100644 index 0000000..812dba8 --- /dev/null +++ b/test/integration/rhel-8.4/module.patch @@ -0,0 +1,85 @@ +From 08078d00ab1749a6f84148a00d8d26572af4ec97 Mon Sep 17 00:00:00 2001 +Message-Id: <08078d00ab1749a6f84148a00d8d26572af4ec97.1586900628.git.jpoimboe@redhat.com> +From: Josh Poimboeuf +Date: Tue, 14 Apr 2020 15:17:51 -0500 +Subject: [PATCH] kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf +--- + fs/nfsd/export.c | 30 ++++++++++++++++++++++++++++++ + net/netlink/af_netlink.c | 5 +++++ + 2 files changed, 35 insertions(+) + +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2021-04-20 11:04:26.703100559 -0400 ++++ src/fs/nfsd/export.c 2021-04-20 11:05:09.399207684 -0400 +@@ -1221,15 +1221,45 @@ static void exp_flags(struct seq_file *m + } + } + ++#include ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); + struct cache_detail *cd = m->private; ++#ifdef CONFIG_X86_64 ++ unsigned long long sched_clock; ++ ++ alternative("ud2", "call yield", X86_FEATURE_ALWAYS); ++ alternative("call yield", "ud2", X86_FEATURE_IA64); ++ ++ sched_clock = paravirt_sched_clock(); ++ if (!jiffies) ++ printk("kpatch: sched_clock: %llu\n", sched_clock); ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&mcsafe_key)) ++ printk("kpatch: mcsafe_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif + + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2021-04-20 11:04:27.385102270 -0400 ++++ src/net/netlink/af_netlink.c 2021-04-20 11:05:09.399207684 -0400 +@@ -2879,4 +2879,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.4/multiple.test b/test/integration/rhel-8.4/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.4/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.4/new-function.patch b/test/integration/rhel-8.4/new-function.patch new file mode 100644 index 0000000..35b222e --- /dev/null +++ b/test/integration/rhel-8.4/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2021-04-20 11:04:26.603100308 -0400 ++++ src/drivers/tty/n_tty.c 2021-04-20 11:05:11.672213387 -0400 +@@ -2296,7 +2296,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2383,6 +2383,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.4/new-globals.patch b/test/integration/rhel-8.4/new-globals.patch new file mode 100644 index 0000000..1ab87c0 --- /dev/null +++ b/test/integration/rhel-8.4/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/cmdline.c 2021-04-20 11:05:13.847218845 -0400 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/meminfo.c 2021-04-20 11:05:13.847218845 -0400 +@@ -21,6 +21,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -57,6 +59,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B); + sunreclaim = global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.4/parainstructions-section.patch b/test/integration/rhel-8.4/parainstructions-section.patch new file mode 100644 index 0000000..843ee94 --- /dev/null +++ b/test/integration/rhel-8.4/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/generic.c 2021-04-20 11:05:16.189224721 -0400 +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.4/shadow-newpid-LOADED.test b/test/integration/rhel-8.4/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.4/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.4/shadow-newpid.patch b/test/integration/rhel-8.4/shadow-newpid.patch new file mode 100644 index 0000000..3292906 --- /dev/null +++ b/test/integration/rhel-8.4/shadow-newpid.patch @@ -0,0 +1,75 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2021-04-20 11:04:26.717100594 -0400 ++++ src/fs/proc/array.c 2021-04-20 11:05:18.430230343 -0400 +@@ -370,12 +370,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2021-04-20 11:04:27.314102092 -0400 ++++ src/kernel/exit.c 2021-04-20 11:05:18.430230343 -0400 +@@ -701,6 +701,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -794,6 +795,8 @@ void __noreturn do_exit(long code) + exit_task_work(tsk); + exit_thread(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2021-04-20 11:04:27.315102095 -0400 ++++ src/kernel/fork.c 2021-04-20 11:05:18.431230346 -0400 +@@ -2222,6 +2222,7 @@ struct mm_struct *copy_init_mm(void) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2234,6 +2235,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2260,6 +2263,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.4/smp-locks-section.patch b/test/integration/rhel-8.4/smp-locks-section.patch new file mode 100644 index 0000000..d6273a5 --- /dev/null +++ b/test/integration/rhel-8.4/smp-locks-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2021-04-20 11:04:26.609100323 -0400 ++++ src/drivers/tty/tty_buffer.c 2021-04-20 11:05:20.584235748 -0400 +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(str + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.4/special-static.patch b/test/integration/rhel-8.4/special-static.patch new file mode 100644 index 0000000..8c1e197 --- /dev/null +++ b/test/integration/rhel-8.4/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2021-04-20 11:04:27.315102095 -0400 ++++ src/kernel/fork.c 2021-04-20 11:05:23.010241835 -0400 +@@ -1554,10 +1554,18 @@ static void posix_cpu_timers_init_group( + posix_cputimers_group_init(pct, cpu_limit); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.4/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.4/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..cd62d06 --- /dev/null +++ b/test/integration/rhel-8.4/symvers-disagreement-FAIL.patch @@ -0,0 +1,46 @@ +From 2d6b7bce089e52563bd9c67df62f48e90b48047d Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff -Nupr src.orig/drivers/base/core.c src/drivers/base/core.c +--- src.orig/drivers/base/core.c 2021-04-20 11:04:25.703098050 -0400 ++++ src/drivers/base/core.c 2021-04-20 11:05:25.287247548 -0400 +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff -Nupr src.orig/drivers/usb/core/usb.c src/drivers/usb/core/usb.c +--- src.orig/drivers/usb/core/usb.c 2021-04-20 11:04:26.613100333 -0400 ++++ src/drivers/usb/core/usb.c 2021-04-20 11:05:25.287247548 -0400 +@@ -693,6 +693,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; diff --git a/test/integration/rhel-8.4/syscall-LOADED.test b/test/integration/rhel-8.4/syscall-LOADED.test new file mode 100755 index 0000000..3a2fd88 --- /dev/null +++ b/test/integration/rhel-8.4/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/rhel-8.4/syscall.patch b/test/integration/rhel-8.4/syscall.patch new file mode 100644 index 0000000..4679d01 --- /dev/null +++ b/test/integration/rhel-8.4/syscall.patch @@ -0,0 +1,26 @@ +diff --git a/kernel/sys.c b/kernel/sys.c +index 871c0848f05c8..479bf8725d2e6 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1241,14 +1241,18 @@ static int override_release(char __user *release, size_t len) + return ret; + } + +-SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) ++#include "kpatch-syscall.h" ++KPATCH_SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + { ++ struct new_utsname tmp; + int errno = 0; + + down_read(&uts_sem); +- if (copy_to_user(name, utsname(), sizeof *name)) +- errno = -EFAULT; ++ memcpy(&tmp, utsname(), sizeof(tmp)); + up_read(&uts_sem); ++ strcat(tmp.sysname, ".kpatch"); ++ if (copy_to_user(name, &tmp, sizeof(tmp))) ++ errno = -EFAULT; + + if (!errno && override_release(name->release, sizeof(name->release))) + errno = -EFAULT; diff --git a/test/integration/rhel-8.4/tracepoints-section.patch b/test/integration/rhel-8.4/tracepoints-section.patch new file mode 100644 index 0000000..5af1b46 --- /dev/null +++ b/test/integration/rhel-8.4/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timer.c src/kernel/time/timer.c +--- src.orig/kernel/time/timer.c 2021-04-20 11:04:27.325102120 -0400 ++++ src/kernel/time/timer.c 2021-04-20 11:05:27.596253341 -0400 +@@ -1751,6 +1751,9 @@ static __latent_entropy void run_timer_s + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.4/warn-detect-FAIL.patch b/test/integration/rhel-8.4/warn-detect-FAIL.patch new file mode 100644 index 0000000..6c1e7ac --- /dev/null +++ b/test/integration/rhel-8.4/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2021-04-20 11:04:27.273101989 -0400 ++++ src/arch/x86/kvm/x86.c 2021-04-20 11:05:29.870259047 -0400 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.6/bug-table-section.patch b/test/integration/rhel-8.6/bug-table-section.patch new file mode 100644 index 0000000..7d05c04 --- /dev/null +++ b/test/integration/rhel-8.6/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2022-04-29 16:08:21.409780550 -0400 ++++ src/fs/proc/proc_sysctl.c 2022-04-29 16:08:21.908782326 -0400 +@@ -338,6 +338,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.6/cmdline-string-LOADED.test b/test/integration/rhel-8.6/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.6/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.6/cmdline-string.patch b/test/integration/rhel-8.6/cmdline-string.patch new file mode 100644 index 0000000..f547c87 --- /dev/null +++ b/test/integration/rhel-8.6/cmdline-string.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2022-04-29 16:08:21.408780547 -0400 ++++ src/fs/proc/cmdline.c 2022-04-29 16:08:24.434791315 -0400 +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.6/data-new-LOADED.test b/test/integration/rhel-8.6/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.6/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.6/data-new.patch b/test/integration/rhel-8.6/data-new.patch new file mode 100644 index 0000000..2fd4b91 --- /dev/null +++ b/test/integration/rhel-8.6/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-04-29 16:08:21.409780550 -0400 ++++ src/fs/proc/meminfo.c 2022-04-29 16:08:26.826799828 -0400 +@@ -31,6 +31,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -150,6 +152,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.6/data-read-mostly.patch b/test/integration/rhel-8.6/data-read-mostly.patch new file mode 100644 index 0000000..6f9e324 --- /dev/null +++ b/test/integration/rhel-8.6/data-read-mostly.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2022-04-29 16:08:21.614781280 -0400 ++++ src/net/core/dev.c 2022-04-29 16:08:29.184808219 -0400 +@@ -5278,6 +5278,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.6/fixup-section.patch b/test/integration/rhel-8.6/fixup-section.patch new file mode 100644 index 0000000..efcbfc2 --- /dev/null +++ b/test/integration/rhel-8.6/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2022-04-29 16:08:21.360780376 -0400 ++++ src/fs/readdir.c 2022-04-29 16:08:31.537816593 -0400 +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.6/gcc-constprop.patch b/test/integration/rhel-8.6/gcc-constprop.patch new file mode 100644 index 0000000..d923fbb --- /dev/null +++ b/test/integration/rhel-8.6/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2022-04-29 16:08:21.581781162 -0400 ++++ src/kernel/time/timekeeping.c 2022-04-29 16:08:33.934825123 -0400 +@@ -1231,6 +1231,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.6/gcc-isra.patch b/test/integration/rhel-8.6/gcc-isra.patch new file mode 100644 index 0000000..c873c3b --- /dev/null +++ b/test/integration/rhel-8.6/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2022-04-29 16:08:21.409780550 -0400 ++++ src/fs/proc/proc_sysctl.c 2022-04-29 16:08:36.314833593 -0400 +@@ -53,6 +53,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.6/gcc-mangled-3.patch b/test/integration/rhel-8.6/gcc-mangled-3.patch new file mode 100644 index 0000000..19d54e1 --- /dev/null +++ b/test/integration/rhel-8.6/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2022-04-29 16:08:21.601781233 -0400 ++++ src/mm/slub.c 2022-04-29 16:08:38.713842130 -0400 +@@ -6107,6 +6107,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.6/gcc-static-local-var-2.patch b/test/integration/rhel-8.6/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a796431 --- /dev/null +++ b/test/integration/rhel-8.6/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2022-04-29 16:08:21.599781226 -0400 ++++ src/mm/mmap.c 2022-04-29 16:08:41.140850766 -0400 +@@ -1714,6 +1714,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.6/gcc-static-local-var-3.patch b/test/integration/rhel-8.6/gcc-static-local-var-3.patch new file mode 100644 index 0000000..286975e --- /dev/null +++ b/test/integration/rhel-8.6/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/reboot.c src/kernel/reboot.c +--- src.orig/kernel/reboot.c 2022-04-29 16:08:21.578781152 -0400 ++++ src/kernel/reboot.c 2022-04-29 16:08:43.524859249 -0400 +@@ -393,8 +393,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.6/gcc-static-local-var-4.patch b/test/integration/rhel-8.6/gcc-static-local-var-4.patch new file mode 100644 index 0000000..ad95d02 --- /dev/null +++ b/test/integration/rhel-8.6/gcc-static-local-var-4.patch @@ -0,0 +1,23 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2022-04-29 16:08:21.357780365 -0400 ++++ src/fs/aio.c 2022-04-29 16:08:45.936867833 -0400 +@@ -247,11 +247,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + +-static void put_aio_ring_file(struct kioctx *ctx) ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ ++__always_inline static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.6/gcc-static-local-var-4.test b/test/integration/rhel-8.6/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.6/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.6/gcc-static-local-var-5.patch b/test/integration/rhel-8.6/gcc-static-local-var-5.patch new file mode 100644 index 0000000..44ee755 --- /dev/null +++ b/test/integration/rhel-8.6/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2022-04-29 16:08:21.568781116 -0400 ++++ src/kernel/audit.c 2022-04-29 16:08:48.347876413 -0400 +@@ -327,6 +327,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -337,6 +343,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -356,6 +363,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -402,6 +414,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.6/gcc-static-local-var-6.patch b/test/integration/rhel-8.6/gcc-static-local-var-6.patch new file mode 100644 index 0000000..14c79a5 --- /dev/null +++ b/test/integration/rhel-8.6/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2022-04-29 16:08:21.627781326 -0400 ++++ src/net/ipv6/netfilter.c 2022-04-29 16:08:50.811885181 -0400 +@@ -86,6 +86,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + int __nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -99,6 +101,9 @@ int __nf_ip6_route(struct net *net, stru + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.6/macro-callbacks.patch b/test/integration/rhel-8.6/macro-callbacks.patch new file mode 100644 index 0000000..24a2579 --- /dev/null +++ b/test/integration/rhel-8.6/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2022-04-29 16:08:20.747778194 -0400 ++++ src/drivers/input/joydev.c 2022-04-29 16:08:53.228893783 -0400 +@@ -1087,3 +1087,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2022-04-29 16:08:20.752778212 -0400 ++++ src/drivers/input/misc/pcspkr.c 2022-04-29 16:08:53.229893786 -0400 +@@ -133,3 +133,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2022-04-29 16:08:21.357780365 -0400 ++++ src/fs/aio.c 2022-04-29 16:08:53.229893786 -0400 +@@ -48,6 +48,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.6/macro-printk.patch b/test/integration/rhel-8.6/macro-printk.patch new file mode 100644 index 0000000..74bd4a9 --- /dev/null +++ b/test/integration/rhel-8.6/macro-printk.patch @@ -0,0 +1,149 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2022-04-29 16:08:21.621781305 -0400 ++++ src/net/ipv4/fib_frontend.c 2022-04-29 16:08:55.676902494 -0400 +@@ -792,6 +792,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -813,6 +814,7 @@ static int inet_rtm_newroute(struct sk_b + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2022-04-29 16:08:21.621781305 -0400 ++++ src/net/ipv4/fib_semantics.c 2022-04-29 16:08:55.677902498 -0400 +@@ -1022,6 +1022,7 @@ static bool fib_valid_prefsrc(struct fib + return true; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1055,6 +1056,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1075,6 +1077,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1088,6 +1091,8 @@ struct fib_info *fib_create_info(struct + } + + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1143,9 +1148,11 @@ struct fib_info *fib_create_info(struct + "LWT encap type not specified"); + goto err_inval; + } ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + err = lwtunnel_build_state(cfg->fc_encap_type, + cfg->fc_encap, AF_INET, cfg, + &lwtstate, extack); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1163,6 +1170,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1184,6 +1192,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1222,6 +1231,7 @@ struct fib_info *fib_create_info(struct + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1231,6 +1241,7 @@ struct fib_info *fib_create_info(struct + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1242,6 +1253,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1265,6 +1277,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1275,6 +1288,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2022-04-29 16:08:21.621781305 -0400 ++++ src/net/ipv4/fib_trie.c 2022-04-29 16:08:55.677902498 -0400 +@@ -1174,6 +1174,7 @@ static void fib_remove_alias(struct trie + struct key_vector *l, struct fib_alias *old); + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1195,11 +1196,14 @@ int fib_table_insert(struct net *net, st + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.6/meminfo-init-FAIL.patch b/test/integration/rhel-8.6/meminfo-init-FAIL.patch new file mode 100644 index 0000000..5105bed --- /dev/null +++ b/test/integration/rhel-8.6/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-04-29 16:08:21.409780550 -0400 ++++ src/fs/proc/meminfo.c 2022-04-29 16:09:00.653920206 -0400 +@@ -160,6 +160,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.6/meminfo-init2-FAIL.patch b/test/integration/rhel-8.6/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..913d67a --- /dev/null +++ b/test/integration/rhel-8.6/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-04-29 16:08:21.409780550 -0400 ++++ src/fs/proc/meminfo.c 2022-04-29 16:08:58.219911544 -0400 +@@ -41,6 +41,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long sreclaimable, sunreclaim; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -160,6 +161,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.6/meminfo-string-LOADED.test b/test/integration/rhel-8.6/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.6/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.6/meminfo-string.patch b/test/integration/rhel-8.6/meminfo-string.patch new file mode 100644 index 0000000..6992c27 --- /dev/null +++ b/test/integration/rhel-8.6/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-04-29 16:08:21.409780550 -0400 ++++ src/fs/proc/meminfo.c 2022-04-29 16:09:03.079928840 -0400 +@@ -124,7 +124,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + show_val_kb(m, "Percpu: ", pcpu_nr_pages()); + + #ifdef CONFIG_MEMORY_FAILURE diff --git a/test/integration/rhel-8.6/module-LOADED.test b/test/integration/rhel-8.6/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/rhel-8.6/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.6/module.patch b/test/integration/rhel-8.6/module.patch new file mode 100644 index 0000000..4818ee0 --- /dev/null +++ b/test/integration/rhel-8.6/module.patch @@ -0,0 +1,85 @@ +From 08078d00ab1749a6f84148a00d8d26572af4ec97 Mon Sep 17 00:00:00 2001 +Message-Id: <08078d00ab1749a6f84148a00d8d26572af4ec97.1586900628.git.jpoimboe@redhat.com> +From: Josh Poimboeuf +Date: Tue, 14 Apr 2020 15:17:51 -0500 +Subject: [PATCH] kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf +--- + fs/nfsd/export.c | 30 ++++++++++++++++++++++++++++++ + net/netlink/af_netlink.c | 5 +++++ + 2 files changed, 35 insertions(+) + +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2022-04-29 16:08:21.394780497 -0400 ++++ src/fs/nfsd/export.c 2022-04-29 16:09:05.551937637 -0400 +@@ -1228,15 +1228,45 @@ static void exp_flags(struct seq_file *m + } + } + ++#include ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); + struct cache_detail *cd = m->private; ++#ifdef CONFIG_X86_64 ++ unsigned long long sched_clock; ++ ++ alternative("ud2", "call yield", X86_FEATURE_ALWAYS); ++ alternative("call yield", "ud2", X86_FEATURE_IA64); ++ ++ sched_clock = paravirt_sched_clock(); ++ if (!jiffies) ++ printk("kpatch: sched_clock: %llu\n", sched_clock); ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&mcsafe_key)) ++ printk("kpatch: mcsafe_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif + + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2022-04-29 16:08:21.644781386 -0400 ++++ src/net/netlink/af_netlink.c 2022-04-29 16:09:05.551937637 -0400 +@@ -2887,4 +2887,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.6/multiple.test b/test/integration/rhel-8.6/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.6/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.6/new-function.patch b/test/integration/rhel-8.6/new-function.patch new file mode 100644 index 0000000..146ca31 --- /dev/null +++ b/test/integration/rhel-8.6/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2022-04-29 16:08:21.286780112 -0400 ++++ src/drivers/tty/n_tty.c 2022-04-29 16:09:07.953946185 -0400 +@@ -2298,7 +2298,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2385,6 +2385,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.6/new-globals.patch b/test/integration/rhel-8.6/new-globals.patch new file mode 100644 index 0000000..6b70151 --- /dev/null +++ b/test/integration/rhel-8.6/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2022-04-29 16:08:21.408780547 -0400 ++++ src/fs/proc/cmdline.c 2022-04-29 16:09:10.382954829 -0400 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-04-29 16:08:21.409780550 -0400 ++++ src/fs/proc/meminfo.c 2022-04-29 16:09:10.382954829 -0400 +@@ -21,6 +21,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -57,6 +59,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B); + sunreclaim = global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.6/parainstructions-section.patch b/test/integration/rhel-8.6/parainstructions-section.patch new file mode 100644 index 0000000..ada24c0 --- /dev/null +++ b/test/integration/rhel-8.6/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2022-04-29 16:08:21.409780550 -0400 ++++ src/fs/proc/generic.c 2022-04-29 16:09:12.837963565 -0400 +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.6/shadow-newpid-LOADED.test b/test/integration/rhel-8.6/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.6/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.6/shadow-newpid.patch b/test/integration/rhel-8.6/shadow-newpid.patch new file mode 100644 index 0000000..50d8adf --- /dev/null +++ b/test/integration/rhel-8.6/shadow-newpid.patch @@ -0,0 +1,75 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2022-04-29 16:08:21.408780547 -0400 ++++ src/fs/proc/array.c 2022-04-29 16:09:15.255972171 -0400 +@@ -372,12 +372,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2022-04-29 16:08:21.572781130 -0400 ++++ src/kernel/exit.c 2022-04-29 16:09:15.256972174 -0400 +@@ -703,6 +703,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -803,6 +804,8 @@ void __noreturn do_exit(long code) + exit_task_work(tsk); + exit_thread(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2022-04-29 16:08:21.572781130 -0400 ++++ src/kernel/fork.c 2022-04-29 16:09:15.256972174 -0400 +@@ -2364,6 +2364,7 @@ struct mm_struct *copy_init_mm(void) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2376,6 +2377,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2402,6 +2405,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.6/smp-locks-section.patch b/test/integration/rhel-8.6/smp-locks-section.patch new file mode 100644 index 0000000..8c6272d --- /dev/null +++ b/test/integration/rhel-8.6/smp-locks-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2022-04-29 16:08:21.287780116 -0400 ++++ src/drivers/tty/tty_buffer.c 2022-04-29 16:09:17.674980779 -0400 +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(str + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.6/special-static.patch b/test/integration/rhel-8.6/special-static.patch new file mode 100644 index 0000000..ba6e75a --- /dev/null +++ b/test/integration/rhel-8.6/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2022-04-29 16:08:21.572781130 -0400 ++++ src/kernel/fork.c 2022-04-29 16:09:20.112989455 -0400 +@@ -1579,10 +1579,18 @@ static void posix_cpu_timers_init_group( + posix_cputimers_group_init(pct, cpu_limit); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.6/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.6/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..04b7567 --- /dev/null +++ b/test/integration/rhel-8.6/symvers-disagreement-FAIL.patch @@ -0,0 +1,46 @@ +From 2d6b7bce089e52563bd9c67df62f48e90b48047d Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff -Nupr src.orig/drivers/base/core.c src/drivers/base/core.c +--- src.orig/drivers/base/core.c 2022-04-29 16:08:20.289776564 -0400 ++++ src/drivers/base/core.c 2022-04-29 16:09:22.510997989 -0400 +@@ -33,6 +33,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff -Nupr src.orig/drivers/usb/core/usb.c src/drivers/usb/core/usb.c +--- src.orig/drivers/usb/core/usb.c 2022-04-29 16:08:21.297780151 -0400 ++++ src/drivers/usb/core/usb.c 2022-04-29 16:09:22.510997989 -0400 +@@ -739,6 +739,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; diff --git a/test/integration/rhel-8.6/syscall-LOADED.test b/test/integration/rhel-8.6/syscall-LOADED.test new file mode 100755 index 0000000..3a2fd88 --- /dev/null +++ b/test/integration/rhel-8.6/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/rhel-8.6/syscall.patch b/test/integration/rhel-8.6/syscall.patch new file mode 100644 index 0000000..1494888 --- /dev/null +++ b/test/integration/rhel-8.6/syscall.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2022-04-29 16:10:59.577343400 -0400 ++++ src/kernel/sys.c 2022-04-29 16:10:59.917344609 -0400 +@@ -1241,14 +1241,18 @@ static int override_release(char __user + return ret; + } + +-SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) ++#include "kpatch-syscall.h" ++KPATCH_SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + { ++ struct new_utsname tmp; + int errno = 0; + + down_read(&uts_sem); +- if (copy_to_user(name, utsname(), sizeof *name)) +- errno = -EFAULT; ++ memcpy(&tmp, utsname(), sizeof(tmp)); + up_read(&uts_sem); ++ strcat(tmp.sysname, ".kpatch"); ++ if (copy_to_user(name, &tmp, sizeof(tmp))) ++ errno = -EFAULT; + + if (!errno && override_release(name->release, sizeof(name->release))) + errno = -EFAULT; diff --git a/test/integration/rhel-8.6/tracepoints-section.patch b/test/integration/rhel-8.6/tracepoints-section.patch new file mode 100644 index 0000000..9d60262 --- /dev/null +++ b/test/integration/rhel-8.6/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timer.c src/kernel/time/timer.c +--- src.orig/kernel/time/timer.c 2022-04-29 16:08:21.581781162 -0400 ++++ src/kernel/time/timer.c 2022-04-29 16:09:24.914006540 -0400 +@@ -1747,6 +1747,9 @@ static __latent_entropy void run_timer_s + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.6/warn-detect-FAIL.patch b/test/integration/rhel-8.6/warn-detect-FAIL.patch new file mode 100644 index 0000000..2245584 --- /dev/null +++ b/test/integration/rhel-8.6/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2022-04-29 16:08:20.241776394 -0400 ++++ src/arch/x86/kvm/x86.c 2022-04-29 16:09:27.340015174 -0400 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-9.0/data-new-LOADED.test b/test/integration/rhel-9.0/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-9.0/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-9.0/data-new.patch b/test/integration/rhel-9.0/data-new.patch new file mode 100644 index 0000000..6b94642 --- /dev/null +++ b/test/integration/rhel-9.0/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-04-29 15:52:13.399335763 -0400 ++++ src/fs/proc/meminfo.c 2022-04-29 15:52:20.014359304 -0400 +@@ -29,6 +29,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -145,6 +147,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-9.0/gcc-static-local-var-6.patch b/test/integration/rhel-9.0/gcc-static-local-var-6.patch new file mode 100644 index 0000000..dbd9f34 --- /dev/null +++ b/test/integration/rhel-9.0/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2022-04-29 15:52:13.640336621 -0400 ++++ src/net/ipv6/netfilter.c 2022-04-29 15:52:45.295449272 -0400 +@@ -91,6 +91,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + int __nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -104,6 +106,9 @@ int __nf_ip6_route(struct net *net, stru + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-9.0/macro-callbacks.patch b/test/integration/rhel-9.0/macro-callbacks.patch new file mode 100644 index 0000000..ab018c3 --- /dev/null +++ b/test/integration/rhel-9.0/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2022-04-29 15:52:12.634333041 -0400 ++++ src/drivers/input/joydev.c 2022-04-29 15:52:48.073459158 -0400 +@@ -1086,3 +1086,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2022-04-29 15:52:12.640333062 -0400 ++++ src/drivers/input/misc/pcspkr.c 2022-04-29 15:52:48.073459158 -0400 +@@ -134,3 +134,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2022-04-29 15:52:13.345335571 -0400 ++++ src/fs/aio.c 2022-04-29 15:52:48.073459158 -0400 +@@ -50,6 +50,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-9.0/module-LOADED.test b/test/integration/rhel-9.0/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/rhel-9.0/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-9.0/module.patch b/test/integration/rhel-9.0/module.patch new file mode 100644 index 0000000..7dcd332 --- /dev/null +++ b/test/integration/rhel-9.0/module.patch @@ -0,0 +1,79 @@ +kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf + +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2022-04-29 15:52:13.385335713 -0400 ++++ src/fs/nfsd/export.c 2022-04-29 15:53:02.037508852 -0400 +@@ -1294,6 +1294,10 @@ static void exp_flags(struct seq_file *m + } + } + ++#include ++extern char *kpatch_string(void); ++ ++__attribute__((optimize("-fno-optimize-sibling-calls"))) + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1301,12 +1305,36 @@ static int e_show(struct seq_file *m, vo + struct cache_detail *cd = m->private; + bool export_stats = is_export_stats_file(m); + ++#ifdef CONFIG_X86_64 ++ alternative("ud2", "call single_task_running", X86_FEATURE_ALWAYS); ++ alternative("call single_task_running", "ud2", X86_FEATURE_IA64); ++ ++ slow_down_io(); /* paravirt call */ ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&memcg_kmem_enabled_key)) ++ printk("kpatch: memcg_kmem_enabled_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif ++ + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + if (export_stats) + seq_puts(m, "# Path Client Start-time\n#\tStats\n"); + else + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2022-04-29 15:52:13.657336681 -0400 ++++ src/net/netlink/af_netlink.c 2022-04-29 15:53:02.038508855 -0400 +@@ -2908,4 +2908,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-9.0/multiple.test b/test/integration/rhel-9.0/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-9.0/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-9.0/new-function.patch b/test/integration/rhel-9.0/new-function.patch new file mode 100644 index 0000000..3e6cadd --- /dev/null +++ b/test/integration/rhel-9.0/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2022-04-29 15:52:13.274335318 -0400 ++++ src/drivers/tty/n_tty.c 2022-04-29 15:53:04.777518603 -0400 +@@ -2253,7 +2253,7 @@ more_to_be_read: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2340,6 +2340,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-9.0/new-globals.patch b/test/integration/rhel-9.0/new-globals.patch new file mode 100644 index 0000000..a296821 --- /dev/null +++ b/test/integration/rhel-9.0/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2022-04-29 15:52:13.399335763 -0400 ++++ src/fs/proc/cmdline.c 2022-04-29 15:53:07.589528610 -0400 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2022-04-29 15:52:13.399335763 -0400 ++++ src/fs/proc/meminfo.c 2022-04-29 15:53:07.589528610 -0400 +@@ -19,6 +19,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -55,6 +57,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B); + sunreclaim = global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-9.0/shadow-newpid-LOADED.test b/test/integration/rhel-9.0/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-9.0/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-9.0/shadow-newpid.patch b/test/integration/rhel-9.0/shadow-newpid.patch new file mode 100644 index 0000000..c020e28 --- /dev/null +++ b/test/integration/rhel-9.0/shadow-newpid.patch @@ -0,0 +1,75 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2022-04-29 15:52:13.399335763 -0400 ++++ src/fs/proc/array.c 2022-04-29 15:53:13.252548763 -0400 +@@ -402,12 +402,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2022-04-29 15:52:13.577336397 -0400 ++++ src/kernel/exit.c 2022-04-29 15:53:13.252548763 -0400 +@@ -725,6 +725,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -826,6 +827,8 @@ void __noreturn do_exit(long code) + exit_task_work(tsk); + exit_thread(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2022-04-29 15:52:13.577336397 -0400 ++++ src/kernel/fork.c 2022-04-29 15:53:13.252548763 -0400 +@@ -2540,6 +2540,7 @@ struct task_struct *create_io_thread(int + * + * args->exit_signal is expected to be checked for sanity by the caller. + */ ++#include + pid_t kernel_clone(struct kernel_clone_args *args) + { + u64 clone_flags = args->flags; +@@ -2548,6 +2549,8 @@ pid_t kernel_clone(struct kernel_clone_a + struct task_struct *p; + int trace = 0; + pid_t nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * For legacy clone() calls, CLONE_PIDFD uses the parent_tid argument +@@ -2587,6 +2590,11 @@ pid_t kernel_clone(struct kernel_clone_a + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-9.0/special-static.patch b/test/integration/rhel-9.0/special-static.patch new file mode 100644 index 0000000..79d333f --- /dev/null +++ b/test/integration/rhel-9.0/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2022-04-29 15:52:13.577336397 -0400 ++++ src/kernel/fork.c 2022-04-29 15:53:18.857568709 -0400 +@@ -1635,10 +1635,18 @@ static void posix_cpu_timers_init_group( + posix_cputimers_group_init(pct, cpu_limit); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-9.0/symvers-disagreement-FAIL.patch b/test/integration/rhel-9.0/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..0c91065 --- /dev/null +++ b/test/integration/rhel-9.0/symvers-disagreement-FAIL.patch @@ -0,0 +1,46 @@ +From 2d6b7bce089e52563bd9c67df62f48e90b48047d Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff -Nupr src.orig/drivers/base/core.c src/drivers/base/core.c +--- src.orig/drivers/base/core.c 2022-04-29 15:52:12.115331194 -0400 ++++ src/drivers/base/core.c 2022-04-29 15:53:21.690578791 -0400 +@@ -34,6 +34,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff -Nupr src.orig/drivers/usb/core/usb.c src/drivers/usb/core/usb.c +--- src.orig/drivers/usb/core/usb.c 2022-04-29 15:52:13.286335361 -0400 ++++ src/drivers/usb/core/usb.c 2022-04-29 15:53:21.690578791 -0400 +@@ -739,6 +739,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; diff --git a/test/integration/rhel-9.0/syscall-LOADED.test b/test/integration/rhel-9.0/syscall-LOADED.test new file mode 100755 index 0000000..3a2fd88 --- /dev/null +++ b/test/integration/rhel-9.0/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/rhel-9.0/syscall.patch b/test/integration/rhel-9.0/syscall.patch new file mode 100644 index 0000000..623f2d9 --- /dev/null +++ b/test/integration/rhel-9.0/syscall.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2022-04-29 15:56:57.808347857 -0400 ++++ src/kernel/sys.c 2022-04-29 15:56:58.373349868 -0400 +@@ -1268,13 +1268,15 @@ static int override_release(char __user + return ret; + } + +-SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) ++#include "kpatch-syscall.h" ++KPATCH_SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + { + struct new_utsname tmp; + + down_read(&uts_sem); + memcpy(&tmp, utsname(), sizeof(tmp)); + up_read(&uts_sem); ++ strcat(tmp.sysname, ".kpatch"); + if (copy_to_user(name, &tmp, sizeof(tmp))) + return -EFAULT; + diff --git a/test/integration/rhel-9.0/warn-detect-FAIL.patch b/test/integration/rhel-9.0/warn-detect-FAIL.patch new file mode 100644 index 0000000..dc5a470 --- /dev/null +++ b/test/integration/rhel-9.0/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2022-04-29 15:52:12.045330945 -0400 ++++ src/arch/x86/kvm/x86.c 2022-04-29 15:53:27.283598695 -0400 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/test-vagrant b/test/integration/test-vagrant new file mode 100755 index 0000000..22a93cd --- /dev/null +++ b/test/integration/test-vagrant @@ -0,0 +1,42 @@ +#!/bin/bash + +SCRIPTDIR=$(readlink -f "$(dirname "$(type -p "${0}")")") +ROOTDIR=$(readlink -f "${SCRIPTDIR}/../..") +SLOWTEST=0 + +# shellcheck disable=SC1090 +source "${ROOTDIR}/test/integration/lib.sh" + +usage() +{ + echo "usage: $(basename "${0}") [options]" >&2 + echo "-h, --help This message" >&2 + echo "-s, --slow Run all of the tests" >&2 +} + +options="$(getopt -o hs -l "help,slow" -- "$@")" || "getopt failed" + +eval set -- "${options}" + +while [[ $# -gt 0 ]]; do + case "$1" in + -s|--slow) + SLOWTEST=1 + ;; + -h|--help) + usage + exit 0 + ;; + esac + shift +done + +declare -a distros=("fedora27" "centos7") + +ret=0 +for distro in "${distros[@]}"; do + kpatch_integration_tests_vagrant_distro "${distro}" "${ROOTDIR}/test/integration/vm-integration-run" "${SLOWTEST}" + rc=$? + ret=$((ret + rc)) +done +exit ${ret} diff --git a/test/integration/ubuntu-16.04/README b/test/integration/ubuntu-16.04/README new file mode 100644 index 0000000..beab02d --- /dev/null +++ b/test/integration/ubuntu-16.04/README @@ -0,0 +1 @@ +4.4.0-53-generic diff --git a/test/integration/ubuntu-16.04/bug-table-section.patch b/test/integration/ubuntu-16.04/bug-table-section.patch new file mode 100644 index 0000000..097a99b --- /dev/null +++ b/test/integration/ubuntu-16.04/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/proc_sysctl.c 2016-12-15 19:56:00.204000000 +0000 +@@ -301,6 +301,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/ubuntu-16.04/cmdline-string-LOADED.test b/test/integration/ubuntu-16.04/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/ubuntu-16.04/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/ubuntu-16.04/cmdline-string.patch b/test/integration/ubuntu-16.04/cmdline-string.patch new file mode 100644 index 0000000..d8ca275 --- /dev/null +++ b/test/integration/ubuntu-16.04/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/cmdline.c 2016-12-15 19:56:12.848000000 +0000 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/ubuntu-16.04/data-new-LOADED.test b/test/integration/ubuntu-16.04/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/ubuntu-16.04/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/ubuntu-16.04/data-new.patch b/test/integration/ubuntu-16.04/data-new.patch new file mode 100644 index 0000000..1b107c1 --- /dev/null +++ b/test/integration/ubuntu-16.04/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/meminfo.c 2016-12-15 19:56:17.076000000 +0000 +@@ -23,6 +23,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -110,6 +112,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -169,6 +172,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/ubuntu-16.04/data-read-mostly.patch b/test/integration/ubuntu-16.04/data-read-mostly.patch new file mode 100644 index 0000000..d8b1ceb --- /dev/null +++ b/test/integration/ubuntu-16.04/data-read-mostly.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2016-12-15 19:55:39.848000000 +0000 ++++ src/net/core/dev.c 2016-12-15 19:56:21.344000000 +0000 +@@ -3926,6 +3926,7 @@ ncls: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/ubuntu-16.04/fixup-section.patch b/test/integration/ubuntu-16.04/fixup-section.patch new file mode 100644 index 0000000..c818b08 --- /dev/null +++ b/test/integration/ubuntu-16.04/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index ced679179cac..7fb338324582 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -173,6 +173,7 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/ubuntu-16.04/gcc-constprop.patch b/test/integration/ubuntu-16.04/gcc-constprop.patch new file mode 100644 index 0000000..fbdf2e9 --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-constprop.patch @@ -0,0 +1,16 @@ +ensure timekeeping_forward_now.constprop.8 and +timekeeping_forward_now.constprop.9 are correlated. + +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2016-12-15 19:56:00.136000000 +0000 ++++ src/kernel/time/timekeeping.c 2016-12-15 19:56:30.496000000 +0000 +@@ -1148,6 +1148,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/ubuntu-16.04/gcc-isra.patch b/test/integration/ubuntu-16.04/gcc-isra.patch new file mode 100644 index 0000000..979fb9b --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/proc_sysctl.c 2016-12-15 19:56:34.800000000 +0000 +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/ubuntu-16.04/gcc-mangled-3.patch.disabled b/test/integration/ubuntu-16.04/gcc-mangled-3.patch.disabled new file mode 100644 index 0000000..a72b19b --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-mangled-3.patch.disabled @@ -0,0 +1,19 @@ +ensure that __cmpxchg_double_slab.isra.45 and +__cmpxchg_double_slab.isra.45.part.46 aren't correlated. + +Disabled: __flush_cpu_slab() is present in vmlinux.symtab but is optimized +out during kpatch builds + +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2016-12-15 19:55:38.988000000 +0000 ++++ src/mm/slub.c 2016-12-15 19:56:39.068000000 +0000 +@@ -5531,6 +5531,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/ubuntu-16.04/gcc-static-local-var-2.patch b/test/integration/ubuntu-16.04/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a9ca12b --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2016-12-15 19:55:38.992000000 +0000 ++++ src/mm/mmap.c 2016-12-15 19:56:43.684000000 +0000 +@@ -1547,6 +1548,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/ubuntu-16.04/gcc-static-local-var-3.patch b/test/integration/ubuntu-16.04/gcc-static-local-var-3.patch new file mode 100644 index 0000000..270e3d3 --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/reboot.c src/kernel/reboot.c +--- src.orig/kernel/reboot.c 2016-12-15 19:56:00.196000000 +0000 ++++ src/kernel/reboot.c 2016-12-15 19:56:48.264000000 +0000 +@@ -366,8 +366,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/ubuntu-16.04/gcc-static-local-var-4.patch b/test/integration/ubuntu-16.04/gcc-static-local-var-4.patch new file mode 100644 index 0000000..48fa8c4 --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2016-12-15 19:55:38.992000000 +0000 ++++ src/fs/aio.c 2016-12-15 19:56:52.588000000 +0000 +@@ -271,9 +271,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/ubuntu-16.04/gcc-static-local-var-4.test b/test/integration/ubuntu-16.04/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/ubuntu-16.04/gcc-static-local-var-5.patch b/test/integration/ubuntu-16.04/gcc-static-local-var-5.patch new file mode 100644 index 0000000..2463454 --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2016-12-15 19:56:00.196000000 +0000 ++++ src/kernel/audit.c 2016-12-15 19:56:56.868000000 +0000 +@@ -213,6 +213,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -223,6 +229,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -242,6 +249,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -288,6 +300,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/ubuntu-16.04/gcc-static-local-var-6.patch b/test/integration/ubuntu-16.04/gcc-static-local-var-6.patch new file mode 100644 index 0000000..20aff19 --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index 39970e2..85e750d 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -108,6 +108,8 @@ static int nf_ip6_reroute(struct net *net, struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -121,6 +123,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/ubuntu-16.04/gcc-static-local-var.patch b/test/integration/ubuntu-16.04/gcc-static-local-var.patch new file mode 100644 index 0000000..fa1ee7c --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-static-local-var.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/arch/x86/kernel/ldt.c src/arch/x86/kernel/ldt.c +--- src.orig/arch/x86/kernel/ldt.c 2016-12-15 19:55:57.560000000 +0000 ++++ src/arch/x86/kernel/ldt.c 2016-12-15 19:57:01.124000000 +0000 +@@ -99,6 +99,12 @@ static void free_ldt_struct(struct ldt_s + kfree(ldt); + } + ++void hi_there(void) ++{ ++ if (!jiffies) ++ printk("hi there\n"); ++} ++ + /* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. +@@ -109,6 +115,8 @@ int init_new_context(struct task_struct + struct mm_struct *old_mm; + int retval = 0; + ++ hi_there(); ++ + mutex_init(&mm->context.lock); + old_mm = current->mm; + if (!old_mm) { diff --git a/test/integration/ubuntu-16.04/macro-callbacks.patch b/test/integration/ubuntu-16.04/macro-callbacks.patch new file mode 100644 index 0000000..569cc7c --- /dev/null +++ b/test/integration/ubuntu-16.04/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.old/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.old/drivers/input/joydev.c 2017-09-03 16:56:17.000000000 -0400 ++++ src/drivers/input/joydev.c 2018-03-22 16:32:40.963082354 -0400 +@@ -1010,3 +1010,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.old/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.old/drivers/input/misc/pcspkr.c 2018-03-22 16:29:27.716082354 -0400 ++++ src/drivers/input/misc/pcspkr.c 2018-03-22 16:32:40.963082354 -0400 +@@ -132,3 +132,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.old/fs/aio.c src/fs/aio.c +--- src.old/fs/aio.c 2017-09-03 16:56:17.000000000 -0400 ++++ src/fs/aio.c 2018-03-22 16:32:40.962082354 -0400 +@@ -46,6 +46,50 @@ + + #include "internal.h" + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/ubuntu-16.04/macro-printk.patch b/test/integration/ubuntu-16.04/macro-printk.patch new file mode 100644 index 0000000..e0eb7d4 --- /dev/null +++ b/test/integration/ubuntu-16.04/macro-printk.patch @@ -0,0 +1,148 @@ +Index: src/net/ipv4/fib_frontend.c +=================================================================== +--- src.orig/net/ipv4/fib_frontend.c ++++ src/net/ipv4/fib_frontend.c +@@ -728,6 +728,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -746,6 +747,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +Index: src/net/ipv4/fib_semantics.c +=================================================================== +--- src.orig/net/ipv4/fib_semantics.c ++++ src/net/ipv4/fib_semantics.c +@@ -998,6 +998,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -1025,6 +1026,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1045,6 +1047,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1059,6 +1062,7 @@ struct fib_info *fib_create_info(struct + } else { + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); + fib_info_cnt++; + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; +@@ -1075,6 +1079,7 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); + if (err) +@@ -1127,6 +1132,8 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1144,6 +1151,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1172,6 +1180,7 @@ struct fib_info *fib_create_info(struct + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) + goto err_inval; +@@ -1180,6 +1189,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1191,6 +1201,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1214,6 +1225,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1224,6 +1236,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +Index: src/net/ipv4/fib_trie.c +=================================================================== +--- src.orig/net/ipv4/fib_trie.c ++++ src/net/ipv4/fib_trie.c +@@ -1078,6 +1078,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) + { + struct trie *t = (struct trie *)tb->tb_data; +@@ -1101,11 +1102,14 @@ int fib_table_insert(struct fib_table *t + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled b/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled new file mode 100755 index 0000000..e2b647d --- /dev/null +++ b/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo && grep kpatch=1 /proc/cmdline diff --git a/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW.patch.disabled b/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW.patch.disabled new file mode 100644 index 0000000..664b9b3 --- /dev/null +++ b/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW.patch.disabled @@ -0,0 +1,42 @@ +Disabled: +kpatch-build currently fails with "invalid ancestor" error. This happens +with at least drivers/gpu/drm/i2c/adv7511.o and drivers/hwmon/htu21.o +files. The problem is their .ko counterparts are never built for some +reason, This looks like a kernel bug since in both cases there are files +with same name but in different paths that have .ko module built. +--- +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/cmdline.c 2016-12-15 19:57:13.988000000 +0000 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/meminfo.c 2016-12-15 19:57:13.988000000 +0000 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif +diff -Nupr src.orig/include/linux/kernel.h src/include/linux/kernel.h +--- src.orig/include/linux/kernel.h 2016-12-15 19:55:56.996000000 +0000 ++++ src/include/linux/kernel.h 2016-12-15 19:57:13.992000000 +0000 +@@ -2,6 +2,7 @@ + #define _LINUX_KERNEL_H + + ++ + #include + #include + #include diff --git a/test/integration/ubuntu-16.04/meminfo-init-FAIL.patch b/test/integration/ubuntu-16.04/meminfo-init-FAIL.patch new file mode 100644 index 0000000..c7a2cdd --- /dev/null +++ b/test/integration/ubuntu-16.04/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/meminfo.c 2016-12-15 19:57:22.564000000 +0000 +@@ -193,6 +193,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/ubuntu-16.04/meminfo-init2-FAIL.patch b/test/integration/ubuntu-16.04/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..8763fc0 --- /dev/null +++ b/test/integration/ubuntu-16.04/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/meminfo.c 2016-12-15 19:57:18.240000000 +0000 +@@ -32,6 +32,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -193,6 +194,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/ubuntu-16.04/meminfo-string-LOADED.test b/test/integration/ubuntu-16.04/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/ubuntu-16.04/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/ubuntu-16.04/meminfo-string.patch b/test/integration/ubuntu-16.04/meminfo-string.patch new file mode 100644 index 0000000..3dd2731 --- /dev/null +++ b/test/integration/ubuntu-16.04/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/meminfo.c 2016-12-15 19:57:26.828000000 +0000 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/ubuntu-16.04/module-call-external.patch.disable b/test/integration/ubuntu-16.04/module-call-external.patch.disable new file mode 100644 index 0000000..b9f51a7 --- /dev/null +++ b/test/integration/ubuntu-16.04/module-call-external.patch.disable @@ -0,0 +1,38 @@ +Disabled: +Original build includes "kzalloc" in af_netlink.c's symbol list, this +does not happen during kpatch build so create-diff-object fails with +find_local_syms. +--- +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2016-12-15 19:55:39.012000000 +0000 ++++ src/fs/nfsd/export.c 2016-12-15 19:57:31.068000000 +0000 +@@ -1183,6 +1183,8 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1192,6 +1194,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2016-12-15 19:55:39.772000000 +0000 ++++ src/net/netlink/af_netlink.c 2016-12-15 19:57:31.072000000 +0000 +@@ -3353,4 +3353,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/ubuntu-16.04/module-kvm-fixup.patch b/test/integration/ubuntu-16.04/module-kvm-fixup.patch new file mode 100644 index 0000000..d1130fd --- /dev/null +++ b/test/integration/ubuntu-16.04/module-kvm-fixup.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2016-12-15 19:55:57.436000000 +0000 ++++ src/arch/x86/kvm/vmx.c 2016-12-15 19:57:35.344000000 +0000 +@@ -10574,6 +10574,8 @@ static int vmx_check_intercept(struct kv + struct x86_instruction_info *info, + enum x86_intercept_stage stage) + { ++ if (!jiffies) ++ printk("kpatch vmx_check_intercept\n"); + return X86EMUL_CONTINUE; + } + diff --git a/test/integration/ubuntu-16.04/multiple.test b/test/integration/ubuntu-16.04/multiple.test new file mode 100755 index 0000000..70a3df3 --- /dev/null +++ b/test/integration/ubuntu-16.04/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(data-new-LOADED.test meminfo-cmdline-rebuild-SLOW-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/ubuntu-16.04/new-function.patch b/test/integration/ubuntu-16.04/new-function.patch new file mode 100644 index 0000000..1f36531 --- /dev/null +++ b/test/integration/ubuntu-16.04/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2016-12-15 19:55:54.840000000 +0000 ++++ src/drivers/tty/n_tty.c 2016-12-15 19:57:43.856000000 +0000 +@@ -2328,7 +2328,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2415,6 +2415,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/ubuntu-16.04/new-globals.patch b/test/integration/ubuntu-16.04/new-globals.patch new file mode 100644 index 0000000..0beef3e --- /dev/null +++ b/test/integration/ubuntu-16.04/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/cmdline.c 2016-12-15 19:57:48.084000000 +0000 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/meminfo.c 2016-12-15 19:57:48.084000000 +0000 +@@ -19,6 +19,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/ubuntu-16.04/parainstructions-section.patch b/test/integration/ubuntu-16.04/parainstructions-section.patch new file mode 100644 index 0000000..2fd39c3 --- /dev/null +++ b/test/integration/ubuntu-16.04/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2016-12-15 19:55:39.076000000 +0000 ++++ src/fs/proc/generic.c 2016-12-15 19:57:52.340000000 +0000 +@@ -195,6 +195,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/ubuntu-16.04/replace-section-references.patch b/test/integration/ubuntu-16.04/replace-section-references.patch new file mode 100644 index 0000000..7fc29db --- /dev/null +++ b/test/integration/ubuntu-16.04/replace-section-references.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2016-12-15 19:55:57.436000000 +0000 ++++ src/arch/x86/kvm/x86.c 2016-12-15 19:57:56.596000000 +0000 +@@ -230,6 +230,8 @@ static void shared_msr_update(unsigned s + + void kvm_define_shared_msr(unsigned slot, u32 msr) + { ++ if (!jiffies) ++ printk("kpatch kvm define shared msr\n"); + BUG_ON(slot >= KVM_NR_SHARED_MSRS); + shared_msrs_global.msrs[slot] = msr; + if (slot >= shared_msrs_global.nr) diff --git a/test/integration/ubuntu-16.04/smp-locks-section.patch b/test/integration/ubuntu-16.04/smp-locks-section.patch new file mode 100644 index 0000000..9d5a4e8 --- /dev/null +++ b/test/integration/ubuntu-16.04/smp-locks-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2016-12-15 19:55:54.840000000 +0000 ++++ src/drivers/tty/tty_buffer.c 2016-12-15 19:58:05.088000000 +0000 +@@ -255,6 +255,8 @@ static int __tty_buffer_request_room(str + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/ubuntu-16.04/special-static-2.patch b/test/integration/ubuntu-16.04/special-static-2.patch new file mode 100644 index 0000000..fef67f4 --- /dev/null +++ b/test/integration/ubuntu-16.04/special-static-2.patch @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2016-12-15 19:55:57.436000000 +0000 ++++ src/arch/x86/kvm/x86.c 2016-12-15 19:58:09.352000000 +0000 +@@ -2026,12 +2026,20 @@ static void record_steal_time(struct kvm + &vcpu->arch.st.steal, sizeof(struct kvm_steal_time)); + } + ++void kpatch_kvm_x86_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch kvm x86 foo\n"); ++} ++ + int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + { + bool pr = false; + u32 msr = msr_info->index; + u64 data = msr_info->data; + ++ kpatch_kvm_x86_foo(); ++ + switch (msr) { + case MSR_AMD64_NB_CFG: + case MSR_IA32_UCODE_REV: diff --git a/test/integration/ubuntu-16.04/special-static.patch b/test/integration/ubuntu-16.04/special-static.patch new file mode 100644 index 0000000..7d96d57 --- /dev/null +++ b/test/integration/ubuntu-16.04/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2016-12-15 19:56:00.184000000 +0000 ++++ src/kernel/fork.c 2016-12-15 19:58:13.588000000 +0000 +@@ -1143,10 +1143,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/ubuntu-16.04/tracepoints-section.patch b/test/integration/ubuntu-16.04/tracepoints-section.patch new file mode 100644 index 0000000..cea83e6 --- /dev/null +++ b/test/integration/ubuntu-16.04/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timer.c src/kernel/time/timer.c +--- src.orig/kernel/time/timer.c 2016-01-10 23:01:32.000000000 +0000 ++++ src/kernel/time/timer.c 2016-12-15 20:27:00.368000000 +0000 +@@ -1433,6 +1433,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = this_cpu_ptr(&tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/ubuntu-16.04/warn-detect-FAIL.patch b/test/integration/ubuntu-16.04/warn-detect-FAIL.patch new file mode 100644 index 0000000..a78ca9d --- /dev/null +++ b/test/integration/ubuntu-16.04/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2016-12-15 19:55:57.436000000 +0000 ++++ src/arch/x86/kvm/x86.c 2016-12-15 19:58:17.844000000 +0000 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/vm-integration-run b/test/integration/vm-integration-run new file mode 100755 index 0000000..d5cb560 --- /dev/null +++ b/test/integration/vm-integration-run @@ -0,0 +1,85 @@ +#!/bin/bash + +KPATCH_SLOW=0 +KPATCH_GIT=${KPATCH_GIT:-https://github.com/dynup/kpatch.git} +KPATCH_REV=${KPATCH_REV:-HEAD} +LOGDIR="/vagrant/logs" + +usage() +{ + echo "usage: $(basename "${0}") [options]" >&2 + echo "-h, --help This message" >&2 + echo "-s, --slow Run all of the tests" >&2 + echo "-g, --git Git url to clone from" >&2 + echo "-r, --revision Revision to use (HEAD by default)" >&2 +} + +options="$(getopt -o "shg:r:" -l "slow,help,git:,revision:" -- "$@")" || "getopt failed" + +eval set -- "${options}" + +while [[ $# -gt 0 ]]; do + case "$1" in + -s|--slow) + KPATCH_SLOW=1 + shift + ;; + -g|--git) + KPATCH_GIT="${2}" + shift 2 + ;; + -r|--revision) + KPATCH_REV="${2}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + break + ;; + esac +done + +git clone "${KPATCH_GIT}" || exit 1 + +cd kpatch || exit 1 + +git fetch origin +refs/pull/*:refs/pull/* +git reset --hard "${KPATCH_REV}" || exit 1 + +# shellcheck disable=SC1091 +source test/integration/lib.sh + +kpatch_dependencies +kpatch_separate_disk_cache /dev/vdb /mnt/build +kpatch_set_ccache_max_size 10G + +# Check if we have predownloaded sources and move them to ~/.kpatch dir which +# is a symlink to a dir on a separate (bigger) volume, suitable for building. +if [[ -d "${HOME}/src" && -f "${HOME}/src/version" ]]; then + cp "${HOME}/src/version" "${HOME}/.kpatch/" + mv "${HOME}/src" "${HOME}/.kpatch/" +fi + +# shellcheck disable=SC1091 +source /etc/os-release + +if [[ "${NAME}" == "Fedora" ]] && [[ "${VERSION_ID}" -lt 30 ]]; then + export BUILDMOD=yes +fi + +if [ ${KPATCH_SLOW} -eq 1 ]; then + make integration-slow 2>&1 +else + make integration-quick 2>&1 +fi + +rc=${PIPESTATUS[0]} +rm -rf "${LOGDIR}" +mkdir -p "${LOGDIR}" +cp ./test/integration/*.log "${LOGDIR}" + +exit "${rc}" diff --git a/test/test-functions.sh b/test/test-functions.sh new file mode 100644 index 0000000..96fa9a7 --- /dev/null +++ b/test/test-functions.sh @@ -0,0 +1,12 @@ +FILE=$1 + +assert_num_funcs() { + local num_funcs=$(nm $FILE | grep -i " t " | wc -l) + + if [[ $num_funcs != $1 ]]; then + echo "$FILE: assertion failed: file has $num_funcs funcs, expected $1" 1>&2 + exit 1 + fi + + return 0 +} diff --git a/test/unit/Makefile b/test/unit/Makefile new file mode 100644 index 0000000..fde1717 --- /dev/null +++ b/test/unit/Makefile @@ -0,0 +1,17 @@ +ARCHES ?= ppc64le x86_64 + +.PHONY: all clean submodule-check + +all: $(addsuffix -test,$(ARCHES)) +clean: $(addsuffix -clean,$(ARCHES)) + +submodule-check: + @cd $(shell git rev-parse --show-toplevel) && \ + git diff-index --quiet HEAD test/unit/objs || \ + echo -e "\nWARNING: unit tests are out of date - run \"git submodule update\"\n" + +%-test: Makefile.include submodule-check + $(MAKE) -C objs/$* + +%-clean: Makefile.include + if [ -d objs/$* ]; then $(MAKE) -C objs/$* clean; fi diff --git a/test/unit/Makefile.include b/test/unit/Makefile.include new file mode 100644 index 0000000..e971743 --- /dev/null +++ b/test/unit/Makefile.include @@ -0,0 +1,73 @@ +EXT_ORIG ?= ORIG.o +EXT_PATCHED ?= PATCHED.o +EXT_FAIL ?= PATCHED.FAIL.o +EXT_TEST ?= test +EXT_OUTPUT ?= OUTPUT.o +EXT_TEST_OUTPUT ?= test.out +EXT_SYMTAB ?= symtab +EXT_SYMVERS ?= symvers +EXT_ENV ?= env +TNAME = $(@:.$(EXT_OUTPUT)=) + +ifndef VERBOSE +MUTE_PASS := >/dev/null +MUTE_FAIL := >/dev/null 2>&1 +.SILENT: $(TARGETS) $(TEST_TARGETS) +endif + +SRC_PATH ?= $(realpath ../../../../) +CDO ?= $(SRC_PATH)/kpatch-build/create-diff-object +TEST_LIBRARY ?= $(SRC_PATH)/test/test-functions.sh + +TEST_ENV = KPATCH_TEST_LIBRARY=$(TEST_LIBRARY) + +TARGETS = $(patsubst %.$(EXT_ORIG),%.$(EXT_OUTPUT),$(wildcard *.$(EXT_ORIG))) +TEST_TARGETS = $(patsubst %.$(EXT_TEST),%.$(EXT_TEST_OUTPUT),$(wildcard *.$(EXT_TEST))) + +SYMVERS_FILE = $(if $(wildcard $(TNAME).$(EXT_SYMVERS)),$(TNAME).$(EXT_SYMVERS),/dev/null) + +define check_stripped = + $(if $(shell readelf --debug-dump $(1)), + $(error $(1) is not properly stripped, use 'strip --strip-debug --keep-file-symbols --remove-section=.eh_frame $(1)' to fix this), + ) +endef + +define check_all = + $(if $(findstring NOSTRIP,$(1)), , $(call check_stripped,$(1))) +endef + +.DELETE_ON_ERROR: %.$(EXT_OUTPUT) + +all: $(TARGETS) $(TEST_TARGETS) + +clean: + rm -f *.$(EXT_TEST_OUTPUT) *.$(EXT_OUTPUT) + +%.$(EXT_SYMTAB): + readelf -s --wide $(patsubst %.$(EXT_SYMTAB),%.$(EXT_ORIG),$(@)) | \ + sed -r 's/\s+\[: 8\]//' >$@ + +%.$(EXT_TEST_OUTPUT): %.$(EXT_OUTPUT) %.$(EXT_TEST) $(TEST_LIBRARY) + @echo "TEST $(@:.$(EXT_TEST_OUTPUT)=)" + $(TEST_ENV) bash $(@:.$(EXT_TEST_OUTPUT)=.$(EXT_TEST)) $< +# Don't rely on script creating this + @touch $@ + +%.$(EXT_OUTPUT): %.$(EXT_ORIG) %.$(EXT_PATCHED) %.$(EXT_SYMTAB) $(CDO) + @echo "BUILD $(TNAME)" + $(call check_all,$(TNAME).$(EXT_ORIG)) + $(call check_all,$(TNAME).$(EXT_PATCHED)) + $(CDO_ENV) $(shell cat $(TNAME).$(EXT_ENV) 2>/dev/null) $(CDO) $(TNAME).$(EXT_ORIG) $(TNAME).$(EXT_PATCHED) \ + vmlinux $(TNAME).$(EXT_SYMTAB) $(SYMVERS_FILE) \ + test_$(TNAME) $@ $(MUTE_PASS) + +%.$(EXT_OUTPUT): %.$(EXT_ORIG) %.$(EXT_FAIL) %.$(EXT_SYMTAB) $(CDO) + @echo "BUILD $(TNAME)-FAIL" + $(call check_all,$(TNAME).$(EXT_ORIG)) + $(call check_all,$(TNAME).$(EXT_FAIL)) + ! $(CDO_ENV) $(shell cat $(TNAME).$(EXT_ENV) 2>/dev/null) $(CDO) $(TNAME).$(EXT_ORIG) $(TNAME).$(EXT_FAIL) \ + vmlinux $(TNAME).$(EXT_SYMTAB) $(SYMVERS_FILE) \ + test_$(TNAME) $@ $(MUTE_FAIL) +# Expecting to fail, thus create output file manually so we won't rerun the +# test without clean + @touch $@ -- Gitee