From 6571f2af93eae40762c64682d86511cb080923ba Mon Sep 17 00:00:00 2001 From: Sumanth Korikkar Date: Wed, 11 Feb 2026 15:25:51 +0100 Subject: [PATCH] create-diff-object: handle .rela__bug_table to avoid pulling in entire __bug_table When building shadow-pid.patch on a debug kernel, it generates __bug_table, which contains an array of struct bug_entries. create-diff-object identifies that .text.kernel_clone has changed and it includes .rela.text.kernel_clone rela section. Then later, it includes all symbols (in kpatch_include_symbols()) associated with it, which ends up including __bug_table and its rela section .rela__bug_table. Then, all the function symbols associated with .rela__bug_table is included irrespective of whether it's section is included or not. The problem occurs since WARN() has been converted to a static call, which passes a reference to its bug table entry (in __bug_table) to the static call. That direct reference from a function to a special section is new, and that's causing create-diff-object to incorrectly pull in the entire __bug_table. (As mentioned by Josh) It leads to the following modpost errors with shadow-pid.patch: kernel/fork.o: changed function: kernel_clone kernel/exit.o: changed function: do_exit fs/proc/array.o: changed function: proc_pid_status make -C /root/linux M=/root/.kpatch/tmp/patch CFLAGS_MODULE='' make[1]: Entering directory '/root/linux' make[2]: Entering directory '/root/.kpatch/tmp/patch' LDS kpatch.lds CC [M] patch-hook.o LD [M] test-shadow-newpid.o MODPOST Module.symvers WARNING: modpost: missing MODULE_DESCRIPTION() in test-shadow-newpid.o ERROR: modpost: "replace_mm_exe_file" [test-shadow-newpid.ko] undefined! ERROR: modpost: "put_task_stack" [test-shadow-newpid.ko] undefined! ERROR: modpost: "release_task" [test-shadow-newpid.ko] undefined! ERROR: modpost: "set_mm_exe_file" [test-shadow-newpid.ko] undefined! Examining the /root/.kpatch/patch/ directory reveals, these symbols are never referenced in any relas. readelf -Ws output.o | grep -E 'put_task_stack|replace_mm_exe_file|release_task|set_mm_exe_file' 27: 0000000000000000 0 SECTION LOCAL DEFAULT 36 .rodata.release_task.str1.2 45: 0000000000000000 0 SECTION LOCAL DEFAULT 55 .rodata.set_mm_exe_file.str1.2 47: 0000000000000000 0 SECTION LOCAL DEFAULT 57 .rodata.replace_mm_exe_file.str1.2 234: 0000000000000000 0 FUNC GLOBAL DEFAULT UND replace_mm_exe_file 254: 0000000000000000 0 FUNC GLOBAL DEFAULT UND put_task_stack 263: 0000000000000000 0 FUNC GLOBAL DEFAULT UND release_task 269: 0000000000000000 0 FUNC GLOBAL DEFAULT UND set_mm_exe_file readelf -Wr output.o | grep -E 'put_task_stack|replace_mm_exe_file|release_task|set_mm_exe_file' Solution: Skip initial symbol inclusion from .rela__bug_table to prevent unwanted __bug_table propagation caused by static call WARN() relocations. These relocations are instead handled later during special section regeneration. Also recalculate addends of relocations targeting __bug_table when bug table entries move, ensuring all references remain consistent. Signed-off-by: Sumanth Korikkar --- kpatch-build/create-diff-object.c | 74 ++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 56ade7ef..24603c6f 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1886,6 +1886,23 @@ static void kpatch_verify_patchability(struct kpatch_elf *kelf) DIFF_FATAL("%d unsupported section change(s)", errs); } +/* + * Do not perform symbol inclusion initially from .rela__bug_table. + * + * WARN() is converted to a static call which emits relocations that point + * directly into __bug_table+off. If the usual "include every symbol referenced + * by this rela section" rule is applied here, it could end up dragging in the + * entire __bug_table via propagation. + * + * .rela__bug_table relocations are processed later in + * kpatch_regenerate_special_section(), which also adjusts relocations + * targetting __bug_table. + */ +static bool kpatch_skip_symbol_inclusion_from_relasec(struct section *relasec) +{ + return !strcmp(relasec->name, ".rela__bug_table"); +} + static void kpatch_include_symbol(struct symbol *sym); static void kpatch_include_section(struct section *sec) @@ -1906,6 +1923,8 @@ static void kpatch_include_section(struct section *sec) if (!sec->rela) return; sec->rela->include = 1; + if (kpatch_skip_symbol_inclusion_from_relasec(sec->rela)) + return; list_for_each_entry(rela, &sec->rela->relas, list) kpatch_include_symbol(rela->sym); } @@ -2791,6 +2810,44 @@ static void kpatch_update_ex_table_addend(struct kpatch_elf *kelf, } } +static bool is_reloc_to_bug_table(struct rela *rela) +{ + return !strcmp(rela->sym->name, "__bug_table"); +} + +static void recalculate_bug_table_rela_addend(struct kpatch_elf *kelf, + long old_bug_offset, + long new_bug_offset) +{ + long add_offset, old_target_offset; + struct section *relasec; + struct rela *rela; + + list_for_each_entry(relasec, &kelf->sections, list) { + if (!is_rela_section(relasec) || + !relasec->include || + !strcmp(relasec->name, ".rela__bug_table")) + continue; + list_for_each_entry(rela, &relasec->relas, list) { + if (!is_reloc_to_bug_table(rela)) + continue; + old_target_offset = rela_target_offset(kelf, relasec, rela); + if (old_target_offset == old_bug_offset) { + add_offset = rela_target_offset(kelf, relasec, rela) - rela->addend; + rela->addend = new_bug_offset - add_offset; + rela->rela.r_addend = rela->addend; + log_debug("%s: adjusting rela from %s+%lx to %s+%lx\n", + relasec->name, + rela->sym->name, + old_bug_offset - add_offset, + rela->sym->name, + new_bug_offset - add_offset); + } + } + } + +} + static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, struct lookup_table *lookup, struct special_section *special, @@ -2798,7 +2855,7 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, { struct rela *rela, *safe; char *src, *dest; - unsigned int group_size, src_offset, dest_offset; + unsigned int group_size, src_offset, dest_offset, new_offset; LIST_HEAD(newrelas); @@ -2852,6 +2909,21 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, list_del(&rela->list); list_add_tail(&rela->list, &newrelas); + if (!strcmp(relasec->name, ".rela__bug_table")) { + new_offset = rela->offset - (src_offset - dest_offset); + /* + * When a bug table rela entry is + * reassigned to a new offset + * (rela->offset adjustment below), + * update the addends of all + * relocations targeting __bug_table so + * they continue to reference the + * updated bug entry. + */ + recalculate_bug_table_rela_addend(kelf, rela->offset, + new_offset); + } + rela->offset -= src_offset - dest_offset; rela->rela.r_offset = rela->offset;