From bb57564e6096c16008b5de1921489694d6abd8e1 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 16 Jan 2023 09:33:55 -0500 Subject: [PATCH 01/10] kpatch-build: paravirt_patch_site is x86-only Architectures like ppc64le may set CONFIG_PARAVIRT=y but do not necessarily implement via struct paravirt_patch_site. Signed-off-by: Joe Lawrence --- kpatch-build/kpatch-build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 45e8b14bf..36ab25a92 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -342,11 +342,12 @@ find_special_section_data() { check[b]=true # bug_entry check[e]=true # exception_table_entry - # Arch-specific features, without kernel CONFIG_ toggle + # Arch-specific features case "$ARCH" in "x86_64") check[a]=true # alt_instr kernel_version_gte 5.10.0 && check[s]=true # static_call_site + [[ -n "$CONFIG_PARAVIRT" ]] && check[p]=true # paravirt_patch_site ;; "ppc64le") check[f]=true # fixup_entry @@ -360,7 +361,6 @@ find_special_section_data() { [[ -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 From 934b3b3c0c12be2435f8edbe484c2696dda93864 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 16 Jan 2023 09:33:55 -0500 Subject: [PATCH 02/10] kpatch-build: ignore init/version-timestamp.o Kernel v6.1+ commit 2df8220cc511 ("kbuild: build init/built-in.a just once") split init_uts_ns and linux_banner out to init/version-timestamp.c from init/version.c Add init/version-timestamp.o to the list of object files that kpatch-cc won't add to its changed_objs list. Signed-off-by: Joe Lawrence --- kpatch-build/kpatch-cc | 1 + 1 file changed, 1 insertion(+) diff --git a/kpatch-build/kpatch-cc b/kpatch-build/kpatch-cc index 2a3d26449..17aae25b6 100755 --- a/kpatch-build/kpatch-cc +++ b/kpatch-build/kpatch-cc @@ -44,6 +44,7 @@ if [[ "$TOOLCHAINCMD" =~ ^(.*-)?gcc$ || "$TOOLCHAINCMD" =~ ^(.*-)?clang$ ]] ; th arch/s390/kernel/vdso64/*|\ drivers/firmware/efi/libstub/*|\ init/version.o|\ + init/version-timestamp.o|\ kernel/system_certificates.o|\ lib/*|\ tools/*|\ From e707515cb15e3f05b1837c0e57bd820d0c657fcc Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 16 Jan 2023 09:33:55 -0500 Subject: [PATCH 03/10] macros: add v6.1 powerpc syscall macros Kernel v6.1+ commit 7e92e01b7245 ("powerpc: Provide syscall wrapper") introduced PowerPC specific macros. Add them to kpatch-syscall.h. WIP: the syscall.patch integration test required -fno-optimize-sibling-calls for sys_newuname() ... should we move the attribute out to the patch? Signed-off-by: Joe Lawrence --- kmod/patch/kpatch-syscall.h | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/kmod/patch/kpatch-syscall.h b/kmod/patch/kpatch-syscall.h index e69f3c109..fdb741049 100644 --- a/kmod/patch/kpatch-syscall.h +++ b/kmod/patch/kpatch-syscall.h @@ -38,7 +38,7 @@ #ifdef CONFIG_X86_64 -/* x86/include/asm/syscall_wrapper.h versions */ +/* arch/x86/include/asm/syscall_wrapper.h versions */ # if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) @@ -81,7 +81,7 @@ #elif defined(CONFIG_S390) -/* s390/include/asm/syscall_wrapper.h versions */ +/* arch/s390/include/asm/syscall_wrapper.h versions */ #define __KPATCH_S390_SYS_STUBx(x, name, ...) \ long __s390_sys##name(struct pt_regs *regs); \ @@ -114,6 +114,33 @@ __diag_pop(); \ static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) +#elif defined(CONFIG_PPC64) + +/* arch/powerpc/include/asm/syscall_wrapper.h versions */ + +# if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + +# define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ + long sys##name(const struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(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__));\ + __attribute__((optimize("-fno-optimize-sibling-calls"))) \ + long sys##name(const struct pt_regs *regs) \ + { \ + return __se_sys##name(SC_POWERPC_REGS_TO_ARGS(x,__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 */ + #endif /* CONFIG_X86_64 */ From 6b78bd5c6723170b0affde59fb715d3216b8d502 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 16 Jan 2023 09:33:55 -0500 Subject: [PATCH 04/10] create-diff-object: support .call_sites sections Kernel v6.2+ commits 00abd3840812 ("objtool: Add .call_sites section") and e81dc127ef69 ("x86/callthunks: Add call patching for call depth tracking") added .call_sites sections to object files. These are filled with an array of s32 values. Signed-off-by: Joe Lawrence --- kpatch-build/create-diff-object.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 707b0a908..6dd69f92c 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -2124,6 +2124,11 @@ static int static_call_sites_group_size(struct kpatch_elf *kelf, int offset) return size; } +static int call_sites_group_size(struct kpatch_elf *kelf, int offset) +{ + return 4; +} + static int retpoline_sites_group_size(struct kpatch_elf *kelf, int offset) { return 4; @@ -2431,6 +2436,11 @@ static struct special_section special_sections[] = { .group_size = static_call_sites_group_size, .group_filter = static_call_sites_group_filter, }, + { + .name = ".call_sites", + .arch = X86_64, + .group_size = call_sites_group_size, + }, { .name = ".retpoline_sites", .arch = X86_64, From 7d22fefa3558d886febaa0e7b29b8ccb08d3f8c7 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Fri, 10 Feb 2023 13:38:05 -0500 Subject: [PATCH 05/10] create-diff-object: ignore __patchable_function_entries Kernel v6.2+ commit bea75b33895f ("x86/Kconfig: Introduce function padding") introduces the -fpatchable-function-entry=16,16 build flag on x86. This leverages compiler support for generating a __patchable_function_entries section similar to __mcount_loc. That said, x86 still utilizes __mcount_loc even when __patchable_function_entries exists. The latter point to the __pfx symbols, but the section is discarded in the vmlinux link and isn't used regardless, for ftrace or for any other purpose. Signed-off-by: Joe Lawrence --- kpatch-build/create-diff-object.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 6dd69f92c..0754f7506 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -2835,6 +2835,12 @@ static void kpatch_mark_ignored_sections(struct kpatch_elf *kelf) !strncmp(sec->name, ".llvm_addrsig", 13) || !strncmp(sec->name, ".llvm.", 6)) sec->ignore = 1; + + if (kelf->arch == X86_64) { + if (!strcmp(sec->name, ".rela__patchable_function_entries") || + !strcmp(sec->name, "__patchable_function_entries")) + sec->ignore = 1; + } } sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.sections"); From 2ddeccccb22dbcdc200ad5515f5776acb0523d01 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 16 Jan 2023 09:33:55 -0500 Subject: [PATCH 06/10] kpatch-elf: copy ELF e_flags from patched .o Kernel v6.2+ commit de3d098dd1fc ("powerpc/64: Add module check for ELF ABI version") now verifies the ELF header e_flags for its ABI value and refuse to load modules with complaint, "Invalid module architecture in ELF header: 21" Like other ELF header details, copy the flags from the reference ELF input file to the new ELF file to avoid module load errors. Signed-off-by: Joe Lawrence --- kpatch-build/kpatch-elf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index c7d12ec93..213ff5f3b 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -948,6 +948,7 @@ void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile, ehout.e_machine = eh.e_machine; ehout.e_type = eh.e_type; ehout.e_version = EV_CURRENT; + ehout.e_flags = eh.e_flags; shstrtab = find_section_by_name(&kelf->sections, ".shstrtab"); if (!shstrtab) From 0d48a4357e524f2499dc6acc9538b46ccf065027 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 16 Jan 2023 09:33:55 -0500 Subject: [PATCH 07/10] test/integration: add upstream 6.2.0 patches Add patches rebased on top of upstream 6.2.0. Integration tests for these can be ran as this: $ make PATCH_DIR="linux-6.2.0" KPATCH_BUILD_OPTS="--non-replace --sourcedir /path/to/src/linux-6.2.0" integration-slow Signed-off-by: Joe Lawrence --- .../linux-6.2.0/data-new-LOADED.test | 3 + test/integration/linux-6.2.0/data-new.patch | 20 +++ .../linux-6.2.0/gcc-static-local-var-6.patch | 22 +++ .../linux-6.2.0/macro-callbacks.patch | 155 ++++++++++++++++++ .../linux-6.2.0/module-LOADED.test | 13 ++ test/integration/linux-6.2.0/module.patch | 79 +++++++++ test/integration/linux-6.2.0/multiple.test | 7 + .../linux-6.2.0/new-function.patch | 25 +++ .../integration/linux-6.2.0/new-globals.patch | 34 ++++ .../linux-6.2.0/shadow-newpid-LOADED.test | 3 + .../linux-6.2.0/shadow-newpid.patch | 75 +++++++++ .../linux-6.2.0/special-static.patch | 22 +++ .../symvers-disagreement-FAIL.patch | 46 ++++++ .../linux-6.2.0/syscall-LOADED.test | 3 + test/integration/linux-6.2.0/syscall.patch | 20 +++ .../linux-6.2.0/warn-detect-FAIL.patch | 9 + 16 files changed, 536 insertions(+) create mode 100755 test/integration/linux-6.2.0/data-new-LOADED.test create mode 100644 test/integration/linux-6.2.0/data-new.patch create mode 100644 test/integration/linux-6.2.0/gcc-static-local-var-6.patch create mode 100644 test/integration/linux-6.2.0/macro-callbacks.patch create mode 100755 test/integration/linux-6.2.0/module-LOADED.test create mode 100644 test/integration/linux-6.2.0/module.patch create mode 100755 test/integration/linux-6.2.0/multiple.test create mode 100644 test/integration/linux-6.2.0/new-function.patch create mode 100644 test/integration/linux-6.2.0/new-globals.patch create mode 100755 test/integration/linux-6.2.0/shadow-newpid-LOADED.test create mode 100644 test/integration/linux-6.2.0/shadow-newpid.patch create mode 100644 test/integration/linux-6.2.0/special-static.patch create mode 100644 test/integration/linux-6.2.0/symvers-disagreement-FAIL.patch create mode 100755 test/integration/linux-6.2.0/syscall-LOADED.test create mode 100644 test/integration/linux-6.2.0/syscall.patch create mode 100644 test/integration/linux-6.2.0/warn-detect-FAIL.patch diff --git a/test/integration/linux-6.2.0/data-new-LOADED.test b/test/integration/linux-6.2.0/data-new-LOADED.test new file mode 100755 index 000000000..9f25744e6 --- /dev/null +++ b/test/integration/linux-6.2.0/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/linux-6.2.0/data-new.patch b/test/integration/linux-6.2.0/data-new.patch new file mode 100644 index 000000000..35cc6a21b --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:07.184710563 -0500 ++++ src/fs/proc/meminfo.c 2023-01-12 11:20:08.166716386 -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; +@@ -154,6 +156,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-6.2.0/gcc-static-local-var-6.patch b/test/integration/linux-6.2.0/gcc-static-local-var-6.patch new file mode 100644 index 000000000..efb63adc6 --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:07.630713207 -0500 ++++ src/net/ipv6/netfilter.c 2023-01-12 11:20:29.874845095 -0500 +@@ -96,6 +96,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) + { +@@ -109,6 +111,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-6.2.0/macro-callbacks.patch b/test/integration/linux-6.2.0/macro-callbacks.patch new file mode 100644 index 000000000..1e0b42455 --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:06.597707083 -0500 ++++ src/drivers/input/joydev.c 2023-01-12 11:20:38.032893465 -0500 +@@ -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 2023-01-12 11:20:06.578706970 -0500 ++++ src/drivers/input/misc/pcspkr.c 2023-01-12 11:20:38.032893465 -0500 +@@ -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 2023-01-12 11:20:07.273711091 -0500 ++++ src/fs/aio.c 2023-01-12 11:20:38.033893471 -0500 +@@ -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-6.2.0/module-LOADED.test b/test/integration/linux-6.2.0/module-LOADED.test new file mode 100755 index 000000000..72bb85266 --- /dev/null +++ b/test/integration/linux-6.2.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-6.2.0/module.patch b/test/integration/linux-6.2.0/module.patch new file mode 100644 index 000000000..5f87a137e --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:07.174710504 -0500 ++++ src/fs/nfsd/export.c 2023-01-12 11:20:46.075941153 -0500 +@@ -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 2023-01-12 11:20:07.603713047 -0500 ++++ src/net/netlink/af_netlink.c 2023-01-12 11:20:46.076941159 -0500 +@@ -2970,4 +2970,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-6.2.0/multiple.test b/test/integration/linux-6.2.0/multiple.test new file mode 100755 index 000000000..7e4b3525c --- /dev/null +++ b/test/integration/linux-6.2.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-6.2.0/new-function.patch b/test/integration/linux-6.2.0/new-function.patch new file mode 100644 index 000000000..86cc50b61 --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:06.978709342 -0500 ++++ src/drivers/tty/n_tty.c 2023-01-12 11:20:54.149989024 -0500 +@@ -2315,7 +2315,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; +@@ -2402,6 +2402,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-6.2.0/new-globals.patch b/test/integration/linux-6.2.0/new-globals.patch new file mode 100644 index 000000000..acb84ab6e --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:07.184710563 -0500 ++++ src/fs/proc/cmdline.c 2023-01-12 11:21:02.411038005 -0500 +@@ -21,3 +21,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 2023-01-12 11:20:07.184710563 -0500 ++++ src/fs/proc/meminfo.c 2023-01-12 11:21:02.411038005 -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-6.2.0/shadow-newpid-LOADED.test b/test/integration/linux-6.2.0/shadow-newpid-LOADED.test new file mode 100755 index 000000000..c07d11205 --- /dev/null +++ b/test/integration/linux-6.2.0/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/linux-6.2.0/shadow-newpid.patch b/test/integration/linux-6.2.0/shadow-newpid.patch new file mode 100644 index 000000000..f4afda77b --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:07.182710551 -0500 ++++ src/fs/proc/array.c 2023-01-12 11:21:10.821087869 -0500 +@@ -397,12 +397,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 2023-01-12 11:20:05.408700033 -0500 ++++ src/kernel/exit.c 2023-01-12 11:21:10.822087875 -0500 +@@ -802,6 +802,7 @@ static void synchronize_group_exit(struc + spin_unlock_irq(&sighand->siglock); + } + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -867,6 +868,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 2023-01-12 11:20:05.408700033 -0500 ++++ src/kernel/fork.c 2023-01-12 11:21:10.824087886 -0500 +@@ -2637,6 +2637,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; +@@ -2645,6 +2646,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 +@@ -2684,6 +2687,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-6.2.0/special-static.patch b/test/integration/linux-6.2.0/special-static.patch new file mode 100644 index 000000000..170049e2d --- /dev/null +++ b/test/integration/linux-6.2.0/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2023-01-12 11:20:05.408700033 -0500 ++++ src/kernel/fork.c 2023-01-12 11:21:19.186137466 -0500 +@@ -1700,10 +1700,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-6.2.0/symvers-disagreement-FAIL.patch b/test/integration/linux-6.2.0/symvers-disagreement-FAIL.patch new file mode 100644 index 000000000..2ff2047db --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:06.403705933 -0500 ++++ src/drivers/base/core.c 2023-01-12 11:21:27.466186559 -0500 +@@ -36,6 +36,8 @@ + #include "physical_location.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 2023-01-12 11:20:06.490706448 -0500 ++++ src/drivers/usb/core/usb.c 2023-01-12 11:21:27.468186571 -0500 +@@ -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-6.2.0/syscall-LOADED.test b/test/integration/linux-6.2.0/syscall-LOADED.test new file mode 100755 index 000000000..3a2fd88e0 --- /dev/null +++ b/test/integration/linux-6.2.0/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/linux-6.2.0/syscall.patch b/test/integration/linux-6.2.0/syscall.patch new file mode 100644 index 000000000..3ea4b0aaa --- /dev/null +++ b/test/integration/linux-6.2.0/syscall.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2023-01-12 11:20:05.406700021 -0500 ++++ src/kernel/sys.c 2023-01-12 11:21:35.782235867 -0500 +@@ -1285,13 +1285,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-6.2.0/warn-detect-FAIL.patch b/test/integration/linux-6.2.0/warn-detect-FAIL.patch new file mode 100644 index 000000000..f309d7c35 --- /dev/null +++ b/test/integration/linux-6.2.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 2023-01-12 11:20:04.716695930 -0500 ++++ src/arch/x86/kvm/x86.c 2023-01-12 11:21:44.162285556 -0500 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * From 3e54c63b175b68cf48654c119e62bda398d0c018 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 16 Jan 2023 09:33:55 -0500 Subject: [PATCH 08/10] create-diff-object: support x86 NOP-padded functions Kernel v6.2+ commit bea75b33895f ("x86/Kconfig: Introduce function padding") added 16 bytes of NOP padding in front of each function. For objects built with --function-sections, this means that function symbols no longer sit at the beginning of their respective ELF sections, but 16 bytes offset. In the same release, kernel v6.2+ commit 9f2899fe36a6 ("objtool: Add option to generate prefix symbols") adds ELF function symbols with prefix "__pfx_" to indicate the start of a function, inclusive of NOP-padding. For example: $ objdump -Dr -j.text.cmdline_proc_show fs/proc/cmdline.o ... Disassembly of section .text.cmdline_proc_show: 0000000000000000 <__pfx_cmdline_proc_show>: 0: 90 nop 1: 90 nop 2: 90 nop 3: 90 nop 4: 90 nop 5: 90 nop 6: 90 nop 7: 90 nop 8: 90 nop 9: 90 nop a: 90 nop b: 90 nop c: 90 nop d: 90 nop e: 90 nop f: 90 nop 0000000000000010 : 10: e8 00 00 00 00 callq 15 11: R_X86_64_PLT32 __fentry__-0x4 15: 55 push %rbp 16: 48 8b 35 00 00 00 00 mov 0x0(%rip),%rsi # 1d 19: R_X86_64_PC32 saved_command_line-0x4 1d: 48 89 fd mov %rdi,%rbp 20: e8 00 00 00 00 callq 25 21: R_X86_64_PLT32 seq_puts-0x4 25: 48 89 ef mov %rbp,%rdi 28: be 0a 00 00 00 mov $0xa,%esi 2d: e8 00 00 00 00 callq 32 2e: R_X86_64_PLT32 seq_putc-0x4 32: 31 c0 xor %eax,%eax 34: 5d pop %rbp 35: e9 00 00 00 00 jmpq 3a 36: R_X86_64_PLT32 __x86_return_thunk-0x4 Kpatch-build needs to gracefully handle NOP-padding when it is present. At the same time, it should include "__pfx_" symbols when their respective functions change, but not treat prefix such functions as first-class functions. This also adds support for CONFIG_CFI_CLANG, which also creates prefixed symbols with the name "__cfi_". Signed-off-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- kpatch-build/create-diff-object.c | 23 ++++++-- kpatch-build/kpatch-elf.c | 92 +++++++++++++++++++++++++++++++ kpatch-build/kpatch-elf.h | 2 + 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 0754f7506..76ba867f9 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -236,14 +236,21 @@ static struct rela *toc_rela(const struct rela *rela) static void kpatch_bundle_symbols(struct kpatch_elf *kelf) { struct symbol *sym; + unsigned int expected_offset; 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", + if (sym->pfx) + expected_offset = 16; + else if (is_gcc6_localentry_bundled_sym(kelf, sym)) + expected_offset = 8; + else + expected_offset = 0; + + if (sym->sym.st_value != expected_offset) { + ERROR("symbol %s at offset %lu within section %s, expected %u", sym->name, sym->sym.st_value, - sym->sec->name); + sym->sec->name, expected_offset); } sym->sec->sym = sym; @@ -1913,6 +1920,8 @@ static int kpatch_include_changed_functions(struct kpatch_elf *kelf) sym->type == STT_FUNC) { changed_nr++; kpatch_include_symbol(sym); + if (sym->pfx) + kpatch_include_symbol(sym->pfx); } if (sym->type == STT_FILE) @@ -1927,7 +1936,8 @@ 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) + if (!sym->include || !sym->sec || sym->type != STT_FUNC || + sym->parent || sym->is_pfx) continue; if (sym->status == NEW) log_normal("new function: %s\n", sym->name); @@ -3922,7 +3932,8 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) struct rela *rela; unsigned char *insn; list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->type != STT_FUNC || !sym->sec || !sym->sec->rela) + if (sym->type != STT_FUNC || sym->is_pfx || + !sym->sec || !sym->sec->rela) continue; switch(kelf->arch) { diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 213ff5f3b..4fb985c19 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -397,6 +397,97 @@ static void kpatch_create_section_list(struct kpatch_elf *kelf) ERROR("expected NULL"); } +/* + * Some x86 kernels have NOP function padding [1] for which objtool [2] + * adds ELF function symbols with prefix "__pfx_" to indicate the start + * of a function, inclusive of NOP-padding. Find the prefix symbols and + * link them to their corresponding function symbols at an expected + * offset. + * + * A few examples: + * + * Value Size Type Bind Vis Ndx Name + * (fork.o, simple case) + * 0000000000000000 0 SECTION LOCAL DEFAULT 31 .text.get_task_mm + * 0000000000000000 16 FUNC GLOBAL DEFAULT 31 __pfx_get_task_mm + * 0000000000000010 91 FUNC GLOBAL DEFAULT 31 get_task_mm + * + * (fork.o, multiple function aliases) + * 0000000000000000 0 SECTION LOCAL DEFAULT 190 .text.__do_sys_fork + * 0000000000000000 16 FUNC GLOBAL DEFAULT 190 __pfx___x64_sys_fork + * 0000000000000010 49 FUNC LOCAL DEFAULT 190 __do_sys_fork + * 0000000000000010 49 FUNC GLOBAL DEFAULT 190 __ia32_sys_fork + * 0000000000000010 49 FUNC GLOBAL DEFAULT 190 __x64_sys_fork + * + * (fork.o multiple functions in one section) + * 0000000000000000 0 SECTION LOCAL DEFAULT 59 .init.text + * 0000000000000000 16 FUNC LOCAL DEFAULT 59 __pfx_coredump_filter_setup + * 0000000000000010 40 FUNC LOCAL DEFAULT 59 coredump_filter_setup + * 0000000000000038 16 FUNC WEAK DEFAULT 59 __pfx_arch_task_cache_init + * 0000000000000048 10 FUNC WEAK DEFAULT 59 arch_task_cache_init + * 0000000000000052 16 FUNC GLOBAL DEFAULT 59 __pfx_fork_init + * 0000000000000062 357 FUNC GLOBAL DEFAULT 59 fork_init + * 00000000000001c7 16 FUNC GLOBAL DEFAULT 59 __pfx_fork_idle + * 00000000000001d7 214 FUNC GLOBAL DEFAULT 59 fork_idle + * 00000000000002ad 16 FUNC GLOBAL DEFAULT 59 __pfx_mm_cache_init + * 00000000000002bd 72 FUNC GLOBAL DEFAULT 59 mm_cache_init + * 0000000000000305 16 FUNC GLOBAL DEFAULT 59 __pfx_proc_caches_init + * 0000000000000315 192 FUNC GLOBAL DEFAULT 59 proc_caches_init + * + * (fork.o, function without nop padding / __pfx_ symbol) + * 0000000000000000 0 SECTION LOCAL DEFAULT 99 .text.unlikely.__mmdrop + * 0000000000000000 48 FUNC LOCAL DEFAULT 99 __mmdrop.cold + * + * (kpatch-build generated tmp.ko, multple functions in one section, no __pfx_ symbols) + * 0000000000000000 0 SECTION LOCAL DEFAULT 10 .text.unlikely.callback_info.isra.0 + * 0000000000000010 65 FUNC LOCAL DEFAULT 10 callback_info.isra.0 + * 0000000000000061 54 FUNC LOCAL DEFAULT 10 callback_info.isra.0 + * 00000000000000a7 54 FUNC LOCAL DEFAULT 10 callback_info.isra.0 + * + * CONFIG_CFI_CLANG uses something very similar, except the symbol is created + * by the compiler and its prefix is "__cfi_". + * + * [1] bea75b33895f ("x86/Kconfig: Introduce function padding") + * [2] 9f2899fe36a6 ("objtool: Add option to generate prefix symbols") + */ +static void kpatch_link_prefixed_functions(struct kpatch_elf *kelf) +{ + struct symbol *func, *pfx; + bool found; + + if (kelf->arch != X86_64) + return; + + list_for_each_entry(pfx, &kelf->symbols, list) { + if (!pfx->name || pfx->type != STT_FUNC) + continue; + + if (strncmp(pfx->name, "__pfx_", 6) && + strncmp(pfx->name, "__cfi_", 6)) + continue; + + found = false; + + list_for_each_entry(func, &kelf->symbols, list) { + if (func->type == STT_FUNC && func->sec == pfx->sec && + func->sym.st_value == pfx->sym.st_value + 16) { + + /* + * If a func has aliases, it's possible for + * multiple functions to have the same 'pfx'. + */ + + pfx->is_pfx = true; + func->pfx = pfx; + found = true; + } + } + + if (!found) + ERROR("missing func for %s", pfx->name); + } +} + static void kpatch_create_symbol_list(struct kpatch_elf *kelf) { struct section *symtab; @@ -459,6 +550,7 @@ static void kpatch_create_symbol_list(struct kpatch_elf *kelf) log_debug("\n"); } + kpatch_link_prefixed_functions(kelf); } struct kpatch_elf *kpatch_elf_open(const char *name) diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index cd2900cf4..956c1c2ce 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -77,6 +77,7 @@ struct symbol { struct list_head list; struct symbol *twin; struct symbol *parent; + struct symbol *pfx; struct list_head children; struct list_head subfunction_node; struct section *sec; @@ -91,6 +92,7 @@ struct symbol { enum symbol_strip strip; /* used in the output elf */ }; int has_func_profiling; + bool is_pfx; }; struct rela { From 4df1528cd91211fbb0d2bccc5461323e1bd485ed Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 14 Mar 2023 16:21:10 -0400 Subject: [PATCH 09/10] gcc-plugin: update gcc-common.h for gcc-13 Backport this kernel commit to kpatch's version of gcc-common.h: commit e6a71160cc145e18ab45195abf89884112e02dfb Author: Kees Cook Date: Wed Jan 18 12:21:35 2023 -0800 gcc-plugins: Reorganize gimple includes for GCC 13 The gimple-iterator.h header must be included before gimple-fold.h starting with GCC 13. Reorganize gimple headers to work for all GCC versions. Reported-by: Palmer Dabbelt Acked-by: Palmer Dabbelt Link: https://lore.kernel.org/all/20230113173033.4380-1-palmer@rivosinc.com/ Cc: linux-hardening@vger.kernel.org Signed-off-by: Kees Cook Note that we can't directly copy the gcc-plugin headers from the kernel as has it has dropped gcc-4.9 and older support since v5.16. Distros like rhel-7 still use gcc-4.8, so manually cherry pick changes into the kpatch tree. Signed-off-by: Joe Lawrence --- kpatch-build/gcc-plugins/gcc-common.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kpatch-build/gcc-plugins/gcc-common.h b/kpatch-build/gcc-plugins/gcc-common.h index 443faf687..e2bff29df 100644 --- a/kpatch-build/gcc-plugins/gcc-common.h +++ b/kpatch-build/gcc-plugins/gcc-common.h @@ -107,7 +107,9 @@ #include "varasm.h" #include "stor-layout.h" #include "internal-fn.h" +#include "gimple.h" #include "gimple-expr.h" +#include "gimple-iterator.h" #include "gimple-fold.h" #include "context.h" #include "tree-ssa-alias.h" @@ -129,7 +131,6 @@ #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 From f499fc25e03024e3a3b7a25579f1b6f5709b2cc2 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 22 Mar 2023 17:38:29 -0700 Subject: [PATCH 10/10] test/integration: Updated module.patch for Linux 6.2 Yulia reported a bug with module.patch which can be seen with CONFIG_PROVE_RCU: BUG: sleeping function called from invalid context at include/linux/percpu-rwsem.h:49 in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 1053, name: grep preempt_count: 0, expected: 0 RCU nest depth: 1, expected: 0 2 locks held by grep/1053: #0: ffff8881079bf1a0 (&p->lock){+.+.}-{3:3}, at: seq_read_iter+0x55/0x460 #1: ffffffff8275d620 (rcu_read_lock){....}-{1:2}, at: cache_seq_start_rcu+0x5/0x140 [sunrpc] CPU: 3 PID: 1053 Comm: grep Tainted: G OE K 6.2.0 #57 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.1-2.fc37 04/01/2014 Call Trace: dump_stack_lvl+0x5b/0x77 __might_resched.cold+0xff/0x13a cpus_read_lock+0x16/0xd0 static_key_disable+0xe/0x20 e_show+0x5b/0xd70 [livepatch_module] seq_read_iter+0x127/0x460 seq_read+0xa3/0xd0 proc_reg_read+0x52/0xa0 vfs_read+0xc9/0x2f0 ? __do_sys_newfstat+0x57/0x60 ? lock_is_held_type+0xe8/0x140 ksys_read+0x6c/0xf0 do_syscall_64+0x37/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc RIP: 0033:0x7fbd314f7af0 Code: b6 fe ff ff 48 8d 3d 2f 78 09 00 48 83 ec 08 e8 96 25 02 00 66 0f 1f 44 00 00 83 3d d9 db 2c 00 00 75 10 b8 00 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 31 c3 48 83 ec 08 e8 7e e3 01 00 48 89 04 24 RSP: 002b:00007ffcd6f1c708 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 RAX: ffffffffffffffda RBX: 0000000000008000 RCX: 00007fbd314f7af0 RDX: 0000000000008000 RSI: 0000563c751ea000 RDI: 0000000000000003 RBP: 0000000000008000 R08: 0000000000009008 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000563c751ea000 R13: 0000000000000003 R14: 0000000000000003 R15: 0000563c751ea000 The problem is that the patched module's seq_operations take an RCU read lock before calling e_show(), which is patched to call static_key_disable(), which can sleep. Fix the issue by moving all the module test code to a different module (xfs) which doesn't take an RCU lock. Also enable the pr_debug() test and make the static branch test unconditional since they're supported by newer kernels and this test is for 6.2. Reported-by: Yulia Kopkova Debugged-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- .../linux-6.2.0/module-LOADED.test | 12 ++-- test/integration/linux-6.2.0/module.patch | 60 +++++++++---------- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/test/integration/linux-6.2.0/module-LOADED.test b/test/integration/linux-6.2.0/module-LOADED.test index 72bb85266..1694a2919 100755 --- a/test/integration/linux-6.2.0/module-LOADED.test +++ b/test/integration/linux-6.2.0/module-LOADED.test @@ -2,12 +2,8 @@ set -o errexit -sudo modprobe nfsd +sudo modprobe xfs 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" +echo "file fs/xfs/xfs_stats.c +p" > /sys/kernel/debug/dynamic_debug/control +grep -q kpatch /sys/fs/xfs/stats/stats +dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/linux-6.2.0/module.patch b/test/integration/linux-6.2.0/module.patch index 5f87a137e..bb8edadaf 100644 --- a/test/integration/linux-6.2.0/module.patch +++ b/test/integration/linux-6.2.0/module.patch @@ -13,38 +13,43 @@ This tests several things related to the patching of modules: Signed-off-by: Josh Poimboeuf -diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c ---- src.orig/fs/nfsd/export.c 2023-01-12 11:20:07.174710504 -0500 -+++ src/fs/nfsd/export.c 2023-01-12 11:20:46.075941153 -0500 -@@ -1294,6 +1294,10 @@ static void exp_flags(struct seq_file *m - } +diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c +index 90a77cd3ebad..f65df90ae8e1 100644 +--- a/fs/xfs/xfs_stats.c ++++ b/fs/xfs/xfs_stats.c +@@ -16,6 +16,8 @@ static int counter_val(struct xfsstats __percpu *stats, int idx) + return val; } -+#include +extern char *kpatch_string(void); + -+__attribute__((optimize("-fno-optimize-sibling-calls"))) - static int e_show(struct seq_file *m, void *p) + int xfs_stats_format(struct xfsstats __percpu *stats, char *buf) { - 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); + int i, j; +@@ -85,6 +87,34 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf) + 0); + #endif ++ /* Reference a symbol outside the .o yet inside the patch module: */ ++ len += scnprintf(buf + len, PATH_MAX-len, "%s\n", kpatch_string()); ++ +#ifdef CONFIG_X86_64 -+ alternative("ud2", "call single_task_running", X86_FEATURE_ALWAYS); -+ alternative("call single_task_running", "ud2", X86_FEATURE_IA64); ++ /* Test alternatives patching: */ ++ alternative("ud2", "nop", X86_FEATURE_ALWAYS); ++ alternative("nop", "ud2", X86_FEATURE_IA64); + ++ /* Test paravirt patching: */ + slow_down_io(); /* paravirt call */ +#endif + ++ /* Test pr_debug: */ + pr_debug("kpatch: pr_debug() test\n"); + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) +{ ++ /* Test static branches: */ + static DEFINE_STATIC_KEY_TRUE(kpatch_key); + -+ if (static_branch_unlikely(&memcg_kmem_enabled_key)) ++ if (static_branch_unlikely(&memcg_kmem_enabled_key)) + printk("kpatch: memcg_kmem_enabled_key\n"); + + BUG_ON(!static_branch_likely(&kpatch_key)); @@ -52,28 +57,21 @@ diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c + 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; - } + return len; + } -diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c ---- src.orig/net/netlink/af_netlink.c 2023-01-12 11:20:07.603713047 -0500 -+++ src/net/netlink/af_netlink.c 2023-01-12 11:20:46.076941159 -0500 -@@ -2970,4 +2970,9 @@ panic: +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index c64277659753..05b753c2ec84 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2980,4 +2980,9 @@ static int __init netlink_proto_init(void) panic("netlink_init: Cannot allocate nl_table\n"); } +char *kpatch_string(void) +{ -+ return "# kpatch\n"; ++ return "kpatch"; +} + core_initcall(netlink_proto_init);