From aa69a146cef07a6cbb89ef52e7f9facae53b0db0 Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Fri, 31 May 2024 15:17:46 -0700 Subject: [PATCH 01/34] avoid reloc-type collisions on elf.h constants Do we need more robust architecture protection (Issue #1356) The elf.h reloc-type constants are not unique across archs #define R_PPC64_REL24 10 /* PC relative 26 bit */ #define R_X86_64_32 10 /* Direct 32 bit zero extended */ so to avoid any unexpected aliasing, guard all R_arch_type refs with a check on kelf->arch, or a global default arch set from the first elf encountered. --- kpatch-build/create-diff-object.c | 77 ++++++++++++++++++----------- kpatch-build/create-kpatch-module.c | 3 ++ kpatch-build/kpatch-elf.c | 24 ++++++++- kpatch-build/kpatch-elf.h | 3 ++ 4 files changed, 77 insertions(+), 30 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 25710e921..1097e2daf 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -215,6 +215,8 @@ static bool is_gcc6_localentry_bundled_sym(struct kpatch_elf *kelf, */ static struct rela *toc_rela(const struct rela *rela) { + if (!is_arch(PPC64)) + return (struct rela *)rela; if (rela->type != R_PPC64_TOC16_HA && rela->type != R_PPC64_TOC16_LO_DS) return (struct rela *)rela; @@ -1618,7 +1620,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) if (is_text_section(relasec->base) && !is_text_section(sym->sec) && - rela->type == R_X86_64_32S && + is_arch(X86_64) && rela->type == R_X86_64_32S && rela->addend == (long)sym->sec->sh.sh_size && end == (long)sym->sec->sh.sh_size) { @@ -3236,12 +3238,23 @@ static int function_ptr_rela(const struct rela *rela) { const struct rela *rela_toc = toc_rela(rela); + switch (def_arch()) { + case PPC64: + if (rela->type != R_PPC64_TOC16_HA && + rela->type != R_PPC64_TOC16_LO_DS) + return false; + break; + case X86_64: + if (rela->type != R_X86_64_32S) + return false; + break; + default: + break; + } + 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)); + rela_toc->addend == (int)rela_toc->sym->sym.st_value); } static bool need_klp_reloc(struct kpatch_elf *kelf, struct lookup_table *table, @@ -3256,32 +3269,38 @@ static bool need_klp_reloc(struct kpatch_elf *kelf, struct lookup_table *table, * These references are treated specially by the module loader and * should never be converted to klp relocations. */ - if (rela->type == R_PPC64_REL16_HA || rela->type == R_PPC64_REL16_LO || - rela->type == R_PPC64_ENTRY) - return false; + switch (kelf->arch) { + case PPC64: + 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; + /* 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; + /* + * 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; + break; + default: + break; + } /* * Allow references to core module symbols to remain as normal diff --git a/kpatch-build/create-kpatch-module.c b/kpatch-build/create-kpatch-module.c index 2884f93d9..b758bd4d9 100644 --- a/kpatch-build/create-kpatch-module.c +++ b/kpatch-build/create-kpatch-module.c @@ -58,6 +58,9 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section dynsec = create_section_pair(kelf, ".kpatch.dynrelas", sizeof(*dynrelas), nr); dynrelas = dynsec->data->d_buf; + if (kelf->arch != X86_64) + return; + for (index = 0; index < nr; index++) { offset = index * (unsigned int)sizeof(*krelas); diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 374d424cc..885ab913a 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -38,6 +38,26 @@ * Helper functions ******************/ +static enum architecture current_arch; + +enum architecture def_arch(void) +{ + return current_arch; +} + +bool is_arch(enum architecture arch) +{ + return current_arch == arch; +} + +void set_arch(enum architecture arch) +{ + if (!arch || (current_arch && arch != current_arch)) + ERROR("inconsistent ELF arch: setting %d but already %d", + arch, current_arch); + current_arch = arch; +} + char *status_str(enum status status) { switch(status) { @@ -594,8 +614,10 @@ struct kpatch_elf *kpatch_elf_open(const char *name) kelf->arch = S390; break; default: - ERROR("Unsupported target architecture"); + ERROR("Unsupported target architecture: e_machine %x", + ehdr.e_machine); } + set_arch(kelf->arch); kpatch_create_section_list(kelf); kpatch_create_symbol_list(kelf); diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index e32209b72..d47b6c7e0 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -157,6 +157,9 @@ 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); +enum architecture def_arch(void); +void set_arch(enum architecture); +bool is_arch(enum architecture); #ifndef R_PPC64_ENTRY #define R_PPC64_ENTRY 118 From 5d6294445570702fcf28566b2b72b23384023799 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Mon, 4 Dec 2023 10:31:13 -0500 Subject: [PATCH 02/34] test/integration/rhel-8.9: integration testing for rhel-8.9 Provides integration tests for rhel-8.9 (kernel-4.18.0-513.5.1.el8_9), note that module.test is from the linux-6.2/ directory and not rhel-8.8/ Signed-off-by: Ryan Sullivan --- .../rhel-8.9/bug-table-section.patch | 12 ++ .../rhel-8.9/cmdline-string-LOADED.test | 3 + .../integration/rhel-8.9/cmdline-string.patch | 13 ++ .../integration/rhel-8.9/data-new-LOADED.test | 3 + test/integration/rhel-8.9/data-new.patch | 20 +++ .../rhel-8.9/data-read-mostly.patch | 11 ++ test/integration/rhel-8.9/fixup-section.patch | 11 ++ test/integration/rhel-8.9/gcc-constprop.patch | 13 ++ test/integration/rhel-8.9/gcc-isra.patch | 11 ++ test/integration/rhel-8.9/gcc-mangled-3.patch | 13 ++ .../rhel-8.9/gcc-static-local-var-2.patch | 13 ++ .../rhel-8.9/gcc-static-local-var-3.patch | 19 +++ .../rhel-8.9/gcc-static-local-var-4.patch | 23 +++ .../rhel-8.9/gcc-static-local-var-4.test | 8 + .../rhel-8.9/gcc-static-local-var-5.patch | 45 +++++ .../rhel-8.9/gcc-static-local-var-6.patch | 22 +++ .../rhel-8.9/macro-callbacks.patch | 155 ++++++++++++++++++ test/integration/rhel-8.9/macro-printk.patch | 149 +++++++++++++++++ .../rhel-8.9/meminfo-init-FAIL.patch | 11 ++ .../rhel-8.9/meminfo-init2-FAIL.patch | 19 +++ .../rhel-8.9/meminfo-string-LOADED.test | 3 + .../integration/rhel-8.9/meminfo-string.patch | 12 ++ test/integration/rhel-8.9/module-LOADED.test | 11 ++ test/integration/rhel-8.9/module.patch | 75 +++++++++ test/integration/rhel-8.9/multiple.test | 7 + test/integration/rhel-8.9/new-function.patch | 25 +++ test/integration/rhel-8.9/new-globals.patch | 34 ++++ .../rhel-8.9/parainstructions-section.patch | 11 ++ .../rhel-8.9/shadow-newpid-LOADED.test | 3 + test/integration/rhel-8.9/shadow-newpid.patch | 75 +++++++++ .../rhel-8.9/smp-locks-section.patch | 13 ++ .../integration/rhel-8.9/special-static.patch | 22 +++ .../rhel-8.9/symvers-disagreement-FAIL.patch | 46 ++++++ test/integration/rhel-8.9/syscall-LOADED.test | 3 + test/integration/rhel-8.9/syscall.patch | 25 +++ .../rhel-8.9/tracepoints-section.patch | 13 ++ .../rhel-8.9/warn-detect-FAIL.patch | 9 + 37 files changed, 961 insertions(+) create mode 100644 test/integration/rhel-8.9/bug-table-section.patch create mode 100755 test/integration/rhel-8.9/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-8.9/cmdline-string.patch create mode 100755 test/integration/rhel-8.9/data-new-LOADED.test create mode 100644 test/integration/rhel-8.9/data-new.patch create mode 100644 test/integration/rhel-8.9/data-read-mostly.patch create mode 100644 test/integration/rhel-8.9/fixup-section.patch create mode 100644 test/integration/rhel-8.9/gcc-constprop.patch create mode 100644 test/integration/rhel-8.9/gcc-isra.patch create mode 100644 test/integration/rhel-8.9/gcc-mangled-3.patch create mode 100644 test/integration/rhel-8.9/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-8.9/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-8.9/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-8.9/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-8.9/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-8.9/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-8.9/macro-callbacks.patch create mode 100644 test/integration/rhel-8.9/macro-printk.patch create mode 100644 test/integration/rhel-8.9/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-8.9/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-8.9/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-8.9/meminfo-string.patch create mode 100755 test/integration/rhel-8.9/module-LOADED.test create mode 100644 test/integration/rhel-8.9/module.patch create mode 100755 test/integration/rhel-8.9/multiple.test create mode 100644 test/integration/rhel-8.9/new-function.patch create mode 100644 test/integration/rhel-8.9/new-globals.patch create mode 100644 test/integration/rhel-8.9/parainstructions-section.patch create mode 100755 test/integration/rhel-8.9/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-8.9/shadow-newpid.patch create mode 100644 test/integration/rhel-8.9/smp-locks-section.patch create mode 100644 test/integration/rhel-8.9/special-static.patch create mode 100644 test/integration/rhel-8.9/symvers-disagreement-FAIL.patch create mode 100755 test/integration/rhel-8.9/syscall-LOADED.test create mode 100644 test/integration/rhel-8.9/syscall.patch create mode 100644 test/integration/rhel-8.9/tracepoints-section.patch create mode 100644 test/integration/rhel-8.9/warn-detect-FAIL.patch diff --git a/test/integration/rhel-8.9/bug-table-section.patch b/test/integration/rhel-8.9/bug-table-section.patch new file mode 100644 index 000000000..8643ed791 --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/proc_sysctl.c 2023-05-18 13:26:13.738170286 -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.9/cmdline-string-LOADED.test b/test/integration/rhel-8.9/cmdline-string-LOADED.test new file mode 100755 index 000000000..a8e0a0817 --- /dev/null +++ b/test/integration/rhel-8.9/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.9/cmdline-string.patch b/test/integration/rhel-8.9/cmdline-string.patch new file mode 100644 index 000000000..f414a15d0 --- /dev/null +++ b/test/integration/rhel-8.9/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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/cmdline.c 2023-05-18 13:26:20.040162860 -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.9/data-new-LOADED.test b/test/integration/rhel-8.9/data-new-LOADED.test new file mode 100755 index 000000000..9f25744e6 --- /dev/null +++ b/test/integration/rhel-8.9/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.9/data-new.patch b/test/integration/rhel-8.9/data-new.patch new file mode 100644 index 000000000..8cf7586ac --- /dev/null +++ b/test/integration/rhel-8.9/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-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/meminfo.c 2023-05-18 13:26:27.035154617 -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.9/data-read-mostly.patch b/test/integration/rhel-8.9/data-read-mostly.patch new file mode 100644 index 000000000..5de06ebfe --- /dev/null +++ b/test/integration/rhel-8.9/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 2023-05-18 13:26:13.486170583 -0400 ++++ src/net/core/dev.c 2023-05-18 13:26:31.948148828 -0400 +@@ -5440,6 +5440,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.9/fixup-section.patch b/test/integration/rhel-8.9/fixup-section.patch new file mode 100644 index 000000000..3a8e77fa1 --- /dev/null +++ b/test/integration/rhel-8.9/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2023-05-18 13:26:13.306170795 -0400 ++++ src/fs/readdir.c 2023-05-18 13:26:37.497142290 -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.9/gcc-constprop.patch b/test/integration/rhel-8.9/gcc-constprop.patch new file mode 100644 index 000000000..797a20e9e --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.459170615 -0400 ++++ src/kernel/time/timekeeping.c 2023-05-18 13:26:42.511136382 -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.9/gcc-isra.patch b/test/integration/rhel-8.9/gcc-isra.patch new file mode 100644 index 000000000..00e9b5dbe --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/proc_sysctl.c 2023-05-18 13:26:48.145129743 -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.9/gcc-mangled-3.patch b/test/integration/rhel-8.9/gcc-mangled-3.patch new file mode 100644 index 000000000..e7363145a --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.476170595 -0400 ++++ src/mm/slub.c 2023-05-18 13:26:51.355125960 -0400 +@@ -6085,6 +6085,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.9/gcc-static-local-var-2.patch b/test/integration/rhel-8.9/gcc-static-local-var-2.patch new file mode 100644 index 000000000..85cc818a4 --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.475170596 -0400 ++++ src/mm/mmap.c 2023-05-18 13:26:54.612122123 -0400 +@@ -1691,6 +1691,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.9/gcc-static-local-var-3.patch b/test/integration/rhel-8.9/gcc-static-local-var-3.patch new file mode 100644 index 000000000..409642edb --- /dev/null +++ b/test/integration/rhel-8.9/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 2023-05-18 13:26:13.457170617 -0400 ++++ src/kernel/reboot.c 2023-05-18 13:26:57.125119162 -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.9/gcc-static-local-var-4.patch b/test/integration/rhel-8.9/gcc-static-local-var-4.patch new file mode 100644 index 000000000..4b7e899d3 --- /dev/null +++ b/test/integration/rhel-8.9/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 2023-05-18 13:26:13.270170838 -0400 ++++ src/fs/aio.c 2023-05-18 13:26:59.650116186 -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.9/gcc-static-local-var-4.test b/test/integration/rhel-8.9/gcc-static-local-var-4.test new file mode 100755 index 000000000..e085f9345 --- /dev/null +++ b/test/integration/rhel-8.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-8.9/gcc-static-local-var-5.patch b/test/integration/rhel-8.9/gcc-static-local-var-5.patch new file mode 100644 index 000000000..f34c538cc --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.449170627 -0400 ++++ src/kernel/audit.c 2023-05-18 13:27:02.109113288 -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.9/gcc-static-local-var-6.patch b/test/integration/rhel-8.9/gcc-static-local-var-6.patch new file mode 100644 index 000000000..483a640e6 --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.496170571 -0400 ++++ src/net/ipv6/netfilter.c 2023-05-18 13:27:04.595110359 -0400 +@@ -93,6 +93,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) + { +@@ -106,6 +108,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.9/macro-callbacks.patch b/test/integration/rhel-8.9/macro-callbacks.patch new file mode 100644 index 000000000..a7be36427 --- /dev/null +++ b/test/integration/rhel-8.9/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-05-18 13:26:12.823171364 -0400 ++++ src/drivers/input/joydev.c 2023-05-18 13:27:06.913107628 -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 2023-05-18 13:26:12.827171360 -0400 ++++ src/drivers/input/misc/pcspkr.c 2023-05-18 13:27:06.913107628 -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 2023-05-18 13:26:13.270170838 -0400 ++++ src/fs/aio.c 2023-05-18 13:27:06.913107628 -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.9/macro-printk.patch b/test/integration/rhel-8.9/macro-printk.patch new file mode 100644 index 000000000..b8453e0ab --- /dev/null +++ b/test/integration/rhel-8.9/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 2023-05-18 13:26:13.492170576 -0400 ++++ src/net/ipv4/fib_frontend.c 2023-05-18 13:27:09.416104679 -0400 +@@ -798,6 +798,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) + { +@@ -819,6 +820,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 2023-05-18 13:26:13.492170576 -0400 ++++ src/net/ipv4/fib_semantics.c 2023-05-18 13:27:09.417104677 -0400 +@@ -1026,6 +1026,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) + { +@@ -1059,6 +1060,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; +@@ -1079,6 +1081,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) +@@ -1092,6 +1095,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; +@@ -1147,9 +1152,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; + +@@ -1167,6 +1174,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) { +@@ -1188,6 +1196,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"); +@@ -1226,6 +1235,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"); +@@ -1235,6 +1245,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); + +@@ -1246,6 +1257,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); +@@ -1269,6 +1281,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: +@@ -1279,6 +1292,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 2023-05-18 13:26:13.492170576 -0400 ++++ src/net/ipv4/fib_trie.c 2023-05-18 13:27:09.417104677 -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.9/meminfo-init-FAIL.patch b/test/integration/rhel-8.9/meminfo-init-FAIL.patch new file mode 100644 index 000000000..1455bf358 --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/meminfo.c 2023-05-18 13:27:14.460098735 -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.9/meminfo-init2-FAIL.patch b/test/integration/rhel-8.9/meminfo-init2-FAIL.patch new file mode 100644 index 000000000..5a28e6041 --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/meminfo.c 2023-05-18 13:27:11.926101721 -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.9/meminfo-string-LOADED.test b/test/integration/rhel-8.9/meminfo-string-LOADED.test new file mode 100755 index 000000000..10dc20b3d --- /dev/null +++ b/test/integration/rhel-8.9/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.9/meminfo-string.patch b/test/integration/rhel-8.9/meminfo-string.patch new file mode 100644 index 000000000..5fea340af --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/meminfo.c 2023-05-18 13:27:16.879095885 -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: ", vmalloc_nr_pages()); +- 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.9/module-LOADED.test b/test/integration/rhel-8.9/module-LOADED.test new file mode 100755 index 000000000..bed43ca52 --- /dev/null +++ b/test/integration/rhel-8.9/module-LOADED.test @@ -0,0 +1,11 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe xfs +sleep 5 +grep -q kpatch /sys/fs/xfs/stats/stats +# 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/xfs/xfs_stats.c +p" > /sys/kernel/debug/dynamic_debug/control +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.9/module.patch b/test/integration/rhel-8.9/module.patch new file mode 100644 index 000000000..a6609316c --- /dev/null +++ b/test/integration/rhel-8.9/module.patch @@ -0,0 +1,75 @@ +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/xfs/xfs_stats.c src/fs/xfs/xfs_stats.c +--- src.orig/fs/xfs/xfs_stats.c 2023-05-18 13:26:13.315170784 -0400 ++++ src/fs/xfs/xfs_stats.c 2023-05-18 13:27:19.233093111 -0400 +@@ -16,6 +16,8 @@ static int counter_val(struct xfsstats _ + return val; + } + ++extern char *kpatch_string(void); ++ + int xfs_stats_format(struct xfsstats __percpu *stats, char *buf) + { + int i, j; +@@ -85,6 +87,34 @@ int xfs_stats_format(struct xfsstats __p + 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 ++ /* 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"); ++ ++{ ++ /* Test static branches: */ ++ 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); ++} ++ + 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-05-18 13:26:13.508170557 -0400 ++++ src/net/netlink/af_netlink.c 2023-05-18 13:27:19.233093111 -0400 +@@ -2886,4 +2886,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "kpatch"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.9/multiple.test b/test/integration/rhel-8.9/multiple.test new file mode 100755 index 000000000..7e4b3525c --- /dev/null +++ b/test/integration/rhel-8.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-8.9/new-function.patch b/test/integration/rhel-8.9/new-function.patch new file mode 100644 index 000000000..74207c219 --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.216170901 -0400 ++++ src/drivers/tty/n_tty.c 2023-05-18 13:27:21.591090332 -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.9/new-globals.patch b/test/integration/rhel-8.9/new-globals.patch new file mode 100644 index 000000000..1c297ee76 --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/cmdline.c 2023-05-18 13:27:23.780087752 -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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/meminfo.c 2023-05-18 13:27:23.780087752 -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.9/parainstructions-section.patch b/test/integration/rhel-8.9/parainstructions-section.patch new file mode 100644 index 000000000..d478d716f --- /dev/null +++ b/test/integration/rhel-8.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 2023-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/generic.c 2023-05-18 13:27:25.966085176 -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.9/shadow-newpid-LOADED.test b/test/integration/rhel-8.9/shadow-newpid-LOADED.test new file mode 100755 index 000000000..c07d11205 --- /dev/null +++ b/test/integration/rhel-8.9/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.9/shadow-newpid.patch b/test/integration/rhel-8.9/shadow-newpid.patch new file mode 100644 index 000000000..4e05bd336 --- /dev/null +++ b/test/integration/rhel-8.9/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-05-18 13:26:13.304170797 -0400 ++++ src/fs/proc/array.c 2023-05-18 13:27:28.151082601 -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 2023-05-18 13:26:13.453170622 -0400 ++++ src/kernel/exit.c 2023-05-18 13:27:28.151082601 -0400 +@@ -704,6 +704,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; +@@ -804,6 +805,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-05-18 13:26:13.453170622 -0400 ++++ src/kernel/fork.c 2023-05-18 13:27:28.152082600 -0400 +@@ -2401,6 +2401,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, +@@ -2413,6 +2414,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 +@@ -2439,6 +2442,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.9/smp-locks-section.patch b/test/integration/rhel-8.9/smp-locks-section.patch new file mode 100644 index 000000000..72fbeb93c --- /dev/null +++ b/test/integration/rhel-8.9/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 2023-05-18 13:26:13.220170896 -0400 ++++ src/drivers/tty/tty_buffer.c 2023-05-18 13:27:30.347080014 -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.9/special-static.patch b/test/integration/rhel-8.9/special-static.patch new file mode 100644 index 000000000..5527b40ec --- /dev/null +++ b/test/integration/rhel-8.9/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2023-05-18 13:26:13.453170622 -0400 ++++ src/kernel/fork.c 2023-05-18 13:27:32.559077407 -0400 +@@ -1588,10 +1588,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.9/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.9/symvers-disagreement-FAIL.patch new file mode 100644 index 000000000..093acf212 --- /dev/null +++ b/test/integration/rhel-8.9/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-05-18 13:26:12.556171679 -0400 ++++ src/drivers/base/core.c 2023-05-18 13:27:34.750074826 -0400 +@@ -34,6 +34,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-05-18 13:26:13.223170893 -0400 ++++ src/drivers/usb/core/usb.c 2023-05-18 13:27:34.751074825 -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.9/syscall-LOADED.test b/test/integration/rhel-8.9/syscall-LOADED.test new file mode 100755 index 000000000..3a2fd88e0 --- /dev/null +++ b/test/integration/rhel-8.9/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/rhel-8.9/syscall.patch b/test/integration/rhel-8.9/syscall.patch new file mode 100644 index 000000000..b5d201f0a --- /dev/null +++ b/test/integration/rhel-8.9/syscall.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2023-05-18 13:26:13.458170616 -0400 ++++ src/kernel/sys.c 2023-05-18 13:27:36.949072235 -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.9/tracepoints-section.patch b/test/integration/rhel-8.9/tracepoints-section.patch new file mode 100644 index 000000000..1f310595e --- /dev/null +++ b/test/integration/rhel-8.9/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 2023-05-18 13:26:13.459170615 -0400 ++++ src/kernel/time/timer.c 2023-05-18 13:27:39.140069653 -0400 +@@ -1986,6 +1986,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.9/warn-detect-FAIL.patch b/test/integration/rhel-8.9/warn-detect-FAIL.patch new file mode 100644 index 000000000..969f49ea1 --- /dev/null +++ b/test/integration/rhel-8.9/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-05-18 13:26:12.518171724 -0400 ++++ src/arch/x86/kvm/x86.c 2023-05-18 13:27:41.361067036 -0400 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * From 439876ef877397e855538254cb6b11eb8442c62b Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Wed, 6 Dec 2023 13:55:23 +0800 Subject: [PATCH 03/34] kpatch-build: check if gawk is installed kpatch-build uses gawk to find special section, but gawk is not always installed. So check if gawk is installed. Signed-off-by: Hongchen Zhang --- kpatch-build/kpatch-build | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 5e02faafc..6653e8d9d 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -674,6 +674,10 @@ usage() { echo " (not recommended)" >&2 } +if ! command -v gawk &> /dev/null; then + die "gawk not installed" +fi + 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" From 6f644142bb6beb8fff5b7e673b993ee5d4f49b9c Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Tue, 5 Dec 2023 12:19:38 -0500 Subject: [PATCH 04/34] test/integration/rhel-9.3: integration testing for rhel-9.3 Provides integration tests for rhel-9.3 (kernel-5.14.0-362.8.1.el9_3) Signed-off-by: Ryan Sullivan --- .../integration/rhel-9.3/data-new-LOADED.test | 3 + test/integration/rhel-9.3/data-new.patch | 20 +++ .../rhel-9.3/gcc-static-local-var-6.patch | 22 +++ .../rhel-9.3/macro-callbacks.patch | 155 ++++++++++++++++++ test/integration/rhel-9.3/module-LOADED.test | 13 ++ test/integration/rhel-9.3/module.patch | 79 +++++++++ test/integration/rhel-9.3/multiple.test | 7 + test/integration/rhel-9.3/new-function.patch | 25 +++ test/integration/rhel-9.3/new-globals.patch | 34 ++++ .../rhel-9.3/shadow-newpid-LOADED.test | 3 + test/integration/rhel-9.3/shadow-newpid.patch | 75 +++++++++ .../integration/rhel-9.3/special-static.patch | 22 +++ .../rhel-9.3/symvers-disagreement-FAIL.patch | 46 ++++++ test/integration/rhel-9.3/syscall-LOADED.test | 3 + test/integration/rhel-9.3/syscall.patch | 20 +++ .../rhel-9.3/warn-detect-FAIL.patch | 9 + 16 files changed, 536 insertions(+) create mode 100755 test/integration/rhel-9.3/data-new-LOADED.test create mode 100644 test/integration/rhel-9.3/data-new.patch create mode 100644 test/integration/rhel-9.3/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-9.3/macro-callbacks.patch create mode 100755 test/integration/rhel-9.3/module-LOADED.test create mode 100644 test/integration/rhel-9.3/module.patch create mode 100755 test/integration/rhel-9.3/multiple.test create mode 100644 test/integration/rhel-9.3/new-function.patch create mode 100644 test/integration/rhel-9.3/new-globals.patch create mode 100755 test/integration/rhel-9.3/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-9.3/shadow-newpid.patch create mode 100644 test/integration/rhel-9.3/special-static.patch create mode 100644 test/integration/rhel-9.3/symvers-disagreement-FAIL.patch create mode 100755 test/integration/rhel-9.3/syscall-LOADED.test create mode 100644 test/integration/rhel-9.3/syscall.patch create mode 100644 test/integration/rhel-9.3/warn-detect-FAIL.patch diff --git a/test/integration/rhel-9.3/data-new-LOADED.test b/test/integration/rhel-9.3/data-new-LOADED.test new file mode 100755 index 000000000..9f25744e6 --- /dev/null +++ b/test/integration/rhel-9.3/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-9.3/data-new.patch b/test/integration/rhel-9.3/data-new.patch new file mode 100644 index 000000000..42b076b43 --- /dev/null +++ b/test/integration/rhel-9.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 2023-07-14 10:41:06.284435188 -0400 ++++ src/fs/proc/meminfo.c 2023-07-14 10:41:07.271433247 -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; +@@ -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/rhel-9.3/gcc-static-local-var-6.patch b/test/integration/rhel-9.3/gcc-static-local-var-6.patch new file mode 100644 index 000000000..e8fc21d3e --- /dev/null +++ b/test/integration/rhel-9.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 2023-07-14 10:41:06.614434539 -0400 ++++ src/net/ipv6/netfilter.c 2023-07-14 10:41:15.328417401 -0400 +@@ -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/rhel-9.3/macro-callbacks.patch b/test/integration/rhel-9.3/macro-callbacks.patch new file mode 100644 index 000000000..96a108832 --- /dev/null +++ b/test/integration/rhel-9.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 2023-07-14 10:41:05.666436404 -0400 ++++ src/drivers/input/joydev.c 2023-07-14 10:41:18.589410987 -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 2023-07-14 10:41:05.671436394 -0400 ++++ src/drivers/input/misc/pcspkr.c 2023-07-14 10:41:18.590410985 -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 2023-07-14 10:41:06.243435269 -0400 ++++ src/fs/aio.c 2023-07-14 10:41:18.591410983 -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.3/module-LOADED.test b/test/integration/rhel-9.3/module-LOADED.test new file mode 100755 index 000000000..72bb85266 --- /dev/null +++ b/test/integration/rhel-9.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-9.3/module.patch b/test/integration/rhel-9.3/module.patch new file mode 100644 index 000000000..548a3d8b8 --- /dev/null +++ b/test/integration/rhel-9.3/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-07-14 10:41:06.272435212 -0400 ++++ src/fs/nfsd/export.c 2023-07-14 10:41:21.273405708 -0400 +@@ -1299,6 +1299,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; +@@ -1306,12 +1310,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-07-14 10:41:06.629434510 -0400 ++++ src/net/netlink/af_netlink.c 2023-07-14 10:41:21.273405708 -0400 +@@ -2942,4 +2942,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.3/multiple.test b/test/integration/rhel-9.3/multiple.test new file mode 100755 index 000000000..7e4b3525c --- /dev/null +++ b/test/integration/rhel-9.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-9.3/new-function.patch b/test/integration/rhel-9.3/new-function.patch new file mode 100644 index 000000000..a9346a481 --- /dev/null +++ b/test/integration/rhel-9.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 2023-07-14 10:41:06.178435397 -0400 ++++ src/drivers/tty/n_tty.c 2023-07-14 10:41:23.942400459 -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.3/new-globals.patch b/test/integration/rhel-9.3/new-globals.patch new file mode 100644 index 000000000..1c1411e85 --- /dev/null +++ b/test/integration/rhel-9.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 2023-07-14 10:41:06.283435190 -0400 ++++ src/fs/proc/cmdline.c 2023-07-14 10:41:26.683395067 -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 2023-07-14 10:41:06.284435188 -0400 ++++ src/fs/proc/meminfo.c 2023-07-14 10:41:26.683395067 -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.3/shadow-newpid-LOADED.test b/test/integration/rhel-9.3/shadow-newpid-LOADED.test new file mode 100755 index 000000000..c07d11205 --- /dev/null +++ b/test/integration/rhel-9.3/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-9.3/shadow-newpid.patch b/test/integration/rhel-9.3/shadow-newpid.patch new file mode 100644 index 000000000..0132bbb73 --- /dev/null +++ b/test/integration/rhel-9.3/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-07-14 10:41:06.283435190 -0400 ++++ src/fs/proc/array.c 2023-07-14 10:41:29.489389549 -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 2023-07-14 10:41:06.560434645 -0400 ++++ src/kernel/exit.c 2023-07-14 10:41:29.490389546 -0400 +@@ -732,6 +732,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 2023-07-14 10:41:06.560434645 -0400 ++++ src/kernel/fork.c 2023-07-14 10:41:29.491389545 -0400 +@@ -2601,6 +2601,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; +@@ -2609,6 +2610,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 +@@ -2648,6 +2651,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.3/special-static.patch b/test/integration/rhel-9.3/special-static.patch new file mode 100644 index 000000000..bf76963bf --- /dev/null +++ b/test/integration/rhel-9.3/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2023-07-14 10:41:06.560434645 -0400 ++++ src/kernel/fork.c 2023-07-14 10:41:32.174384268 -0400 +@@ -1675,10 +1675,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.3/symvers-disagreement-FAIL.patch b/test/integration/rhel-9.3/symvers-disagreement-FAIL.patch new file mode 100644 index 000000000..a9cd6bb46 --- /dev/null +++ b/test/integration/rhel-9.3/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-07-14 10:41:05.314437096 -0400 ++++ src/drivers/base/core.c 2023-07-14 10:41:34.809379085 -0400 +@@ -35,6 +35,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-07-14 10:41:06.189435375 -0400 ++++ src/drivers/usb/core/usb.c 2023-07-14 10:41:34.810379083 -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/rhel-9.3/syscall-LOADED.test b/test/integration/rhel-9.3/syscall-LOADED.test new file mode 100755 index 000000000..3a2fd88e0 --- /dev/null +++ b/test/integration/rhel-9.3/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/rhel-9.3/syscall.patch b/test/integration/rhel-9.3/syscall.patch new file mode 100644 index 000000000..fa6bd5d67 --- /dev/null +++ b/test/integration/rhel-9.3/syscall.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2023-07-14 10:41:06.567434632 -0400 ++++ src/kernel/sys.c 2023-07-14 10:41:37.436373918 -0400 +@@ -1284,13 +1284,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.3/warn-detect-FAIL.patch b/test/integration/rhel-9.3/warn-detect-FAIL.patch new file mode 100644 index 000000000..1e30cfc24 --- /dev/null +++ b/test/integration/rhel-9.3/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-07-14 10:41:05.270437183 -0400 ++++ src/arch/x86/kvm/x86.c 2023-07-14 10:41:40.078368722 -0400 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * From 7e7c9694220018fb40787e9394f08a29e4404cd6 Mon Sep 17 00:00:00 2001 From: "zhangyongde.zyd" Date: Wed, 17 Jan 2024 12:45:12 +0800 Subject: [PATCH 05/34] kpatch-build: simplify distro support Rather than adding yet another set of conditionals to handle the Anolis OS distribution, refactor the SUPPORTED_DISTROS code using an associative array. The array is keyed by the short distro name, and contains the longer distribution description. Signed-off-by: Wardenjohn --- kpatch-build/kpatch-build | 52 +++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 6653e8d9d..15cd6f9bf 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -65,6 +65,20 @@ LLD="${CROSS_COMPILE:-}ld.lld" READELF="${CROSS_COMPILE:-}readelf" OBJCOPY="${CROSS_COMPILE:-}objcopy" + +declare -rA SUPPORTED_DEB_DISTROS=( + ["debian"]="Debian OS" + ["ubuntu"]="Ubuntu OS") + +declare -rA SUPPORTED_RPM_DISTROS=( + ["centos"]="CentOS" + ["fedora"]="Fedora" + ["openEuler"]="OpenEuler" + ["ol"]="Oracle" + ["photon"]="Photon OS" + ["rhel"]="RHEL") + + warn() { echo "ERROR: $1" >&2 } @@ -649,6 +663,25 @@ module_name_string() { echo "${1//[^a-zA-Z0-9_-]/-}" | cut -c 1-55 } +is_supported_deb_distro(){ + [[ -n "${SUPPORTED_DEB_DISTROS[$1]:-}" ]] +} + +is_supported_rpm_distro(){ + [[ -n "${SUPPORTED_RPM_DISTROS[$1]:-}" ]] +} + +print_supported_distro(){ + if is_supported_deb_distro "$DISTRO"; then + echo "${SUPPORTED_DEB_DISTROS[$DISTRO]} distribution detected" + elif is_supported_rpm_distro "$DISTRO"; then + echo "${SUPPORTED_RPM_DISTROS[$DISTRO]} distribution detected" + else + echo "$DISTRO is not supported" + fi +} + + usage() { echo "usage: $(basename "$0") [options] " >&2 echo " patchN Input patchfile(s)" >&2 @@ -869,16 +902,14 @@ fi [[ -z "$TARGETS" ]] && TARGETS="vmlinux modules" -if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || - [[ "$DISTRO" = centos ]] || [[ "$DISTRO" = openEuler ]] || - [[ "$DISTRO" = photon ]]; then +if is_supported_rpm_distro "$DISTRO"; 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 +elif is_supported_deb_distro "$DISTRO"; then [[ -z "$VMLINUX" ]] && VMLINUX="/usr/lib/debug/boot/vmlinux-$ARCHVERSION" if [[ "$DISTRO" = ubuntu ]]; then @@ -910,14 +941,9 @@ elif [[ -e "$KERNEL_SRCDIR"/.config ]] && [[ -e "$VERSIONFILE" ]] && [[ "$(cat " echo "Using cache at $KERNEL_SRCDIR" else - if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || [[ "$DISTRO" = centos ]] || [[ "$DISTRO" = openEuler ]] || [[ "$DISTRO" = photon ]]; then + if is_supported_rpm_distro "$DISTRO"; 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" - [[ "$DISTRO" = photon ]] && echo "Photon OS distribution detected" + print_supported_distro "$DISTRO" clean_cache @@ -1013,9 +1039,9 @@ else (cd "$KERNEL_SRCDIR" && make mrproper 2>&1 | logger) || die - elif [[ "$DISTRO" = ubuntu ]] || [[ "$DISTRO" = debian ]]; then + elif is_supported_deb_distro "$DISTRO"; then - echo "Debian/Ubuntu distribution detected" + print_supported_distro "$DISTRO" if [[ "$DISTRO" = ubuntu ]]; then From 289045e39a4e6270d270cdd3ba0afe214a129b93 Mon Sep 17 00:00:00 2001 From: "zhangyongde.zyd" Date: Wed, 17 Jan 2024 12:46:34 +0800 Subject: [PATCH 06/34] kpatch-build: Support distro Anolis OS Support Anolis OS Signed-off-by: Wardenjohn --- kpatch-build/kpatch-build | 1 + test/integration/lib.sh | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 15cd6f9bf..6bdad3bb3 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -71,6 +71,7 @@ declare -rA SUPPORTED_DEB_DISTROS=( ["ubuntu"]="Ubuntu OS") declare -rA SUPPORTED_RPM_DISTROS=( + ["anolis"]="Anolis OS" ["centos"]="CentOS" ["fedora"]="Fedora" ["openEuler"]="OpenEuler" diff --git a/test/integration/lib.sh b/test/integration/lib.sh index 961e58883..036391b5a 100644 --- a/test/integration/lib.sh +++ b/test/integration/lib.sh @@ -120,6 +120,18 @@ kpatch_photon_dependencies() fi } +kpatch_anolis_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-debuginfo-${kernel_version%.*}"\ + "kernel-devel-${kernel_version%.*}" +} + kpatch_dependencies() { # shellcheck disable=SC1091 From a78bc444f313c9da3ea14556167a86f613016f9f Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Thu, 29 Feb 2024 15:20:53 -0500 Subject: [PATCH 07/34] kmod: maintain syscall metadata sections in kpatch syscall macros The KPATCH_SYSCALL_DEFINEn macros in kpatch-syscall.h do not provide the same syscall metadata (saved in the __syscalls_metadata and _ftrace_events ELF sections) as the kernel. These same macros also instruct kpatch-build to ignore changes to these sections. This works fine as long as there are other unmodified syscalls present in the object file. However, if not, the kpatch syscall macros may result in either metadata ELF sections not appearing in the patched object file. The create-diff-object program expects to encounter any ELF section that has been marked by KPATCH_IGNORE_SECTION in the patched object file. To avoid this limitation, create dummy __syscalls_metadata and _ftrace_events entries for the kpatch-modified syscall. The specific values shouldn't matter since their sections will still be marked with KPATCH_IGNORE_SECTION and now their presence will be guarenteed for create-diff-object. Closes: #1375 ("kpatch-build error when modifying an object file's only syscall") Signed-off-by: Joe Lawrence --- kmod/patch/kpatch-syscall.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/kmod/patch/kpatch-syscall.h b/kmod/patch/kpatch-syscall.h index 3ff9189f6..ec8d24d53 100644 --- a/kmod/patch/kpatch-syscall.h +++ b/kmod/patch/kpatch-syscall.h @@ -15,14 +15,24 @@ * 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. - + * caller __x64_sys##name() function, which has an fentry hook. Since the + * kpatch versions do not provide SYSCALL_METADATA, specifically entries in the + * __syscalls_metadata and _ftrace_events sections, provide dummy values in + * these sections and instruct kpatch-build to ignore changes to them. + * * 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 \ +#define KPATCH_SYSCALL_METADATA(sname) \ + static struct syscall_metadata __used \ + __section("__syscalls_metadata") \ + *__p_syscall_meta_##sname = NULL; \ KPATCH_IGNORE_SECTION("__syscalls_metadata"); \ + \ + static struct trace_event_call __used \ + __section("_ftrace_events") \ + *__event_enter_##sname = NULL; \ KPATCH_IGNORE_SECTION("_ftrace_events") #define KPATCH_SYSCALL_DEFINE1(name, ...) KPATCH_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) @@ -33,7 +43,7 @@ #define KPATCH_SYSCALL_DEFINE6(name, ...) KPATCH_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) #define KPATCH_SYSCALL_DEFINEx(x, sname, ...) \ - KPATCH_IGNORE_SYSCALL_SECTIONS; \ + KPATCH_SYSCALL_METADATA(sname); \ __KPATCH_SYSCALL_DEFINEx(x, sname, __VA_ARGS__) #ifdef CONFIG_X86_64 From 776056a87775903f8ea60d15f7aaf42b2da25201 Mon Sep 17 00:00:00 2001 From: Longjun Luo Date: Mon, 18 Mar 2024 23:57:09 +0800 Subject: [PATCH 08/34] kpatch-build: add support for OpenCloudOS OpenCloudOS is a centos-like Linux distribution. I test kpatch in OpenCloudOS V8 and V9. It works well in V9. But v8 itself has two problems: 1. no available epol repo, so kpatch can't install ccache. 2. executing 'uname -r' can't get an accurate kernel version. Both problems have been notified to the OpenCloudOS community. After they fix these problems, kpatch will work well in all versions. Signed-off-by: Longjun Luo --- kpatch-build/kpatch-build | 5 ++++- test/integration/lib.sh | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 6bdad3bb3..7ae128c5b 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -71,6 +71,7 @@ declare -rA SUPPORTED_DEB_DISTROS=( ["ubuntu"]="Ubuntu OS") declare -rA SUPPORTED_RPM_DISTROS=( + ["opencloudos"]="OpenCloudOS" ["anolis"]="Anolis OS" ["centos"]="CentOS" ["fedora"]="Fedora" @@ -994,6 +995,8 @@ else # 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 + elif [[ "$DISTRO" = opencloudos ]]; then + mv "$RPMTOPDIR"/BUILD/kernel-*/kernel-* "$KERNEL_SRCDIR" 2>&1 | logger || die elif [[ "$DISTRO" = photon ]]; then # Photon has some files that are copied over during the build section of the spec file (instead of prep) # These change occasionally, so check they exist before copying @@ -1022,7 +1025,7 @@ else echo "$ARCHVERSION" > "$VERSIONFILE" || die - if [[ "$DISTRO" = openEuler ]]; then + if [[ "$DISTRO" = openEuler ]] || [[ "$DISTRO" = opencloudos ]]; then [[ -z "$CONFIGFILE" ]] && CONFIGFILE="/boot/config-${ARCHVERSION}" elif [[ "$DISTRO" = photon ]]; then [[ -z "$CONFIGFILE" ]] && CONFIGFILE="$KERNEL_SRCDIR/$SRC_CFG" diff --git a/test/integration/lib.sh b/test/integration/lib.sh index 036391b5a..84a6dc80c 100644 --- a/test/integration/lib.sh +++ b/test/integration/lib.sh @@ -120,6 +120,19 @@ kpatch_photon_dependencies() fi } +kpatch_opencloudos_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 dnf-utils \ + rpm-build dnf-plugins-core python3-devel openssl-devel ncurses-devel elfutils-libelf-devel + sudo yum install -y "kernel-debuginfo-${kernel_version%.*}"\ + "kernel-devel-${kernel_version%.*}" + sudo yum-builddep -y "kernel-${kernel_version}*" +} + kpatch_anolis_dependencies() { local kernel_version From 936afb932b8a5dec6e8f6eaeb5d1baeb5f5fa561 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 22 Jan 2024 16:15:12 -0500 Subject: [PATCH 09/34] test/integration/rhel-8.10: add kernel-4.18.0-552.el8_10 tests Signed-off-by: Ryan Sullivan --- .../rhel-8.10/bug-table-section.patch | 12 ++ .../rhel-8.10/cmdline-string-LOADED.test | 3 + .../rhel-8.10/cmdline-string.patch | 13 ++ .../rhel-8.10/data-new-LOADED.test | 3 + test/integration/rhel-8.10/data-new.patch | 20 +++ .../rhel-8.10/data-read-mostly.patch | 11 ++ .../integration/rhel-8.10/fixup-section.patch | 11 ++ .../integration/rhel-8.10/gcc-constprop.patch | 13 ++ test/integration/rhel-8.10/gcc-isra.patch | 11 ++ .../integration/rhel-8.10/gcc-mangled-3.patch | 13 ++ .../rhel-8.10/gcc-static-local-var-2.patch | 13 ++ .../rhel-8.10/gcc-static-local-var-3.patch | 19 +++ .../rhel-8.10/gcc-static-local-var-4.patch | 23 +++ .../rhel-8.10/gcc-static-local-var-4.test | 8 + .../rhel-8.10/gcc-static-local-var-5.patch | 45 +++++ .../rhel-8.10/gcc-static-local-var-6.patch | 22 +++ .../rhel-8.10/macro-callbacks.patch | 155 ++++++++++++++++++ test/integration/rhel-8.10/macro-printk.patch | 149 +++++++++++++++++ .../rhel-8.10/meminfo-init-FAIL.patch | 11 ++ .../rhel-8.10/meminfo-init2-FAIL.patch | 19 +++ .../rhel-8.10/meminfo-string-LOADED.test | 3 + .../rhel-8.10/meminfo-string.patch | 12 ++ test/integration/rhel-8.10/module-LOADED.test | 11 ++ test/integration/rhel-8.10/module.patch | 60 +++++++ test/integration/rhel-8.10/multiple.test | 7 + test/integration/rhel-8.10/new-function.patch | 25 +++ test/integration/rhel-8.10/new-globals.patch | 34 ++++ .../rhel-8.10/parainstructions-section.patch | 11 ++ .../rhel-8.10/shadow-newpid-LOADED.test | 3 + .../integration/rhel-8.10/shadow-newpid.patch | 75 +++++++++ .../rhel-8.10/smp-locks-section.patch | 13 ++ .../rhel-8.10/special-static.patch | 22 +++ .../rhel-8.10/symvers-disagreement-FAIL.patch | 24 +++ .../integration/rhel-8.10/syscall-LOADED.test | 3 + test/integration/rhel-8.10/syscall.patch | 25 +++ .../rhel-8.10/tracepoints-section.patch | 13 ++ .../rhel-8.10/warn-detect-FAIL.patch | 9 + 37 files changed, 924 insertions(+) create mode 100644 test/integration/rhel-8.10/bug-table-section.patch create mode 100755 test/integration/rhel-8.10/cmdline-string-LOADED.test create mode 100644 test/integration/rhel-8.10/cmdline-string.patch create mode 100755 test/integration/rhel-8.10/data-new-LOADED.test create mode 100644 test/integration/rhel-8.10/data-new.patch create mode 100644 test/integration/rhel-8.10/data-read-mostly.patch create mode 100644 test/integration/rhel-8.10/fixup-section.patch create mode 100644 test/integration/rhel-8.10/gcc-constprop.patch create mode 100644 test/integration/rhel-8.10/gcc-isra.patch create mode 100644 test/integration/rhel-8.10/gcc-mangled-3.patch create mode 100644 test/integration/rhel-8.10/gcc-static-local-var-2.patch create mode 100644 test/integration/rhel-8.10/gcc-static-local-var-3.patch create mode 100644 test/integration/rhel-8.10/gcc-static-local-var-4.patch create mode 100755 test/integration/rhel-8.10/gcc-static-local-var-4.test create mode 100644 test/integration/rhel-8.10/gcc-static-local-var-5.patch create mode 100644 test/integration/rhel-8.10/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-8.10/macro-callbacks.patch create mode 100644 test/integration/rhel-8.10/macro-printk.patch create mode 100644 test/integration/rhel-8.10/meminfo-init-FAIL.patch create mode 100644 test/integration/rhel-8.10/meminfo-init2-FAIL.patch create mode 100755 test/integration/rhel-8.10/meminfo-string-LOADED.test create mode 100644 test/integration/rhel-8.10/meminfo-string.patch create mode 100755 test/integration/rhel-8.10/module-LOADED.test create mode 100644 test/integration/rhel-8.10/module.patch create mode 100755 test/integration/rhel-8.10/multiple.test create mode 100644 test/integration/rhel-8.10/new-function.patch create mode 100644 test/integration/rhel-8.10/new-globals.patch create mode 100644 test/integration/rhel-8.10/parainstructions-section.patch create mode 100755 test/integration/rhel-8.10/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-8.10/shadow-newpid.patch create mode 100644 test/integration/rhel-8.10/smp-locks-section.patch create mode 100644 test/integration/rhel-8.10/special-static.patch create mode 100644 test/integration/rhel-8.10/symvers-disagreement-FAIL.patch create mode 100755 test/integration/rhel-8.10/syscall-LOADED.test create mode 100644 test/integration/rhel-8.10/syscall.patch create mode 100644 test/integration/rhel-8.10/tracepoints-section.patch create mode 100644 test/integration/rhel-8.10/warn-detect-FAIL.patch diff --git a/test/integration/rhel-8.10/bug-table-section.patch b/test/integration/rhel-8.10/bug-table-section.patch new file mode 100644 index 000000000..a271c7eec --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/proc_sysctl.c 2024-04-18 14:12:03.887374205 -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.10/cmdline-string-LOADED.test b/test/integration/rhel-8.10/cmdline-string-LOADED.test new file mode 100755 index 000000000..a8e0a0817 --- /dev/null +++ b/test/integration/rhel-8.10/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.10/cmdline-string.patch b/test/integration/rhel-8.10/cmdline-string.patch new file mode 100644 index 000000000..d524a001b --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/cmdline.c 2024-04-18 14:12:10.803359173 -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.10/data-new-LOADED.test b/test/integration/rhel-8.10/data-new-LOADED.test new file mode 100755 index 000000000..9f25744e6 --- /dev/null +++ b/test/integration/rhel-8.10/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.10/data-new.patch b/test/integration/rhel-8.10/data-new.patch new file mode 100644 index 000000000..5ffd55260 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/meminfo.c 2024-04-18 14:12:13.626353038 -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.10/data-read-mostly.patch b/test/integration/rhel-8.10/data-read-mostly.patch new file mode 100644 index 000000000..e2625cf72 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.639374744 -0400 ++++ src/net/core/dev.c 2024-04-18 14:12:15.656348626 -0400 +@@ -5469,6 +5469,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.10/fixup-section.patch b/test/integration/rhel-8.10/fixup-section.patch new file mode 100644 index 000000000..6478b06a3 --- /dev/null +++ b/test/integration/rhel-8.10/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2024-04-18 14:12:03.462375128 -0400 ++++ src/fs/readdir.c 2024-04-18 14:12:17.665344260 -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.10/gcc-constprop.patch b/test/integration/rhel-8.10/gcc-constprop.patch new file mode 100644 index 000000000..0b7f6a0bd --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.614374798 -0400 ++++ src/kernel/time/timekeeping.c 2024-04-18 14:12:19.675339891 -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.10/gcc-isra.patch b/test/integration/rhel-8.10/gcc-isra.patch new file mode 100644 index 000000000..c4f0d0ca1 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/proc_sysctl.c 2024-04-18 14:12:21.710335469 -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.10/gcc-mangled-3.patch b/test/integration/rhel-8.10/gcc-mangled-3.patch new file mode 100644 index 000000000..0b20ad281 --- /dev/null +++ b/test/integration/rhel-8.10/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2024-04-18 14:12:03.629374765 -0400 ++++ src/mm/slub.c 2024-04-18 14:12:23.903330702 -0400 +@@ -6192,6 +6192,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.10/gcc-static-local-var-2.patch b/test/integration/rhel-8.10/gcc-static-local-var-2.patch new file mode 100644 index 000000000..a27a28a6b --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.628374767 -0400 ++++ src/mm/mmap.c 2024-04-18 14:12:25.958326236 -0400 +@@ -1691,6 +1691,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.10/gcc-static-local-var-3.patch b/test/integration/rhel-8.10/gcc-static-local-var-3.patch new file mode 100644 index 000000000..1b4bbb087 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.612374802 -0400 ++++ src/kernel/reboot.c 2024-04-18 14:12:27.985321831 -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.10/gcc-static-local-var-4.patch b/test/integration/rhel-8.10/gcc-static-local-var-4.patch new file mode 100644 index 000000000..3c617a8b6 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.428375202 -0400 ++++ src/fs/aio.c 2024-04-18 14:12:30.010317430 -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.10/gcc-static-local-var-4.test b/test/integration/rhel-8.10/gcc-static-local-var-4.test new file mode 100755 index 000000000..e085f9345 --- /dev/null +++ b/test/integration/rhel-8.10/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.10/gcc-static-local-var-5.patch b/test/integration/rhel-8.10/gcc-static-local-var-5.patch new file mode 100644 index 000000000..25d99c494 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.604374820 -0400 ++++ src/kernel/audit.c 2024-04-18 14:12:32.055312985 -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.10/gcc-static-local-var-6.patch b/test/integration/rhel-8.10/gcc-static-local-var-6.patch new file mode 100644 index 000000000..0801d859b --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.648374724 -0400 ++++ src/net/ipv6/netfilter.c 2024-04-18 14:12:34.140308454 -0400 +@@ -93,6 +93,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) + { +@@ -106,6 +108,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.10/macro-callbacks.patch b/test/integration/rhel-8.10/macro-callbacks.patch new file mode 100644 index 000000000..b90acb9b9 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.003376126 -0400 ++++ src/drivers/input/joydev.c 2024-04-18 14:12:36.165304053 -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 2024-04-18 14:12:03.007376117 -0400 ++++ src/drivers/input/misc/pcspkr.c 2024-04-18 14:12:36.166304050 -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 2024-04-18 14:12:03.428375202 -0400 ++++ src/fs/aio.c 2024-04-18 14:12:36.166304050 -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.10/macro-printk.patch b/test/integration/rhel-8.10/macro-printk.patch new file mode 100644 index 000000000..a58b4b726 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.644374733 -0400 ++++ src/net/ipv4/fib_frontend.c 2024-04-18 14:12:38.274299469 -0400 +@@ -798,6 +798,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) + { +@@ -819,6 +820,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 2024-04-18 14:12:03.644374733 -0400 ++++ src/net/ipv4/fib_semantics.c 2024-04-18 14:12:38.275299467 -0400 +@@ -1028,6 +1028,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) + { +@@ -1061,6 +1062,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; +@@ -1081,6 +1083,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) +@@ -1094,6 +1097,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; +@@ -1149,9 +1154,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; + +@@ -1169,6 +1176,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) { +@@ -1190,6 +1198,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"); +@@ -1228,6 +1237,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"); +@@ -1237,6 +1247,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); + +@@ -1248,6 +1259,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); +@@ -1271,6 +1283,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: +@@ -1281,6 +1294,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 2024-04-18 14:12:03.644374733 -0400 ++++ src/net/ipv4/fib_trie.c 2024-04-18 14:12:38.275299467 -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.10/meminfo-init-FAIL.patch b/test/integration/rhel-8.10/meminfo-init-FAIL.patch new file mode 100644 index 000000000..1b1e9b90c --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/meminfo.c 2024-04-18 14:12:42.540290197 -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.10/meminfo-init2-FAIL.patch b/test/integration/rhel-8.10/meminfo-init2-FAIL.patch new file mode 100644 index 000000000..d8d8bd0a4 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/meminfo.c 2024-04-18 14:12:40.414294818 -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.10/meminfo-string-LOADED.test b/test/integration/rhel-8.10/meminfo-string-LOADED.test new file mode 100755 index 000000000..10dc20b3d --- /dev/null +++ b/test/integration/rhel-8.10/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.10/meminfo-string.patch b/test/integration/rhel-8.10/meminfo-string.patch new file mode 100644 index 000000000..6f6f8d3e4 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/meminfo.c 2024-04-18 14:12:44.665285579 -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: ", vmalloc_nr_pages()); +- 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.10/module-LOADED.test b/test/integration/rhel-8.10/module-LOADED.test new file mode 100755 index 000000000..bed43ca52 --- /dev/null +++ b/test/integration/rhel-8.10/module-LOADED.test @@ -0,0 +1,11 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe xfs +sleep 5 +grep -q kpatch /sys/fs/xfs/stats/stats +# 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/xfs/xfs_stats.c +p" > /sys/kernel/debug/dynamic_debug/control +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.10/module.patch b/test/integration/rhel-8.10/module.patch new file mode 100644 index 000000000..4e29bca75 --- /dev/null +++ b/test/integration/rhel-8.10/module.patch @@ -0,0 +1,60 @@ +diff -Nupr src.orig/fs/xfs/xfs_stats.c src/fs/xfs/xfs_stats.c +--- src.orig/fs/xfs/xfs_stats.c 2024-04-18 14:12:03.471375109 -0400 ++++ src/fs/xfs/xfs_stats.c 2024-04-18 14:12:46.797280945 -0400 +@@ -16,6 +16,8 @@ static int counter_val(struct xfsstats _ + return val; + } + ++extern char *kpatch_string(void); ++ + int xfs_stats_format(struct xfsstats __percpu *stats, char *buf) + { + int i, j; +@@ -85,6 +87,34 @@ int xfs_stats_format(struct xfsstats __p + 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 ++ /* 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"); ++ ++{ ++ /* Test static branches: */ ++ 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); ++} ++ + return len; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2024-04-18 14:12:03.660374698 -0400 ++++ src/net/netlink/af_netlink.c 2024-04-18 14:12:46.798280943 -0400 +@@ -2888,4 +2888,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "kpatch"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.10/multiple.test b/test/integration/rhel-8.10/multiple.test new file mode 100755 index 000000000..7e4b3525c --- /dev/null +++ b/test/integration/rhel-8.10/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.10/new-function.patch b/test/integration/rhel-8.10/new-function.patch new file mode 100644 index 000000000..b4843a404 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.376375315 -0400 ++++ src/drivers/tty/n_tty.c 2024-04-18 14:12:48.938276292 -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.10/new-globals.patch b/test/integration/rhel-8.10/new-globals.patch new file mode 100644 index 000000000..3df314297 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/cmdline.c 2024-04-18 14:12:51.049271704 -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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/meminfo.c 2024-04-18 14:12:51.049271704 -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.10/parainstructions-section.patch b/test/integration/rhel-8.10/parainstructions-section.patch new file mode 100644 index 000000000..6ff3cc7d1 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/generic.c 2024-04-18 14:12:53.192267047 -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.10/shadow-newpid-LOADED.test b/test/integration/rhel-8.10/shadow-newpid-LOADED.test new file mode 100755 index 000000000..c07d11205 --- /dev/null +++ b/test/integration/rhel-8.10/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.10/shadow-newpid.patch b/test/integration/rhel-8.10/shadow-newpid.patch new file mode 100644 index 000000000..9e8e60db0 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.460375133 -0400 ++++ src/fs/proc/array.c 2024-04-18 14:12:55.335262389 -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 2024-04-18 14:12:03.608374811 -0400 ++++ src/kernel/exit.c 2024-04-18 14:12:55.335262389 -0400 +@@ -704,6 +704,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; +@@ -804,6 +805,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 2024-04-18 14:12:03.608374811 -0400 ++++ src/kernel/fork.c 2024-04-18 14:12:55.336262387 -0400 +@@ -2475,6 +2475,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, +@@ -2487,6 +2488,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 +@@ -2513,6 +2516,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.10/smp-locks-section.patch b/test/integration/rhel-8.10/smp-locks-section.patch new file mode 100644 index 000000000..780fc0897 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.380375306 -0400 ++++ src/drivers/tty/tty_buffer.c 2024-04-18 14:12:57.481257725 -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.10/special-static.patch b/test/integration/rhel-8.10/special-static.patch new file mode 100644 index 000000000..33256c0cd --- /dev/null +++ b/test/integration/rhel-8.10/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2024-04-18 14:12:03.608374811 -0400 ++++ src/kernel/fork.c 2024-04-18 14:12:59.608253102 -0400 +@@ -1661,10 +1661,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.10/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.10/symvers-disagreement-FAIL.patch new file mode 100644 index 000000000..b59bb9b30 --- /dev/null +++ b/test/integration/rhel-8.10/symvers-disagreement-FAIL.patch @@ -0,0 +1,24 @@ +diff -Nupr src.orig/drivers/base/core.c src/drivers/base/core.c +--- src.orig/drivers/base/core.c 2024-04-18 14:12:02.743376691 -0400 ++++ src/drivers/base/core.c 2024-04-18 14:13:01.732248486 -0400 +@@ -34,6 +34,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 2024-04-18 14:12:03.383375300 -0400 ++++ src/drivers/usb/core/usb.c 2024-04-18 14:13:01.733248484 -0400 +@@ -769,6 +769,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.10/syscall-LOADED.test b/test/integration/rhel-8.10/syscall-LOADED.test new file mode 100755 index 000000000..3a2fd88e0 --- /dev/null +++ b/test/integration/rhel-8.10/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/rhel-8.10/syscall.patch b/test/integration/rhel-8.10/syscall.patch new file mode 100644 index 000000000..0de0b862b --- /dev/null +++ b/test/integration/rhel-8.10/syscall.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2024-04-18 14:12:03.613374800 -0400 ++++ src/kernel/sys.c 2024-04-18 14:13:03.847243889 -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.10/tracepoints-section.patch b/test/integration/rhel-8.10/tracepoints-section.patch new file mode 100644 index 000000000..49d39e5a1 --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:03.614374798 -0400 ++++ src/kernel/time/timer.c 2024-04-18 14:13:05.967239282 -0400 +@@ -1988,6 +1988,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.10/warn-detect-FAIL.patch b/test/integration/rhel-8.10/warn-detect-FAIL.patch new file mode 100644 index 000000000..c6261665d --- /dev/null +++ b/test/integration/rhel-8.10/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 2024-04-18 14:12:02.707376769 -0400 ++++ src/arch/x86/kvm/x86.c 2024-04-18 14:13:08.122234598 -0400 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * From e86e0cf667c9f5018fccccd0f7dab2d8326fecee Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 22 Jan 2024 16:23:59 -0500 Subject: [PATCH 10/34] test/integration/rhel-9.4: add kernel-5.14.0-427.13.1.el9_4 tests Signed-off-by: Ryan Sullivan --- .../integration/rhel-9.4/data-new-LOADED.test | 3 + test/integration/rhel-9.4/data-new.patch | 20 +++ .../rhel-9.4/gcc-static-local-var-6.patch | 22 +++ .../rhel-9.4/macro-callbacks.patch | 155 ++++++++++++++++++ test/integration/rhel-9.4/module-LOADED.test | 13 ++ test/integration/rhel-9.4/module.patch | 64 ++++++++ test/integration/rhel-9.4/multiple.test | 7 + test/integration/rhel-9.4/new-function.patch | 25 +++ test/integration/rhel-9.4/new-globals.patch | 34 ++++ .../rhel-9.4/shadow-newpid-LOADED.test | 3 + test/integration/rhel-9.4/shadow-newpid.patch | 75 +++++++++ .../integration/rhel-9.4/special-static.patch | 22 +++ test/integration/rhel-9.4/syscall-LOADED.test | 3 + test/integration/rhel-9.4/syscall.patch | 20 +++ .../rhel-9.4/warn-detect-FAIL.patch | 9 + 15 files changed, 475 insertions(+) create mode 100755 test/integration/rhel-9.4/data-new-LOADED.test create mode 100644 test/integration/rhel-9.4/data-new.patch create mode 100644 test/integration/rhel-9.4/gcc-static-local-var-6.patch create mode 100644 test/integration/rhel-9.4/macro-callbacks.patch create mode 100755 test/integration/rhel-9.4/module-LOADED.test create mode 100644 test/integration/rhel-9.4/module.patch create mode 100755 test/integration/rhel-9.4/multiple.test create mode 100644 test/integration/rhel-9.4/new-function.patch create mode 100644 test/integration/rhel-9.4/new-globals.patch create mode 100755 test/integration/rhel-9.4/shadow-newpid-LOADED.test create mode 100644 test/integration/rhel-9.4/shadow-newpid.patch create mode 100644 test/integration/rhel-9.4/special-static.patch create mode 100755 test/integration/rhel-9.4/syscall-LOADED.test create mode 100644 test/integration/rhel-9.4/syscall.patch create mode 100644 test/integration/rhel-9.4/warn-detect-FAIL.patch diff --git a/test/integration/rhel-9.4/data-new-LOADED.test b/test/integration/rhel-9.4/data-new-LOADED.test new file mode 100755 index 000000000..9f25744e6 --- /dev/null +++ b/test/integration/rhel-9.4/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-9.4/data-new.patch b/test/integration/rhel-9.4/data-new.patch new file mode 100644 index 000000000..322a4ca56 --- /dev/null +++ b/test/integration/rhel-9.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 2024-04-18 11:19:38.154638374 -0400 ++++ src/fs/proc/meminfo.c 2024-04-18 11:19:39.092636204 -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; +@@ -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); + + #ifdef CONFIG_UNACCEPTED_MEMORY + show_val_kb(m, "Unaccepted: ", diff --git a/test/integration/rhel-9.4/gcc-static-local-var-6.patch b/test/integration/rhel-9.4/gcc-static-local-var-6.patch new file mode 100644 index 000000000..fe3394dd6 --- /dev/null +++ b/test/integration/rhel-9.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 2024-04-18 11:19:38.428637740 -0400 ++++ src/net/ipv6/netfilter.c 2024-04-18 11:19:46.066620071 -0400 +@@ -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/rhel-9.4/macro-callbacks.patch b/test/integration/rhel-9.4/macro-callbacks.patch new file mode 100644 index 000000000..9b82049a6 --- /dev/null +++ b/test/integration/rhel-9.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 2024-04-18 11:19:37.635639575 -0400 ++++ src/drivers/input/joydev.c 2024-04-18 11:19:48.560614301 -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 2024-04-18 11:19:37.639639566 -0400 ++++ src/drivers/input/misc/pcspkr.c 2024-04-18 11:19:48.561614299 -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 2024-04-18 11:19:38.122638448 -0400 ++++ src/fs/aio.c 2024-04-18 11:19:48.561614299 -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.4/module-LOADED.test b/test/integration/rhel-9.4/module-LOADED.test new file mode 100755 index 000000000..72bb85266 --- /dev/null +++ b/test/integration/rhel-9.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-9.4/module.patch b/test/integration/rhel-9.4/module.patch new file mode 100644 index 000000000..e7c24d082 --- /dev/null +++ b/test/integration/rhel-9.4/module.patch @@ -0,0 +1,64 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2024-04-18 11:19:38.144638397 -0400 ++++ src/fs/nfsd/export.c 2024-04-18 11:19:51.106608412 -0400 +@@ -1342,6 +1342,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; +@@ -1349,12 +1353,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 2024-04-18 11:19:38.442637708 -0400 ++++ src/net/netlink/af_netlink.c 2024-04-18 11:19:51.107608409 -0400 +@@ -2944,4 +2944,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.4/multiple.test b/test/integration/rhel-9.4/multiple.test new file mode 100755 index 000000000..7e4b3525c --- /dev/null +++ b/test/integration/rhel-9.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-9.4/new-function.patch b/test/integration/rhel-9.4/new-function.patch new file mode 100644 index 000000000..f88ad1c8f --- /dev/null +++ b/test/integration/rhel-9.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 2024-04-18 11:19:38.071638566 -0400 ++++ src/drivers/tty/n_tty.c 2024-04-18 11:19:53.617602603 -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.4/new-globals.patch b/test/integration/rhel-9.4/new-globals.patch new file mode 100644 index 000000000..3462fcaee --- /dev/null +++ b/test/integration/rhel-9.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 2024-04-18 11:19:38.153638377 -0400 ++++ src/fs/proc/cmdline.c 2024-04-18 11:19:56.139596769 -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 2024-04-18 11:19:38.154638374 -0400 ++++ src/fs/proc/meminfo.c 2024-04-18 11:19:56.140596766 -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.4/shadow-newpid-LOADED.test b/test/integration/rhel-9.4/shadow-newpid-LOADED.test new file mode 100755 index 000000000..c07d11205 --- /dev/null +++ b/test/integration/rhel-9.4/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-9.4/shadow-newpid.patch b/test/integration/rhel-9.4/shadow-newpid.patch new file mode 100644 index 000000000..fc36c22b2 --- /dev/null +++ b/test/integration/rhel-9.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 2024-04-18 11:19:38.153638377 -0400 ++++ src/fs/proc/array.c 2024-04-18 11:19:58.692590862 -0400 +@@ -403,12 +403,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 2024-04-18 11:19:38.381637849 -0400 ++++ src/kernel/exit.c 2024-04-18 11:19:58.692590862 -0400 +@@ -734,6 +734,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; +@@ -797,6 +798,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 2024-04-18 11:19:38.381637849 -0400 ++++ src/kernel/fork.c 2024-04-18 11:19:58.693590860 -0400 +@@ -2639,6 +2639,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; +@@ -2647,6 +2648,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 +@@ -2686,6 +2689,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.4/special-static.patch b/test/integration/rhel-9.4/special-static.patch new file mode 100644 index 000000000..370c3f373 --- /dev/null +++ b/test/integration/rhel-9.4/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2024-04-18 11:19:38.381637849 -0400 ++++ src/kernel/fork.c 2024-04-18 11:20:01.214585028 -0400 +@@ -1711,10 +1711,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.4/syscall-LOADED.test b/test/integration/rhel-9.4/syscall-LOADED.test new file mode 100755 index 000000000..3a2fd88e0 --- /dev/null +++ b/test/integration/rhel-9.4/syscall-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +uname -s | grep -q kpatch diff --git a/test/integration/rhel-9.4/syscall.patch b/test/integration/rhel-9.4/syscall.patch new file mode 100644 index 000000000..0f8139f25 --- /dev/null +++ b/test/integration/rhel-9.4/syscall.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2024-04-18 11:19:38.387637835 -0400 ++++ src/kernel/sys.c 2024-04-18 11:20:03.760579138 -0400 +@@ -1284,13 +1284,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.4/warn-detect-FAIL.patch b/test/integration/rhel-9.4/warn-detect-FAIL.patch new file mode 100644 index 000000000..b797905f1 --- /dev/null +++ b/test/integration/rhel-9.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 2024-04-18 11:19:37.300640350 -0400 ++++ src/arch/x86/kvm/x86.c 2024-04-18 11:20:06.339573172 -0400 +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-only ++ + /* + * Kernel-based Virtual Machine driver for Linux + * From 59a0f2414106644e298dd021b97fe6174efdd27c Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Fri, 22 Mar 2024 14:46:54 -0400 Subject: [PATCH 11/34] kpatch-test: add dmesg entry at start of test If the kernel log is empty prior to running the integration tests, the following confusing status may be reported: ... ERROR: dmesg overflow, try increasing kernel log buffer size SUCCESS This occurs because the script can't find an empty dmesg entry when the tests are complete. Copy the upstream kernel livepatching kselftests to fix this by logging a canary message at the beginning of the integration tests. This will ensure a "real" message than can be found at the end. Fixes: de1d0c6e08e6 ("kpatch-test: don't clear dmesg during test") Signed-off-by: Joe Lawrence --- test/integration/kpatch-test | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/integration/kpatch-test b/test/integration/kpatch-test index 688ab1dea..33a8ba749 100755 --- a/test/integration/kpatch-test +++ b/test/integration/kpatch-test @@ -299,12 +299,13 @@ run_combined_test() { # save existing dmesg so we can detect new content save_dmesg() { - SAVED_DMESG="$(dmesg | tail -n1)" + SAVED_DMESG="kpatch-test timestamp: $(date --rfc-3339=ns)" + echo "$SAVED_DMESG" > /dev/kmsg } # 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 + if ! dmesg --notime | awk -v last="$SAVED_DMESG" 'p; $0 == last{p=1} END {exit !p}'; then error "dmesg overflow, try increasing kernel log buffer size" fi } From 2060c1fd067b7ce3715a1cde46bb85ed6875cdab Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Fri, 29 Mar 2024 11:08:31 -0400 Subject: [PATCH 12/34] kpatch-build: handle paravirt absence in Linux v6.8+ Upstream kernel commit f7af6977621a ("x86/paravirt: Remove no longer needed paravirt patching code") v6.8+ removed the .parainstructions section and its paravirt_patch_site struct. Therefore this checks the kernel version and does not export the struct size if the kernel version is >= v6.8.0, avoiding the code path for it in create-diff-object.c entirely. Fixes: https://github.com/dynup/kpatch/issues/1380 Signed-off-by: Ryan Sullivan --- kpatch-build/kpatch-build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 7ae128c5b..17bf1a482 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -358,7 +358,7 @@ find_special_section_data() { "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 + [[ -n "$CONFIG_PARAVIRT" ]] && ! kernel_version_gte 6.8.0 && check[p]=true # paravirt_patch_site ;; "ppc64le") check[f]=true # fixup_entry From b857c4e1240c7ae63e9383b5ec68572713b00b83 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 30 Apr 2024 15:27:03 -0400 Subject: [PATCH 13/34] kpatch-build: demote CONFIG_LD_ORPHAN_WARN_LEVEL Upstream kernel v6.1+ commit linux@e1789d7c752e ("kbuild: upgrade the orphan section warning to an error if CONFIG_WERROR is set") and CONFIG_WERROR will result in failed kernel builds due to the linker reporting tons of "unplaced orphan section `.text.` " errors. Workaround this by temporarily demoting such errors in the top-level kernel Makefile. Reported-and-tested-by: Zhijun Wang Closes: #1391 ("CONFIG_WERROR=y and CONFIG_LD_ORPHAN_WARN_LEVEL="error" break kpatch-build") Signed-off-by: Joe Lawrence --- kpatch-build/kpatch-build | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 17bf1a482..ac1db68be 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -186,6 +186,7 @@ cleanup() { # restore any files that were modified for the build [[ -e "$TEMPDIR/vmlinux" ]] && mv -f "$TEMPDIR/vmlinux" "$KERNEL_SRCDIR/" + [[ -e "$TEMPDIR/Makefile" ]] && mv -f "$TEMPDIR/Makefile" "$KERNEL_SRCDIR/" [[ -e "$TEMPDIR/link-vmlinux.sh" ]] && mv -f "$TEMPDIR/link-vmlinux.sh" "$KERNEL_SRCDIR/scripts" [[ -e "$TEMPDIR/Makefile.modfinal" ]] && mv -f "$TEMPDIR/Makefile.modfinal" "$KERNEL_SRCDIR/scripts" [[ -e "$TEMPDIR/setlocalversion" ]] && mv -f "$TEMPDIR/setlocalversion" "$KERNEL_SRCDIR/scripts" @@ -1174,6 +1175,14 @@ if [[ -n "$CONFIG_DEBUG_INFO_BTF" ]]; then fi fi +# CONFIG_LD_ORPHAN_WARN_LEVEL="error" will fail kernel builds with +# --ffunction-sections with lots of "ld: error: unplaced orphan section" +# errors. Temporarily demote to "warn"ings in the kernel Makefile. +if [[ "$CONFIG_LD_ORPHAN_WARN_LEVEL" == "error" ]]; then + cp -f "$KERNEL_SRCDIR/Makefile" "$TEMPDIR/Makefile" || die + sed -i 's/--orphan-handling=[$](CONFIG_LD_ORPHAN_WARN_LEVEL)/--orphan-handling="warn"/g' "$KERNEL_SRCDIR/Makefile" || die +fi + if [[ -n "$CONFIG_CC_IS_CLANG" ]]; then echo "WARNING: Clang support is experimental" fi From 4003ca5c6842293e4e369bd5b4bff4e9986ac9eb Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 30 Apr 2024 15:22:59 -0400 Subject: [PATCH 14/34] kpatch-build: cleanup kernel file backup/restore Temporarily editing kernel tree sources has become a recurring requirement in kpatch-build. Pull the saving/restoring of these files into a common function helpers to standardize the pattern. Reported-and-tested-by: Zhijun Wang Signed-off-by: Joe Lawrence --- kpatch-build/kpatch-build | 46 +++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index ac1db68be..f1a11bdde 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -181,15 +181,37 @@ remove_patches() { [[ -d "$BUILDDIR/.git" ]] && (cd "$BUILDDIR" && git update-index -q --refresh) } +# List of kernel tree files that kpatch-build backed up to +# $KERNEL_BACKUPDIR before modification +declare -a BACKUP_KERNEL_FILES +KERNEL_BACKUPDIR="$TEMPDIR/kernel-backup" + +# Save a kernel file (i.e. "scripts/Makefile.modfinal") +backup_kernel_file() { + local kernel_path="$1" + + if [[ ! -e "$KERNEL_SRCDIR/$kernel_path" ]]; then + die "Kernel path not found: $KERNEL_SRCDIR/$kernel_path" + fi + + mkdir --parents "$KERNEL_BACKUPDIR/$(dirname "$kernel_path")" || die + cp --force "$KERNEL_SRCDIR/$kernel_path" "$KERNEL_BACKUPDIR/$kernel_path" || die + + BACKUP_KERNEL_FILES+=("$kernel_path") +} + +# Restore all kernel files backed up by backup_kernel_file() +restore_kernel_files() { + for kernel_path in "${BACKUP_KERNEL_FILES[@]}"; do + if ! mv --force "$KERNEL_BACKUPDIR/$kernel_path" "$KERNEL_SRCDIR/$kernel_path"; then + warn "Couldn't restore kernel path: $kernel_path" + fi + done +} + cleanup() { remove_patches - - # restore any files that were modified for the build - [[ -e "$TEMPDIR/vmlinux" ]] && mv -f "$TEMPDIR/vmlinux" "$KERNEL_SRCDIR/" - [[ -e "$TEMPDIR/Makefile" ]] && mv -f "$TEMPDIR/Makefile" "$KERNEL_SRCDIR/" - [[ -e "$TEMPDIR/link-vmlinux.sh" ]] && mv -f "$TEMPDIR/link-vmlinux.sh" "$KERNEL_SRCDIR/scripts" - [[ -e "$TEMPDIR/Makefile.modfinal" ]] && mv -f "$TEMPDIR/Makefile.modfinal" "$KERNEL_SRCDIR/scripts" - [[ -e "$TEMPDIR/setlocalversion" ]] && mv -f "$TEMPDIR/setlocalversion" "$KERNEL_SRCDIR/scripts" + restore_kernel_files [[ "$DEBUG" -eq 0 ]] && rm -rf "$TEMPDIR" rm -rf "$RPMTOPDIR" @@ -933,7 +955,7 @@ if [[ -n "$USERSRCDIR" ]]; then # save original vmlinux before it gets overwritten by sourcedir build if [[ "$VMLINUX" -ef "$KERNEL_SRCDIR"/vmlinux ]]; then - cp -f "$VMLINUX" "$TEMPDIR/vmlinux" || die + backup_kernel_file "vmlinux" VMLINUX="$TEMPDIR/vmlinux" fi elif [[ -n "$OOT_MODULE" ]]; then @@ -1103,7 +1125,7 @@ fi # changes to the source. if [[ -n "$USERSRCDIR" && -e "$KERNEL_SRCDIR/.git" ]]; then cd "$KERNEL_SRCDIR" || die - cp -f scripts/setlocalversion "$TEMPDIR" || die + backup_kernel_file "scripts/setlocalversion" LOCALVERSION="$(make kernelversion)" LOCALVERSION="$(KERNELVERSION="$LOCALVERSION" ./scripts/setlocalversion)" [[ -n "$LOCALVERSION" ]] || die "setlocalversion failed" @@ -1166,11 +1188,11 @@ fi # 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 + backup_kernel_file "scripts/link-vmlinux.sh" 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 + backup_kernel_file "scripts/Makefile.modfinal" sed -i 's/CONFIG_DEBUG_INFO_BTF_MODULES/DISABLED_FOR_KPATCH_BUILD/g' "$KERNEL_SRCDIR"/scripts/Makefile.modfinal || die fi fi @@ -1179,7 +1201,7 @@ fi # --ffunction-sections with lots of "ld: error: unplaced orphan section" # errors. Temporarily demote to "warn"ings in the kernel Makefile. if [[ "$CONFIG_LD_ORPHAN_WARN_LEVEL" == "error" ]]; then - cp -f "$KERNEL_SRCDIR/Makefile" "$TEMPDIR/Makefile" || die + backup_kernel_file "Makefile" sed -i 's/--orphan-handling=[$](CONFIG_LD_ORPHAN_WARN_LEVEL)/--orphan-handling="warn"/g' "$KERNEL_SRCDIR/Makefile" || die fi From c8e6949a7860d662e324da24ed783ac7b8eb2683 Mon Sep 17 00:00:00 2001 From: Ziwei Mao Date: Fri, 10 May 2024 17:27:55 +0000 Subject: [PATCH 15/34] Update spec file Fix formatting issue and date error --- contrib/kpatch.spec | 78 ++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/contrib/kpatch.spec b/contrib/kpatch.spec index a5a3880cc..cd385da86 100644 --- a/contrib/kpatch.spec +++ b/contrib/kpatch.spec @@ -112,41 +112,41 @@ rm -rf %{buildroot} - Added RHEL-8.8 and 9.2 integration tests * Wed Mar 8 2023 Joe Lawrence - 0.9.8 -* Clang fix ups from Pete Swain -* Support for gcc-12 -* Support for Linux 5.19 -* Added RHEL-8.7 and 9.1 integration tests -* Fixed __UNIQUE_ID() variable correlation -* Improved handling of unsupported static calls - -* 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 +- Clang fix ups from Pete Swain +- Support for gcc-12 +- Support for Linux 5.19 +- Added RHEL-8.7 and 9.1 integration tests +- Fixed __UNIQUE_ID() variable correlation +- Improved handling of unsupported static calls + +* Wed Sep 14 2022 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 2022 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 2021 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() @@ -155,7 +155,7 @@ rm -rf %{buildroot} - kmod/patch: clean only rebuildable objs - kpatch-build: save environment varibles to file -* Wed Aug 25 Yannick Cote - 0.9.4 +* Wed Aug 25 2021 Yannick Cote - 0.9.4 - Support for multiple source files - Makefile tweaks for handling non-replace kpatch building - Support CONFIG_PRINTK_INDEX @@ -168,7 +168,7 @@ rm -rf %{buildroot} - kpatch: Sync signal subcmd usage output with manpage - fixes for the out-of-range relocation check -* Tue Apr 20 Yannick Cote - 0.9.3 +* Tue Apr 20 2021 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 @@ -269,7 +269,7 @@ rm -rf %{buildroot} - 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 +* Sun 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 @@ -294,7 +294,7 @@ rm -rf %{buildroot} - KASLR support. - Many other bug fixes and improvements. -* Thu Oct 11 2016 Jessica Yu - 0.3.4 +* Tue Oct 11 2016 Jessica Yu - 0.3.4 - bump version to 0.3.4 * Fri Aug 19 2016 Josh Poimboeuf - 0.3.3 From fc870702381f5051a309514f84a5e7ce6da38cc7 Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Wed, 6 Oct 2021 12:41:16 -0700 Subject: [PATCH 16/34] kpatch-build: Add sym->has_func_profiling support for aarch64 The "has_function_profiling" support field in the symbol struct is used to show that a function symbol is able to be patched. This is necessary to check that functions which need to be patched are able to be. On arm64 this means the presence of 2 NOP instructions at function entry which are patched by ftrace to call the ftrace handling code. These 2 NOPs are inserted by the compiler and the location of them is recorded in a section called "__patchable_function_entries". Check whether a symbol has a corresponding entry in the "__patchable_function_entries" section and if so mark it as "has_func_profiling". Signed-off-by: Suraj Jitindar Singh --- V1->V2: - Make error message standard across architectures when no patchable entry - Don't store __patchable_function_entries section in kpatch_find_func_profiling_calls(), instead find it each time --- kpatch-build/create-diff-object.c | 20 +++++++++++++++++++- kpatch-build/kpatch-elf.c | 3 +++ kpatch-build/kpatch-elf.h | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 1097e2daf..03794da82 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1696,7 +1696,7 @@ static void kpatch_check_func_profiling_calls(struct kpatch_elf *kelf) (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", + log_normal("function %s doesn't have patchable function entry, unable to patch\n", sym->name); errs++; } @@ -3976,6 +3976,24 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) continue; switch(kelf->arch) { + case AARCH64: { + struct section *sec = find_section_by_name(&kelf->sections, + "__patchable_function_entries"); + /* + * If we can't find the __patchable_function_entries section or + * there are no relocations in it then not patchable. + */ + if (!sec || !sec->rela) + return; + list_for_each_entry(rela, &sec->rela->relas, list) { + if (rela->sym->sec && sym->sec == rela->sym->sec) { + sym->has_func_profiling = 1; + break; + } + } + + break; + } case PPC64: list_for_each_entry(rela, &sym->sec->rela->relas, list) { if (!strcmp(rela->sym->name, "_mcount")) { diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 885ab913a..c9a0188fc 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -604,6 +604,9 @@ struct kpatch_elf *kpatch_elf_open(const char *name) if (!gelf_getehdr(kelf->elf, &ehdr)) ERROR("gelf_getehdr"); switch (ehdr.e_machine) { + case EM_AARCH64: + kelf->arch = AARCH64; + break; case EM_PPC64: kelf->arch = PPC64; break; diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index d47b6c7e0..7f787e526 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -115,6 +115,7 @@ enum architecture { PPC64 = 0x1 << 0, X86_64 = 0x1 << 1, S390 = 0x1 << 2, + AARCH64 = 0x1 << 3, }; struct kpatch_elf { From 3521a46d3da7f062dffde8da91c9211dcdf81cf2 Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Fri, 28 Jul 2023 16:33:23 -0700 Subject: [PATCH 17/34] create-diff-object: Split kpatch_create_mcount_sections into alloc and populate The function kpatch_create_mcount_sections() allocates the __mcount_loc section and then populates it with functions which have a patchable entry. The following patch will add aarch64 support to this function where the allocation will have to be done before the kelf_patched is torn down. Thus split this function so that the allocation can be performed earlier and the populating as before. No intended functional change. Signed-off-by: Suraj Jitindar Singh --- V1->V2: - Add patch to series --- kpatch-build/create-diff-object.c | 31 ++++++++++++++++++++++--------- test/unit/objs | 2 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 03794da82..0d0efc046 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -3695,6 +3695,21 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char * } } +static void kpatch_alloc_mcount_sections(struct kpatch_elf *kelf, struct kpatch_elf *kelfout) +{ + int nr; + struct symbol *sym; + + nr = 0; + list_for_each_entry(sym, &kelfout->symbols, list) + if (sym->type == STT_FUNC && sym->status != SAME && + sym->has_func_profiling) + nr++; + + /* create text/rela section pair */ + create_section_pair(kelfout, "__mcount_loc", sizeof(void *), nr); +} + /* * This function basically reimplements the functionality of the Linux * recordmcount script, so that patched functions can be recognized by ftrace. @@ -3702,7 +3717,7 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char * * 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) +static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) { int nr, index; struct section *sec, *relasec; @@ -3711,15 +3726,10 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) 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); + sec = find_section_by_name(&kelf->sections, "__mcount_loc"); relasec = sec->rela; + nr = (int) (sec->data->d_size / sizeof(void *)); /* populate sections */ index = 0; @@ -4165,6 +4175,9 @@ int main(int argc, char *argv[]) /* this is destructive to kelf_patched */ kpatch_migrate_included_elements(kelf_patched, &kelf_out); + /* this must be done before kelf_patched is torn down */ + kpatch_alloc_mcount_sections(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 @@ -4183,7 +4196,7 @@ int main(int argc, char *argv[]) kpatch_create_callbacks_objname_rela(kelf_out, parent_name); kpatch_build_strings_section_data(kelf_out); - kpatch_create_mcount_sections(kelf_out); + kpatch_populate_mcount_sections(kelf_out); /* * At this point, the set of output sections and symbols is diff --git a/test/unit/objs b/test/unit/objs index a51c80a60..31f16a29c 160000 --- a/test/unit/objs +++ b/test/unit/objs @@ -1 +1 @@ -Subproject commit a51c80a60fc8ade7e7ec8ad875b2963f3a15a494 +Subproject commit 31f16a29c6c3dc9ac101d8ca780723a6667c219e From 22ccb91040c923e08d1e69cccaec9406cc350c2d Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Mon, 9 Jan 2023 02:15:58 -0800 Subject: [PATCH 18/34] create-diff-object: Create __patchable_function_entries section for aarch64 The __mcount_loc section contains the addresses of patchable ftrace sites which is used by the ftrace infrastructure in the kernel to create a list of tracable functions and to know where to patch to enable tracing of them. On aarch64 this section is called __patchable_function_entries and is generated by the compiler. Either of __mcount_loc or __patchable_function_entries is recognised by the kernel but for aarch64 use __patchable_function_entries as it is what is expected. Add aarch64 support to kpatch_alloc_mcount_sections(). The SHF_LINK_ORDER section flag must be copied to ensure that it matches to avoid the following: ld: __patchable_function_entries has both ordered [...] and unordered [...] sections Add aarch64 support to kpatch_populate_mcount_sections(). Check for the 2 required NOP instructions on function entry, which may be preceded by a BTI C instruction depending on whether the function is a leaf function. This determines the offset of the patch site. Signed-off-by: Suraj Jitindar Singh --- V1->V2: - Don't preserve the __patchable_function_entries section from the patched elf as this is already verified by kpatch_check_func_profiling_calls() - Instead get the patch entry offset by checking for a preceding BTI C instr - Copy the section flags for __patchable_function_entries --- rebased, added sh_link fix from Suraj's later commit "kpatch-build: Enable ARM64 support" Signed-off-by: Pete Swain --- kpatch-build/create-diff-object.c | 75 +++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 0d0efc046..63e4254f1 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -3695,6 +3695,11 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char * } } +/* + * Allocate the mcount/patchable_function_entry sections which must be done + * before the patched object is torn down so that the section flags can be + * copied. + */ static void kpatch_alloc_mcount_sections(struct kpatch_elf *kelf, struct kpatch_elf *kelfout) { int nr; @@ -3707,10 +3712,36 @@ static void kpatch_alloc_mcount_sections(struct kpatch_elf *kelf, struct kpatch_ nr++; /* create text/rela section pair */ - create_section_pair(kelfout, "__mcount_loc", sizeof(void *), nr); + switch(kelf->arch) { + case AARCH64: { + struct section *sec, *tmp; + + sec = create_section_pair(kelfout, "__patchable_function_entries", sizeof(void *), nr); + + /* + * Depending on the compiler the __patchable_function_entries section + * can be ordered or not, copy this flag to the section we created to + * avoid: + * ld: __patchable_function_entries has both ordered [...] and unordered [...] sections + */ + tmp = find_section_by_name(&kelf->sections, "__patchable_function_entries"); + sec->sh.sh_flags |= (tmp->sh.sh_flags & SHF_LINK_ORDER); + sec->sh.sh_link = 1; + break; + } + case PPC64: + case X86_64: + case S390: + create_section_pair(kelfout, "__mcount_loc", sizeof(void *), nr); + break; + default: + ERROR("unsupported arch\n"); + } } /* + * Populate the mcount sections allocated by kpatch_alloc_mcount_sections() + * previously. * This function basically reimplements the functionality of the Linux * recordmcount script, so that patched functions can be recognized by ftrace. * @@ -3726,8 +3757,18 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) void **funcs; unsigned long insn_offset = 0; - - sec = find_section_by_name(&kelf->sections, "__mcount_loc"); + switch(kelf->arch) { + case AARCH64: + sec = find_section_by_name(&kelf->sections, "__patchable_function_entries"); + break; + case PPC64: + case X86_64: + case S390: + sec = find_section_by_name(&kelf->sections, "__mcount_loc"); + break; + default: + ERROR("unsupported arch\n"); + } relasec = sec->rela; nr = (int) (sec->data->d_size / sizeof(void *)); @@ -3744,6 +3785,34 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) } switch(kelf->arch) { + case AARCH64: { + unsigned char *insn; + int i; + + insn = sym->sec->data->d_buf; + insn_offset = 0; + + /* + * If BTI (Branch Target Identification) is enabled then there + * might be an additional 'BTI C' instruction before the two + * patchable function entry 'NOP's. + * i.e. 0xd503245f (little endian) + */ + if (insn[0] == 0x5f) { + if (insn[1] != 0x24 || insn[2] != 0x03 || insn[3] != 0xd5) + ERROR("%s: unexpected instruction in patch section of function\n", sym->name); + insn_offset += 4; + insn += 4; + } + for (i = 0; i < 8; i += 4) { + /* We expect a NOP i.e. 0xd503201f (little endian) */ + if (insn[i] != 0x1f || insn[i + 1] != 0x20 || + insn[i + 2] != 0x03 || insn [i + 3] != 0xd5) + ERROR("%s: unexpected instruction in patch section of function\n", sym->name); + } + + break; + } case PPC64: { bool found = false; From 3650f3adcaa7b15f2e843a525005e751a9052a24 Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Fri, 31 May 2024 15:24:07 -0700 Subject: [PATCH 19/34] kpatch-build: Enable ARM64 support Add the final support required for aarch64 and enable building on that arch. Signed-off-by: Suraj Jitindar Singh --- V1->V2: - Add # shellcheck disable=SC2086 - Add comment to kpatch_is_mapping_symbol() --- README.md | 2 +- kpatch-build/Makefile | 2 +- kpatch-build/create-diff-object.c | 74 +++++++++++++++++++++++++------ kpatch-build/kpatch-build | 3 ++ kpatch-build/kpatch-elf.c | 3 ++ 5 files changed, 69 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 75995fe8c..78fd14bc4 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Supported Architectures - [x] x86-64 - [x] ppc64le -- [ ] arm64 +- [x] arm64 - [x] s390 [upstream prerequisites](doc/s390-upstream-prerequisites.md) Installation diff --git a/kpatch-build/Makefile b/kpatch-build/Makefile index bebf3cd96..7fb223138 100644 --- a/kpatch-build/Makefile +++ b/kpatch-build/Makefile @@ -22,7 +22,7 @@ PLUGIN_CFLAGS := $(filter-out -Wconversion, $(CFLAGS)) PLUGIN_CFLAGS += -shared -I$(GCC_PLUGINS_DIR)/include \ -Igcc-plugins -fPIC -fno-rtti -O2 -Wall endif -ifeq ($(filter $(ARCH),s390x x86_64 ppc64le),) +ifeq ($(filter $(ARCH),aarch64 s390x x86_64 ppc64le),) $(error Unsupported architecture ${ARCH}, check https://github.com/dynup/kpatch/#supported-architectures) endif diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 63e4254f1..9290440bd 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -173,6 +173,8 @@ static bool is_gcc6_localentry_bundled_sym(struct kpatch_elf *kelf, struct symbol *sym) { switch(kelf->arch) { + case AARCH64: + return false; case PPC64: return ((PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other) != 0) && sym->sym.st_value == 8); @@ -230,6 +232,24 @@ static struct rela *toc_rela(const struct rela *rela) (unsigned int)rela->addend); } +/* + * Mapping symbols are used to mark and label the transitions between code and + * data in elf files. They begin with a "$" dollar symbol. Don't correlate them + * as they often all have the same name either "$x" to mark the start of code + * or "$d" to mark the start of data. + */ +static bool kpatch_is_mapping_symbol(struct kpatch_elf *kelf, struct symbol *sym) +{ + if (kelf->arch != AARCH64) + return false; + + if (sym->name && sym->name[0] == '$' && + sym->type == STT_NOTYPE && + sym->bind == STB_LOCAL) + return true; + return false; +} + /* * When compiling with -ffunction-sections and -fdata-sections, almost every * symbol gets its own dedicated section. We call such symbols "bundled" @@ -624,6 +644,13 @@ static void kpatch_compare_correlated_section(struct section *sec) goto out; } + /* As above but for __p_f_e users like aarch64 */ + if (!strcmp(sec->name, ".rela__patchable_function_entries") || + !strcmp(sec->name, "__patchable_function_entries")) { + 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) || @@ -735,7 +762,7 @@ static bool insn_is_load_immediate(struct kpatch_elf *kelf, void *addr) * 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, +static bool _kpatch_line_macro_change_only(struct kpatch_elf *kelf, struct section *sec) { unsigned long offset, insn1_len, insn2_len; @@ -834,6 +861,23 @@ static bool kpatch_line_macro_change_only(struct kpatch_elf *kelf, return true; } +static bool kpatch_line_macro_change_only(struct kpatch_elf *kelf, + struct section *sec) +{ + switch(kelf->arch) { + case AARCH64: + /* TODO */ + return false; + case PPC64: + case S390: + case X86_64: + return _kpatch_line_macro_change_only(kelf, sec); + default: + ERROR("unsupported arch"); + } + return false; +} + /* * 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 @@ -1071,15 +1115,15 @@ static void kpatch_correlate_sections(struct list_head *seclist_orig, } } -static void kpatch_correlate_symbols(struct list_head *symlist_orig, - struct list_head *symlist_patched) +static void kpatch_correlate_symbols(struct kpatch_elf *kelf_orig, + struct kpatch_elf *kelf_patched) { struct symbol *sym_orig, *sym_patched; - list_for_each_entry(sym_orig, symlist_orig, list) { + list_for_each_entry(sym_orig, &kelf_orig->symbols, list) { if (sym_orig->twin) continue; - list_for_each_entry(sym_patched, symlist_patched, list) { + list_for_each_entry(sym_patched, &kelf_patched->symbols, list) { if (kpatch_mangled_strcmp(sym_orig->name, sym_patched->name) || sym_orig->type != sym_patched->type || sym_patched->twin) continue; @@ -1099,6 +1143,9 @@ static void kpatch_correlate_symbols(struct list_head *symlist_orig, !strncmp(sym_orig->name, ".LC", 3)) continue; + if (kpatch_is_mapping_symbol(kelf_orig, sym_orig)) + continue; + /* group section symbols must have correlated sections */ if (sym_orig->sec && sym_orig->sec->sh.sh_type == SHT_GROUP && @@ -1504,7 +1551,7 @@ 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); + kpatch_correlate_symbols(kelf_orig, kelf_patched); } static void kpatch_compare_correlated_elements(struct kpatch_elf *kelf) @@ -1620,7 +1667,8 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) if (is_text_section(relasec->base) && !is_text_section(sym->sec) && - is_arch(X86_64) && rela->type == R_X86_64_32S && + ((is_arch(X86_64) && rela->type == R_X86_64_32S) || + (is_arch(AARCH64) && rela->type == R_AARCH64_ABS64)) && rela->addend == (long)sym->sec->sh.sh_size && end == (long)sym->sec->sh.sh_size) { @@ -2417,28 +2465,28 @@ static bool static_call_sites_group_filter(struct lookup_table *lookup, static struct special_section special_sections[] = { { .name = "__bug_table", - .arch = X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390, .group_size = bug_table_group_size, }, { .name = ".fixup", - .arch = X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390, .group_size = fixup_group_size, }, { .name = "__ex_table", /* must come after .fixup */ - .arch = X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390, .group_size = ex_table_group_size, }, { .name = "__jump_table", - .arch = X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390, .group_size = jump_table_group_size, .group_filter = jump_table_group_filter, }, { .name = ".printk_index", - .arch = X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390, .group_size = printk_index_group_size, }, { @@ -2453,7 +2501,7 @@ static struct special_section special_sections[] = { }, { .name = ".altinstructions", - .arch = X86_64 | S390, + .arch = AARCH64 | X86_64 | S390, .group_size = altinstructions_group_size, }, { diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index f1a11bdde..268368797 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -378,6 +378,9 @@ find_special_section_data() { # Arch-specific features case "$ARCH" in + "aarch64") + check[a]=true # alt_instr + ;; "x86_64") check[a]=true # alt_instr kernel_version_gte 5.10.0 && check[s]=true # static_call_site diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index c9a0188fc..751b76e76 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -156,6 +156,8 @@ struct rela *find_rela_by_offset(struct section *relasec, unsigned int offset) unsigned int absolute_rela_type(struct kpatch_elf *kelf) { switch(kelf->arch) { + case AARCH64: + return R_AARCH64_ABS64; case PPC64: return R_PPC64_ADDR64; case X86_64: @@ -225,6 +227,7 @@ long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec, struct section *sec = relasec->base; switch(kelf->arch) { + case AARCH64: case PPC64: add_off = 0; break; From 52842efe76d2ee3fe3f41f0894c5ba6824fe06fd Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Mon, 14 Feb 2022 21:37:50 -0500 Subject: [PATCH 20/34] create-diff-object: add aarch64 ASSERT_RTNL macro detection On aarch64, only the ASSERT_RTNL macro is affected by source line number changes (WARN, BUG, etc. no longer embed line numbers in the instruction stream.) A small test function that invokes the macro for a line change from 42 to 43: 0000000000000000 : 0: d503245f bti c 4: d503201f nop 8: d503201f nop c: d503233f paciasp 10: a9bf7bfd stp x29, x30, [sp, #-16]! 14: 910003fd mov x29, sp 18: 94000000 bl 0 18: R_AARCH64_CALL26 rtnl_is_locked 1c: 34000080 cbz w0, 2c 20: a8c17bfd ldp x29, x30, [sp], #16 24: d50323bf autiasp 28: d65f03c0 ret 2c: 90000000 adrp x0, 0 2c: R_AARCH64_ADR_PREL_PG_HI21 .data.once 30: 39400001 ldrb w1, [x0] 30: R_AARCH64_LDST8_ABS_LO12_NC .data.once 34: 35ffff61 cbnz w1, 20 38: 52800022 mov w2, #0x1 // #1 3c: 90000001 adrp x1, 0 3c: R_AARCH64_ADR_PREL_PG_HI21 .rodata.str1.8+0x8 40: 39000002 strb w2, [x0] 40: R_AARCH64_LDST8_ABS_LO12_NC .data.once 44: 91000021 add x1, x1, #0x0 44: R_AARCH64_ADD_ABS_LO12_NC .rodata.str1.8+0x8 - 48: 52800542 mov w2, #0x2a // #42 + 48: 52800562 mov w2, #0x2b // #43 4c: 90000000 adrp x0, 0 4c: R_AARCH64_ADR_PREL_PG_HI21 .rodata.str1.8+0x20 50: 91000000 add x0, x0, #0x0 50: R_AARCH64_ADD_ABS_LO12_NC .rodata.str1.8+0x20 54: 94000000 bl 0 <__warn_printk> 54: R_AARCH64_CALL26 __warn_printk 58: d4210000 brk #0x800 5c: 17fffff1 b 20 Create an implementation of kpatch_line_macro_change_only() for aarch64 modeled after the other architectures. Only look for relocations to __warn_printk that ASSERT_RTNL invokes. Based-on-s390x-code-by: C. Erastus Toe Signed-off-by: Joe Lawrence --- kpatch-build/create-diff-object.c | 65 ++++++++++++++++++++++++++++++- kpatch-build/kpatch-elf.c | 2 + 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 9290440bd..6f94ee87a 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -861,13 +861,74 @@ static bool _kpatch_line_macro_change_only(struct kpatch_elf *kelf, return true; } +static bool _kpatch_line_macro_change_only_aarch64(struct kpatch_elf *kelf, + struct section *sec) +{ + unsigned char *start1, *start2; + unsigned long size, offset, insn_len; + struct rela *rela; + int lineonly = 0, found; + + insn_len = insn_length(kelf, NULL); + + 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; + + start1 = sec->twin->data->d_buf; + start2 = sec->data->d_buf; + size = sec->sh.sh_size; + for (offset = 0; offset < size; offset += insn_len) { + if (!memcmp(start1 + offset, start2 + offset, insn_len)) + continue; + + /* Verify mov w2 */ + if (((start1[offset] & 0b11111) != 0x2) || (start1[offset+3] != 0x52) || + ((start1[offset] & 0b11111) != 0x2) || (start2[offset+3] != 0x52)) + return false; + + /* + * Verify zero or more string relas followed by a + * warn_slowpath_* or another similar rela. + */ + found = 0; + list_for_each_entry(rela, &sec->rela->relas, list) { + if (rela->offset < offset + insn_len) + continue; + if (rela->string) + continue; + if (!strncmp(rela->sym->name, "__warned.", 9) || + !strncmp(rela->sym->name, "__already_done.", 15)) + continue; + if (!strcmp(rela->sym->name, "__warn_printk")) { + found = 1; + break; + } + return false; + } + if (!found) + return false; + + lineonly = 1; + } + + if (!lineonly) + ERROR("no instruction changes detected for changed section %s", + sec->name); + + return true; +} + static bool kpatch_line_macro_change_only(struct kpatch_elf *kelf, struct section *sec) { switch(kelf->arch) { case AARCH64: - /* TODO */ - return false; + return _kpatch_line_macro_change_only_aarch64(kelf, sec); case PPC64: case S390: case X86_64: diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 751b76e76..d03731283 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -277,6 +277,8 @@ unsigned int insn_length(struct kpatch_elf *kelf, void *addr) char *insn = addr; switch(kelf->arch) { + case AARCH64: + return 4; case X86_64: insn_init(&decoded_insn, addr, 1); From 62e3ba3c557f80bccc777b3b0e1f0c686cdecf36 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 4 Oct 2022 22:39:58 -0700 Subject: [PATCH 21/34] testing: add aarch unit tests Update the kpatch-unit-test-objs submodule reference to add aarch64 unit tests. Signed-off-by: Joe Lawrence --- .gitmodules | 3 ++- test/unit/Makefile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 49b10248a..f5b573ad6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "test/unit/objs"] path = test/unit/objs - url = https://github.com/dynup/kpatch-unit-test-objs.git + url = https://github.com/joe-lawrence/kpatch-unit-test-objs.git + branch = initial-aarch64 diff --git a/test/unit/Makefile b/test/unit/Makefile index fde1717dd..e3ed7d718 100644 --- a/test/unit/Makefile +++ b/test/unit/Makefile @@ -1,4 +1,4 @@ -ARCHES ?= ppc64le x86_64 +ARCHES ?= aarch64 ppc64le x86_64 .PHONY: all clean submodule-check From e46ddb3713362035e3f06c8087e14ea260fb5625 Mon Sep 17 00:00:00 2001 From: Misono Tomohiro Date: Wed, 7 Sep 2022 10:38:01 +0900 Subject: [PATCH 22/34] create-diff-object: Fix mapping symbol handling on aarch64 It seems mapping symbols in aarch64 elf has section size of 0. So, exclude it in section symbol replacing code just like kpatch_correlate_symbols(). This fixes the data-read-mostly unit test on aarch64. Signed-off-by: Misono Tomohiro --- kpatch-build/create-diff-object.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 6f94ee87a..5c11de007 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1766,6 +1766,9 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) */ } else if (target_off == start && target_off == end) { + if(kpatch_is_mapping_symbol(kelf, sym)) + continue; + /* * Allow replacement for references to * empty symbols. From 9479442c1574860e275f456d19419cc76a5869aa Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Fri, 28 Jul 2023 16:34:10 -0700 Subject: [PATCH 23/34] kpatch-syscall.h: add aarch64 helper Copy from kernel source tree. Signed-off-by: Misono Tomohiro --- kmod/patch/kpatch-syscall.h | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/kmod/patch/kpatch-syscall.h b/kmod/patch/kpatch-syscall.h index ec8d24d53..84b90157c 100644 --- a/kmod/patch/kpatch-syscall.h +++ b/kmod/patch/kpatch-syscall.h @@ -186,7 +186,34 @@ # endif /* LINUX_VERSION_CODE */ -#endif /* CONFIG_X86_64 */ +#elif defined(CONFIG_ARM64) + +/* arm64/include/asm/syscall_wrapper.h versions */ + +#define SC_ARM64_REGS_TO_ARGS(x, ...) \ + __MAP(x,__SC_ARGS \ + ,,regs->regs[0],,regs->regs[1],,regs->regs[2] \ + ,,regs->regs[3],,regs->regs[4],,regs->regs[5]) + +#define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long __arm64_sys##name(const struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__arm64_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 __arm64_sys##name(const struct pt_regs *regs) \ + { \ + return __se_sys##name(SC_ARM64_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 /* which arch */ #ifndef __KPATCH_SYSCALL_DEFINEx From a4755233442b665b7b140eed64953a55a4d99444 Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Mon, 10 Oct 2022 19:03:09 -0700 Subject: [PATCH 24/34] doc/arm64-upstream-prerequisites.md --- README.md | 2 +- doc/arm64-upstream-prerequisites.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 doc/arm64-upstream-prerequisites.md diff --git a/README.md b/README.md index 78fd14bc4..b9c563000 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Supported Architectures - [x] x86-64 - [x] ppc64le -- [x] arm64 +- [x] arm64 [upstream prerequisites](doc/arm64-upstream-prerequisites.md) - [x] s390 [upstream prerequisites](doc/s390-upstream-prerequisites.md) Installation diff --git a/doc/arm64-upstream-prerequisites.md b/doc/arm64-upstream-prerequisites.md new file mode 100644 index 000000000..3c49af860 --- /dev/null +++ b/doc/arm64-upstream-prerequisites.md @@ -0,0 +1,11 @@ +### arm64 backporting + +**Prerequisite kernel patches:** +**v5.19:** +- [Madhavan Venkataraman's [RFC PATCH v2 00/20] arm64: livepatch: Use ORC for dynamic frame pointer validation](https://lore.kernel.org/linux-arm-kernel/20220524001637.1707472-1-madvenka@linux.microsoft.com/) +- also tested against madvenka's earlier pre-objtool series up to v15 + +**v5.15 and v5.10:** +- under development, both known to work with backports of madvenka's v15, + but the objtool-using version above is likely to be the approach that + finally merges into upstream kernel From 17a90ecf9bb7629363e03100b5545bb254e03557 Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Mon, 9 Jan 2023 02:16:57 -0800 Subject: [PATCH 25/34] arm64: per-func __patchable_function_entries sections new clang toolchain on arm64 produces individual __patchable_function_entries sections for each patchable func, in -ffunction-sections mode, rather than traditional single __mcount_loc section. Bend the existing logic to detect this multiplicity in the incoming kelf objects, and allocate N identical one-entry sections. These are retrieved as needed by a new function: find_nth_section_by_name() and attached to the .text sections they describe. These __pfe section are not actually arm64-specific, but a generic enhancement across gcc & clang, to allow better garbage collection of unreferenced object sections, and mcount/pfe objects which refer to them. The __pfe sections are combined in kernel-or-module final link, from 5.19.9's 9440155ccb948f8e3ce5308907a2e7378799be60. From clang-11, __pfe is supported for x86, though not yet used by kernel The split between allocate/populate phases here is necessary to enumerate/populate the outgoing section-headers before beginning to produce output sections Also adds some missing \n to log_debug()s Signed-off-by: Pete Swain --- kpatch-build/create-diff-object.c | 120 ++++++++++++++++++++++-------- kpatch-build/kpatch-elf.c | 32 ++++++-- kpatch-build/kpatch-elf.h | 3 + 3 files changed, 119 insertions(+), 36 deletions(-) mode change 100644 => 100755 kpatch-build/kpatch-elf.c diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 5c11de007..c1bc87dc6 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -70,6 +70,7 @@ enum subsection { enum loglevel loglevel = NORMAL; bool KLP_ARCH; +bool multi_pfe; int jump_label_errors, static_call_errors; @@ -3273,7 +3274,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf, 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", + log_debug("lookup for %s: obj=%s sympos=%lu size=%lu\n", sym->name, symbol.objname, symbol.sympos, symbol.size); @@ -3662,7 +3663,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, ERROR("can't find symbol '%s' in symbol table", rela->sym->name); - log_debug("lookup for %s: obj=%s sympos=%lu", + log_debug("lookup for %s: obj=%s sympos=%lu\n", rela->sym->name, symbol.objname, symbol.sympos); @@ -3816,19 +3817,24 @@ static void kpatch_alloc_mcount_sections(struct kpatch_elf *kelf, struct kpatch_ { int nr; struct symbol *sym; + int text_idx = 0; nr = 0; - list_for_each_entry(sym, &kelfout->symbols, list) + list_for_each_entry(sym, &kelfout->symbols, list) { if (sym->type == STT_FUNC && sym->status != SAME && - sym->has_func_profiling) + sym->has_func_profiling) { + text_idx = sym->sec->index; nr++; + } + } /* create text/rela section pair */ switch(kelf->arch) { case AARCH64: { - struct section *sec, *tmp; - - sec = create_section_pair(kelfout, "__patchable_function_entries", sizeof(void *), nr); + struct section *sec; + int entries = multi_pfe ? 1 : nr; + int copies = multi_pfe ? nr : 1; + int flags = 0, rflags = 0; /* * Depending on the compiler the __patchable_function_entries section @@ -3836,9 +3842,26 @@ static void kpatch_alloc_mcount_sections(struct kpatch_elf *kelf, struct kpatch_ * avoid: * ld: __patchable_function_entries has both ordered [...] and unordered [...] sections */ - tmp = find_section_by_name(&kelf->sections, "__patchable_function_entries"); - sec->sh.sh_flags |= (tmp->sh.sh_flags & SHF_LINK_ORDER); - sec->sh.sh_link = 1; + sec = find_section_by_name(&kelf->sections, "__patchable_function_entries"); + if (sec) { + flags = (sec->sh.sh_flags & (SHF_LINK_ORDER|SHF_WRITE)); + if (sec->rela) + rflags = (sec->rela->sh.sh_flags & (SHF_LINK_ORDER|SHF_WRITE)); + } + + for (nr = 0; nr < copies; nr++) { + sec = create_section_pair(kelfout, + "__patchable_function_entries", + sizeof(void *), entries); + + sec->sh.sh_flags |= flags; + if (sec->rela) + sec->rela->sh.sh_flags |= rflags; + if (multi_pfe) + sec->sh.sh_link = 0; + else + sec->sh.sh_link = text_idx; + } break; } case PPC64: @@ -3867,11 +3890,14 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) struct symbol *sym; struct rela *rela, *mcount_rela; void **funcs; - unsigned long insn_offset = 0; switch(kelf->arch) { case AARCH64: - sec = find_section_by_name(&kelf->sections, "__patchable_function_entries"); + if (multi_pfe) + sec = NULL; + else + sec = find_section_by_name(&kelf->sections, + "__patchable_function_entries"); break; case PPC64: case X86_64: @@ -3881,12 +3907,20 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) default: ERROR("unsupported arch\n"); } - relasec = sec->rela; - nr = (int) (sec->data->d_size / sizeof(void *)); + + if (multi_pfe) { + relasec = NULL; + nr = 0; + } else { + relasec = sec->rela; + nr = (int) (sec->data->d_size / sizeof(void *)); + } /* populate sections */ index = 0; list_for_each_entry(sym, &kelf->symbols, list) { + unsigned long insn_offset = 0; + if (sym->type != STT_FUNC || sym->status == SAME) continue; @@ -3902,7 +3936,6 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) int i; insn = sym->sec->data->d_buf; - insn_offset = 0; /* * If BTI (Branch Target Identification) is enabled then there @@ -3993,6 +4026,18 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) ERROR("unsupported arch"); } + if (multi_pfe) { + sec = find_nth_section_by_name(&kelf->sections, nr, "__patchable_function_entries"); + if (!sec) + ERROR("cannot retrieve pre-allocated __pfe #%d\n", nr); + + relasec = sec->rela; + sym->sec->pfe = sec; + sec->sh.sh_link = sec->index; + + nr++; + } + /* * 'rela' points to the mcount/fentry call. * @@ -4002,7 +4047,13 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) 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)); + + if (multi_pfe) { + mcount_rela->offset = 0; + sec = NULL; + } else { + mcount_rela->offset = (unsigned int) (index * sizeof(*funcs)); + } index++; } @@ -4161,6 +4212,7 @@ 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->is_pfx || !sym->sec || !sym->sec->rela) @@ -4168,21 +4220,23 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) switch(kelf->arch) { case AARCH64: { - struct section *sec = find_section_by_name(&kelf->sections, - "__patchable_function_entries"); - /* - * If we can't find the __patchable_function_entries section or - * there are no relocations in it then not patchable. - */ - if (!sec || !sec->rela) - return; - list_for_each_entry(rela, &sec->rela->relas, list) { - if (rela->sym->sec && sym->sec == rela->sym->sec) { - sym->has_func_profiling = 1; - break; + struct section *sec; + + list_for_each_entry(sec, &kelf->sections, list) { + if (strcmp(sec->name, "__patchable_function_entries")) + continue; + if (multi_pfe && sym->sec->pfe != sec) + continue; + if (!sec->rela) + continue; + + list_for_each_entry(rela, &sec->rela->relas, list) { + if (rela->sym->sec && sym->sec == rela->sym->sec) { + sym->has_func_profiling = 1; + goto next_symbol; + } } } - break; } case PPC64: @@ -4215,6 +4269,7 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) default: ERROR("unsupported arch"); } + next_symbol:; } } @@ -4262,6 +4317,12 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) return 0; } +static bool has_multi_pfe(struct kpatch_elf *kelf) +{ + return !!find_nth_section_by_name(&kelf->sections, 1, + "__patchable_function_entries"); +} + static struct argp argp = { options, parse_opt, args_doc, NULL }; int main(int argc, char *argv[]) @@ -4295,6 +4356,7 @@ int main(int argc, char *argv[]) kelf_orig = kpatch_elf_open(orig_obj); kelf_patched = kpatch_elf_open(patched_obj); + multi_pfe = has_multi_pfe(kelf_orig) || has_multi_pfe(kelf_patched); kpatch_find_func_profiling_calls(kelf_orig); kpatch_find_func_profiling_calls(kelf_patched); diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c old mode 100644 new mode 100755 index d03731283..d9eaf21d0 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -108,17 +108,29 @@ struct section *find_section_by_index(struct list_head *list, unsigned int index return NULL; } -struct section *find_section_by_name(struct list_head *list, const char *name) +struct section *find_nth_section_by_name( struct list_head *list, int nth, const char *name) { struct section *sec; - list_for_each_entry(sec, list, list) - if (!strcmp(sec->name, name)) - return sec; + if (!list || !list->next || !name) + return NULL; + + list_for_each_entry(sec, list, list) { + if (strcmp(sec->name, name)) + continue; + if (--nth >= 0) + continue; + return sec; + } return NULL; } +struct section *find_section_by_name(struct list_head *list, const char *name) +{ + return find_nth_section_by_name(list, 0, name); +} + struct symbol *find_symbol_by_index(struct list_head *list, size_t index) { struct symbol *sym; @@ -1007,11 +1019,17 @@ void kpatch_reindex_elements(struct kpatch_elf *kelf) index = 0; list_for_each_entry(sym, &kelf->symbols, list) { sym->index = index++; - if (sym->sec) + 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) + if (sym->sec->pfe) { + sym->sec->pfe->sh.sh_link = sym->sec->index; + if (sym->sec->pfe->rela) + sym->sec->pfe->rela->sh.sh_info = sym->sec->index; + } + } else if (sym->sym.st_shndx != SHN_ABS && + sym->sym.st_shndx != SHN_LIVEPATCH) { sym->sym.st_shndx = SHN_UNDEF; + } } } diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index 7f787e526..64ebf8863 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -65,6 +65,7 @@ struct section { struct symbol *secsym, *sym; }; }; + struct section *pfe; /* arm64 per-func __patchable_function_entries */ }; enum symbol_strip { @@ -138,6 +139,8 @@ 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 section *find_nth_section_by_name(struct list_head *list, int nth, + 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); From 9012da590f3354a248725987b77de30357520b4a Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Wed, 12 Jul 2023 08:13:27 -0700 Subject: [PATCH 26/34] arm64 leaf-function fix On arm64, kpatch_find_func_profiling_calls() was skipping leaf functions, with no relocations, so they weren't patchable. Here other archs need to walk a function's reloc entries to check for __fentry__ or __mcount, so it's valid to skip over functions without sym->sec->rela, because they cannot be patchable, else they would have at least an __fentry__ call relocation. But arm64 marks functions patchable in a different way, with per-func __patchable_function_entries sections referring _to_ the func, not relocations _within_ the func, so a function w/o relocations for text or data can still be patchable. Move the sym->sec->rela check to the per-arch paths. This allows gcc-static-local-var-5.patch to generate livepatch, on arm64 & x86 Suggested-By: Bill Wendling Signed-off-by: Pete Swain --- kpatch-build/create-diff-object.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index c1bc87dc6..92c38b82f 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -4214,8 +4214,7 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) unsigned char *insn; list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->type != STT_FUNC || sym->is_pfx || - !sym->sec || !sym->sec->rela) + if (sym->type != STT_FUNC || sym->is_pfx || !sym->sec) continue; switch(kelf->arch) { @@ -4240,6 +4239,8 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) break; } case PPC64: + if (!sym->sec->rela) + continue; list_for_each_entry(rela, &sym->sec->rela->relas, list) { if (!strcmp(rela->sym->name, "_mcount")) { sym->has_func_profiling = 1; @@ -4248,6 +4249,8 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) } break; case X86_64: + if (!sym->sec->rela) + continue; rela = list_first_entry(&sym->sec->rela->relas, struct rela, list); if ((rela->type != R_X86_64_NONE && @@ -4259,6 +4262,8 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) sym->has_func_profiling = 1; break; case S390: + if (!sym->sec->rela) + continue; /* Check for compiler generated fentry nop - jgnop 0 */ insn = sym->sec->data->d_buf; if (insn[0] == 0xc0 && insn[1] == 0x04 && From 9dd492390ce47faa2bb4a1bdea41f69265f26e8e Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Sun, 16 Oct 2022 22:55:44 -0700 Subject: [PATCH 27/34] kpatch-cc skip arch/arm64/kernel/vdso*/* Signed-off-by: Pete Swain --- kpatch-build/kpatch-cc | 1 + 1 file changed, 1 insertion(+) diff --git a/kpatch-build/kpatch-cc b/kpatch-build/kpatch-cc index 17aae25b6..d5ec99362 100755 --- a/kpatch-build/kpatch-cc +++ b/kpatch-build/kpatch-cc @@ -42,6 +42,7 @@ if [[ "$TOOLCHAINCMD" =~ ^(.*-)?gcc$ || "$TOOLCHAINCMD" =~ ^(.*-)?clang$ ]] ; th arch/s390/boot/*|\ arch/s390/purgatory/*|\ arch/s390/kernel/vdso64/*|\ + arch/arm64/kernel/vdso*/*|\ drivers/firmware/efi/libstub/*|\ init/version.o|\ init/version-timestamp.o|\ From 2e26ee2dab1faf98073d2a7a8539102459cf2440 Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Mon, 9 Jan 2023 02:18:33 -0800 Subject: [PATCH 28/34] adapt to clang/arm64 naming New toolchain/arch, new conventions for section/label/etc names gcc's .LCx symbols point to string literals in '.rodata..str1.*' sections. Clang creates similar .Ltmp%d symbols in '.rodata.str' The function is_string_literal_section() generalized (too much?) to match either - clang's/arm64 /^\.rodata\.str$/ - gcc's /^\.rodata\./ && /\.str1\./ Various matchers for .data.unlikely .bss.unlikely replaced by is_data_unlikely_section() generalized to match - gcc's ".data.unlikely" - clang's ".(data|bss).module_name.unlikely" .data.once handled similarly Signed-off-by: Pete Swain --- kpatch-build/create-diff-object.c | 34 +++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 92c38b82f..5cb15ac2b 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -354,6 +354,28 @@ static bool is_string_literal_section(struct section *sec) return !strncmp(sec->name, ".rodata.", 8) && strstr(sec->name, ".str"); } +/* gcc's ".data.unlikely" or clang's ".(data|bss).module_name.unlikely" */ +static bool is_data_unlikely_section(const char *name) +{ + size_t len = strlen(name); + + return (len >= 5 + 8 && + ((!strncmp(name, ".data.", 6) || + !strncmp(name, ".bss.", 5)) && + strstr(name + len - 9, ".unlikely"))); +} + +/* either ".data.once" or clang's ".(data|bss).module_name.once" */ +static bool is_data_once_section(const char *name) +{ + size_t len = strlen(name); + + return (len >= 5 + 4 && + (!strncmp(name, ".data.", 6) || + !strncmp(name, ".bss.", 5)) && + strstr(name + len - 5, ".once")); +} + /* * This function detects whether the given symbol is a "special" static local * variable (for lack of a better term). @@ -395,7 +417,7 @@ static bool is_special_static(struct symbol *sym) if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL) return false; - if (!strcmp(sym->sec->name, ".data.once")) + if (is_data_once_section(sym->sec->name)) return true; for (var_name = var_names; *var_name; var_name++) { @@ -1200,9 +1222,11 @@ static void kpatch_correlate_symbols(struct kpatch_elf *kelf_orig, * The .LCx symbols point to string literals in * '.rodata..str1.*' sections. They get included * in kpatch_include_standard_elements(). + * Clang creates similar .Ltmp%d symbols in .rodata.str */ if (sym_orig->type == STT_NOTYPE && - !strncmp(sym_orig->name, ".LC", 3)) + !(strncmp(sym_orig->name, ".LC", 3) && + strncmp(sym_orig->name, ".Ltmp", 5))) continue; if (kpatch_is_mapping_symbol(kelf_orig, sym_orig)) @@ -1847,8 +1871,10 @@ static void kpatch_verify_patchability(struct kpatch_elf *kelf) * (.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"))) { + (!strncmp(sec->name, ".data", 5) || + !strncmp(sec->name, ".bss", 4)) && + !is_data_once_section(sec->name) && + !is_data_unlikely_section(sec->name)) { log_normal("data section %s selected for inclusion\n", sec->name); errs++; From 5c31d6a7c8768714cdbecdd5fb83665eae10ca0e Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Mon, 30 Jan 2023 19:15:12 -0800 Subject: [PATCH 29/34] testing: freshen unit tests to address ppc64le fails merged in joe-lawrence/ppc64le-remove-eh_frame-take2 this should resolve github's test failures --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index f5b573ad6..824d77df9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "test/unit/objs"] path = test/unit/objs - url = https://github.com/joe-lawrence/kpatch-unit-test-objs.git - branch = initial-aarch64 + url = https://github.com/swine/kpatch-unit-test-objs.git + branch = remotes/github-swine/arm64 From 141d8a12dc0cf1a59cd12012e68aac109f0a62fa Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Tue, 31 Jan 2023 18:15:08 -0800 Subject: [PATCH 30/34] create-diff-object: merge aarch64 kpatch_line_macro_change_only() Generalized kpatch_line_macro_change_only() & insn_is_load_immediate() to collapse the aarch64 support back into parent. I'm assuming the 3rd start1 of the original /* Verify mov w2 */ if (((start1[offset] & 0b11111) != 0x2) || (start1[offset+3] != 0x52) || ((start1[offset] & 0b11111) != 0x2) || (start2[offset+3] != 0x52)) was a typo for start2. That's now absorbed into insn_is_load_immediate() leaving just one aarch64-specific piece: thinning out the match-list for diagnosing a __LINE__ reference, to just "__warn_printf". --- kpatch-build/create-diff-object.c | 96 ++++++------------------------- 1 file changed, 16 insertions(+), 80 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 5cb15ac2b..7189a0051 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -752,6 +752,12 @@ static bool insn_is_load_immediate(struct kpatch_elf *kelf, void *addr) break; + case AARCH64: + /* Verify mov w2 */ + if ((insn[0] & 0b11111) == 0x2 && insn[3] == 0x52) + return true; + break; + default: ERROR("unsupported arch"); } @@ -785,13 +791,14 @@ static bool insn_is_load_immediate(struct kpatch_elf *kelf, void *addr) * 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, +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; + bool warn_printk_only = (kelf->arch == AARCH64); if (sec->status != CHANGED || is_rela_section(sec) || @@ -855,8 +862,15 @@ static bool _kpatch_line_macro_change_only(struct kpatch_elf *kelf, !strncmp(rela->sym->name, "__func__.", 9)) continue; + if (!strcmp(rela->sym->name, "__warn_printk")) { + found = true; + break; + } + + if (warn_printk_only) + return false; + 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") || @@ -884,84 +898,6 @@ static bool _kpatch_line_macro_change_only(struct kpatch_elf *kelf, return true; } -static bool _kpatch_line_macro_change_only_aarch64(struct kpatch_elf *kelf, - struct section *sec) -{ - unsigned char *start1, *start2; - unsigned long size, offset, insn_len; - struct rela *rela; - int lineonly = 0, found; - - insn_len = insn_length(kelf, NULL); - - 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; - - start1 = sec->twin->data->d_buf; - start2 = sec->data->d_buf; - size = sec->sh.sh_size; - for (offset = 0; offset < size; offset += insn_len) { - if (!memcmp(start1 + offset, start2 + offset, insn_len)) - continue; - - /* Verify mov w2 */ - if (((start1[offset] & 0b11111) != 0x2) || (start1[offset+3] != 0x52) || - ((start1[offset] & 0b11111) != 0x2) || (start2[offset+3] != 0x52)) - return false; - - /* - * Verify zero or more string relas followed by a - * warn_slowpath_* or another similar rela. - */ - found = 0; - list_for_each_entry(rela, &sec->rela->relas, list) { - if (rela->offset < offset + insn_len) - continue; - if (rela->string) - continue; - if (!strncmp(rela->sym->name, "__warned.", 9) || - !strncmp(rela->sym->name, "__already_done.", 15)) - continue; - if (!strcmp(rela->sym->name, "__warn_printk")) { - found = 1; - break; - } - return false; - } - if (!found) - return false; - - lineonly = 1; - } - - if (!lineonly) - ERROR("no instruction changes detected for changed section %s", - sec->name); - - return true; -} - -static bool kpatch_line_macro_change_only(struct kpatch_elf *kelf, - struct section *sec) -{ - switch(kelf->arch) { - case AARCH64: - return _kpatch_line_macro_change_only_aarch64(kelf, sec); - case PPC64: - case S390: - case X86_64: - return _kpatch_line_macro_change_only(kelf, sec); - default: - ERROR("unsupported arch"); - } - return false; -} - /* * 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 From 30056268dba3e4f0eb502b7c6c0d5be7ba9a361d Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Fri, 28 Jul 2023 16:35:45 -0700 Subject: [PATCH 31/34] create-diff-object: keep ubsan section If CONFIG_UBSAN is enabled, ubsan section (.data..Lubsan_{data,type}) can be created. Keep them unconditionally. NOTE: This patch needs to be verified. Signed-off-by: Misono Tomohiro --- kpatch-build/create-diff-object.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 7189a0051..bb9ad99e9 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1646,6 +1646,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) !strcmp(rela->sym->name, ".fixup") || !strcmp(rela->sym->name, ".altinstr_replacement") || !strcmp(rela->sym->name, ".altinstr_aux") || + !strncmp(rela->sym->name, ".data..Lubsan", 13) || !strcmp(rela->sym->name, ".text..refcount") || !strncmp(rela->sym->name, "__ftr_alt_", 10)) continue; @@ -1810,7 +1811,8 @@ static void kpatch_verify_patchability(struct kpatch_elf *kelf) (!strncmp(sec->name, ".data", 5) || !strncmp(sec->name, ".bss", 4)) && !is_data_once_section(sec->name) && - !is_data_unlikely_section(sec->name)) { + !is_data_unlikely_section(sec->name) && + strncmp(sec->name, ".data..Lubsan", 13)) { log_normal("data section %s selected for inclusion\n", sec->name); errs++; @@ -1906,6 +1908,7 @@ static void kpatch_include_standard_elements(struct kpatch_elf *kelf) !strcmp(sec->name, ".symtab") || !strcmp(sec->name, ".toc") || !strcmp(sec->name, ".rodata") || + !strncmp(sec->name, ".data..Lubsan", 13) || is_string_literal_section(sec)) { kpatch_include_section(sec); } From 41375b5f8aac13722245fe6d473fbcf309209ced Mon Sep 17 00:00:00 2001 From: Pete Swain Date: Thu, 10 Aug 2023 10:43:28 -0700 Subject: [PATCH 32/34] uninit var in kpatch-elf.c Initialize add_off earlier, so it's obviously never used uninitialized. Clang was warning on this, even if gcc was not. No functional change, the only path which left it undefined would call ERROR() anyway. --- kpatch-build/kpatch-elf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index d9eaf21d0..049062bf5 100755 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -235,13 +235,12 @@ static void rela_insn(const struct section *sec, const struct rela *rela, long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec, struct rela *rela) { - long add_off; + long add_off = 0; struct section *sec = relasec->base; switch(kelf->arch) { case AARCH64: case PPC64: - add_off = 0; break; case X86_64: if (!is_text_section(sec) || From c48342cbf87fb8867114d98fef5b2487ed67bceb Mon Sep 17 00:00:00 2001 From: zimao Date: Mon, 7 Aug 2023 21:56:50 +0000 Subject: [PATCH 33/34] create-diff-object: Remove the multi_pfe flag. In ARM64, every function section should have its own pfe section. It is a bug in GCC 11/12 which will only generate a single pfe section for all functions. The bug has been fixed in GCC 13.1. As the create-diff-object is generating the pfe sections on its own, we should also fix this bug, instead of try to repeat the bug. -- Adjusted whitespace in Zimao's proposed code. Signed-off-by: Pete Swain --- kpatch-build/create-diff-object.c | 159 ++++++++++-------------------- kpatch-build/kpatch-elf.c | 6 +- 2 files changed, 57 insertions(+), 108 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index bb9ad99e9..b699d5041 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -70,7 +70,6 @@ enum subsection { enum loglevel loglevel = NORMAL; bool KLP_ARCH; -bool multi_pfe; int jump_label_errors, static_call_errors; @@ -3773,114 +3772,68 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char * } } -/* - * Allocate the mcount/patchable_function_entry sections which must be done - * before the patched object is torn down so that the section flags can be - * copied. - */ -static void kpatch_alloc_mcount_sections(struct kpatch_elf *kelf, struct kpatch_elf *kelfout) +static void kpatch_set_pfe_link(struct kpatch_elf *kelf) { - int nr; - struct symbol *sym; - int text_idx = 0; + struct section* sec; + struct rela *rela; - nr = 0; - list_for_each_entry(sym, &kelfout->symbols, list) { - if (sym->type == STT_FUNC && sym->status != SAME && - sym->has_func_profiling) { - text_idx = sym->sec->index; - nr++; + list_for_each_entry(sec, &kelf->sections, list) { + if (strcmp(sec->name, "__patchable_function_entries")) { + continue; } - } - - /* create text/rela section pair */ - switch(kelf->arch) { - case AARCH64: { - struct section *sec; - int entries = multi_pfe ? 1 : nr; - int copies = multi_pfe ? nr : 1; - int flags = 0, rflags = 0; - /* - * Depending on the compiler the __patchable_function_entries section - * can be ordered or not, copy this flag to the section we created to - * avoid: - * ld: __patchable_function_entries has both ordered [...] and unordered [...] sections - */ - sec = find_section_by_name(&kelf->sections, "__patchable_function_entries"); - if (sec) { - flags = (sec->sh.sh_flags & (SHF_LINK_ORDER|SHF_WRITE)); - if (sec->rela) - rflags = (sec->rela->sh.sh_flags & (SHF_LINK_ORDER|SHF_WRITE)); + if (!sec->rela) { + continue; } - - for (nr = 0; nr < copies; nr++) { - sec = create_section_pair(kelfout, - "__patchable_function_entries", - sizeof(void *), entries); - - sec->sh.sh_flags |= flags; - if (sec->rela) - sec->rela->sh.sh_flags |= rflags; - if (multi_pfe) - sec->sh.sh_link = 0; - else - sec->sh.sh_link = text_idx; + list_for_each_entry(rela, &sec->rela->relas, list) { + rela->sym->sec->pfe = sec; } - break; - } - case PPC64: - case X86_64: - case S390: - create_section_pair(kelfout, "__mcount_loc", sizeof(void *), nr); - break; - default: - ERROR("unsupported arch\n"); } } /* - * Populate the mcount sections allocated by kpatch_alloc_mcount_sections() - * previously. * 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_populate_mcount_sections(struct kpatch_elf *kelf) +static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) { int nr, index; - struct section *sec, *relasec; + struct section *relasec; struct symbol *sym; struct rela *rela, *mcount_rela; void **funcs; + bool pfe_per_function; - switch(kelf->arch) { + nr = 0; + list_for_each_entry(sym, &kelf->symbols, list) + if (sym->type == STT_FUNC && sym->status != SAME && + sym->has_func_profiling) + nr++; + + switch (kelf->arch) { case AARCH64: - if (multi_pfe) - sec = NULL; - else - sec = find_section_by_name(&kelf->sections, - "__patchable_function_entries"); + /* For aarch64, we will create separate __patchable_function_entries sections for each symbols. */ + pfe_per_function = true; + relasec = NULL; break; case PPC64: case X86_64: case S390: - sec = find_section_by_name(&kelf->sections, "__mcount_loc"); + { + struct section *sec; + + /* create text/rela section pair */ + sec = create_section_pair(kelf, "__mcount_loc", sizeof(void*), nr); + relasec = sec->rela; break; + } default: ERROR("unsupported arch\n"); } - if (multi_pfe) { - relasec = NULL; - nr = 0; - } else { - relasec = sec->rela; - nr = (int) (sec->data->d_size / sizeof(void *)); - } - /* populate sections */ index = 0; list_for_each_entry(sym, &kelf->symbols, list) { @@ -3897,6 +3850,7 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) switch(kelf->arch) { case AARCH64: { + struct section *sec; unsigned char *insn; int i; @@ -3921,6 +3875,14 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) ERROR("%s: unexpected instruction in patch section of function\n", sym->name); } + /* Allocate __patchable_function_entries for symbol */ + sec = create_section_pair(kelf, "__patchable_function_entries", sizeof(void *), 1); + sec->sh.sh_flags |= SHF_WRITE | SHF_LINK_ORDER; + /* We will reset this sh_link in the reindex function. */ + sec->sh.sh_link = 0; + + relasec = sec->rela; + sym->sec->pfe = sec; break; } case PPC64: { @@ -3991,18 +3953,6 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) ERROR("unsupported arch"); } - if (multi_pfe) { - sec = find_nth_section_by_name(&kelf->sections, nr, "__patchable_function_entries"); - if (!sec) - ERROR("cannot retrieve pre-allocated __pfe #%d\n", nr); - - relasec = sec->rela; - sym->sec->pfe = sec; - sec->sh.sh_link = sec->index; - - nr++; - } - /* * 'rela' points to the mcount/fentry call. * @@ -4013,9 +3963,8 @@ static void kpatch_populate_mcount_sections(struct kpatch_elf *kelf) mcount_rela->type = absolute_rela_type(kelf); mcount_rela->addend = insn_offset - sym->sym.st_value; - if (multi_pfe) { + if (pfe_per_function) { mcount_rela->offset = 0; - sec = NULL; } else { mcount_rela->offset = (unsigned int) (index * sizeof(*funcs)); } @@ -4185,19 +4134,21 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) switch(kelf->arch) { case AARCH64: { struct section *sec; - list_for_each_entry(sec, &kelf->sections, list) { - if (strcmp(sec->name, "__patchable_function_entries")) + if (strcmp(sec->name, "__patchable_function_entries")) { continue; - if (multi_pfe && sym->sec->pfe != sec) + } + if (sym->sec->pfe != sec) { continue; - if (!sec->rela) + } + if (!sec->rela) { continue; + } list_for_each_entry(rela, &sec->rela->relas, list) { if (rela->sym->sec && sym->sec == rela->sym->sec) { sym->has_func_profiling = 1; - goto next_symbol; + goto next_symbol; } } } @@ -4287,12 +4238,6 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) return 0; } -static bool has_multi_pfe(struct kpatch_elf *kelf) -{ - return !!find_nth_section_by_name(&kelf->sections, 1, - "__patchable_function_entries"); -} - static struct argp argp = { options, parse_opt, args_doc, NULL }; int main(int argc, char *argv[]) @@ -4326,7 +4271,10 @@ int main(int argc, char *argv[]) kelf_orig = kpatch_elf_open(orig_obj); kelf_patched = kpatch_elf_open(patched_obj); - multi_pfe = has_multi_pfe(kelf_orig) || has_multi_pfe(kelf_patched); + + kpatch_set_pfe_link(kelf_orig); + kpatch_set_pfe_link(kelf_patched); + kpatch_find_func_profiling_calls(kelf_orig); kpatch_find_func_profiling_calls(kelf_patched); @@ -4388,9 +4336,6 @@ int main(int argc, char *argv[]) /* this is destructive to kelf_patched */ kpatch_migrate_included_elements(kelf_patched, &kelf_out); - /* this must be done before kelf_patched is torn down */ - kpatch_alloc_mcount_sections(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 @@ -4409,7 +4354,7 @@ int main(int argc, char *argv[]) kpatch_create_callbacks_objname_rela(kelf_out, parent_name); kpatch_build_strings_section_data(kelf_out); - kpatch_populate_mcount_sections(kelf_out); + kpatch_create_mcount_sections(kelf_out); /* * At this point, the set of output sections and symbols is diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 049062bf5..a2223fc4a 100755 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -685,6 +685,7 @@ void kpatch_dump_kelf(struct kpatch_elf *kelf) if (sec->rela) printf(", rela-> %s", sec->rela->name); } + printf(", pfe-> [%d]", (sec->pfe) == NULL ? -1 : (int)sec->pfe->index); next: printf("\n"); } @@ -694,8 +695,10 @@ void kpatch_dump_kelf(struct kpatch_elf *kelf) 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)) + if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT)) { printf(" -> %s", sym->sec->name); + printf(", profiling: %d", sym->has_func_profiling); + } printf("\n"); } } @@ -964,6 +967,7 @@ struct section *create_section_pair(struct kpatch_elf *kelf, char *name, relasec->sh.sh_type = SHT_RELA; relasec->sh.sh_entsize = sizeof(GElf_Rela); relasec->sh.sh_addralign = 8; + relasec->sh.sh_flags = SHF_INFO_LINK; /* set text rela section pointer */ sec->rela = relasec; From 9b261ab52cff936665c46147dfaad3fba7b3d9d3 Mon Sep 17 00:00:00 2001 From: zimao Date: Wed, 8 Nov 2023 07:57:08 +0000 Subject: [PATCH 34/34] create-diff-object: add init value for pfe flag Set pfe_per_function default to false. --- kpatch-build/create-diff-object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index b699d5041..185b54a50 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -3805,7 +3805,7 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) struct symbol *sym; struct rela *rela, *mcount_rela; void **funcs; - bool pfe_per_function; + bool pfe_per_function = false; nr = 0; list_for_each_entry(sym, &kelf->symbols, list)