From 16e4a4804715d3d49a268261126760a7f39a12d7 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 27 Jun 2023 10:48:42 -0400 Subject: [PATCH] kpatch-build: account for __pfx_-less NOP padding Some kernel configurations generate function NOP padding, but without associated __pfx_ symbols. For example: $ git describe HEAD v6.4-rc7-72-gdad9774deaf1 # Initial default config turns on Indirect Branch Tracking and 16-NOP padding bytes $ make defconfig $ grep -e CONFIG_X86_KERNEL_IBT -e CONFIG_FUNCTION_PADDING_BYTES .config CONFIG_X86_KERNEL_IBT=y CONFIG_FUNCTION_PADDING_BYTES=16 # Test .o build generates 16 bytes of NOPs but no "__pfx_" symbol $ make -j$(nproc) fs/proc/cmdline.o $ objdump -Dr -j .text fs/proc/cmdline.o | grep -e '<.*>:' -e 'Disassembly' Disassembly of section .text: 0000000000000000 : 0000000000000010 : This is because objtool operation on the object code may be delayed under certain configurations. As such, create-diff-object should be prepared to encounter NOP padded functions in object files before any __pfx_ symbols are created. Fixes: 3e54c63b175b ("create-diff-object: support x86 NOP-padded functions") Closes: #1347 ("x86 NOP padded functions without __pfx_ symbol") Reported-by: Ryan Sullivan Signed-off-by: Joe Lawrence --- kpatch-build/create-diff-object.c | 54 +++++++++++++++++++++++++++++-- kpatch-build/kpatch-elf.c | 4 +-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index a8182fe87..332e0c9f2 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -228,6 +228,54 @@ static struct rela *toc_rela(const struct rela *rela) (unsigned int)rela->addend); } +/* + * Some x86 kernels have NOP function padding, see upstream commit + * bea75b33895f ("x86/Kconfig: Introduce function padding"). + * Check for any amount of NOPs between the beginning of a function's + * section and its offset value within that section. + */ +static bool insn_is_nop_pad(struct kpatch_elf *kelf, void *addr, void *end, + unsigned long *insn_len) +{ + unsigned char *insn = addr; + + switch(kelf->arch) { + + case X86_64: + if (insn[0] == 0x90) { + *insn_len = 1; + return true; + } + break; + + case PPC64: + case S390: + /* kernel feature not present on these arches */ + return false; + + default: + ERROR("unsupported arch"); + } + + return false; +} + +bool is_function_nop_padded(struct kpatch_elf *kelf, struct symbol *sym) +{ + unsigned long offset, insn_len; + void *end = sym->sec->data->d_buf + sym->sym.st_value; + + if (sym->type != STT_FUNC) + return false; + + for (offset = 0; offset < sym->sym.st_value; offset += insn_len) { + if (!insn_is_nop_pad(kelf, sym->sec->data->d_buf + offset, end, &insn_len)) + return false; + } + + return true; +} + /* * When compiling with -ffunction-sections and -fdata-sections, almost every * symbol gets its own dedicated section. We call such symbols "bundled" @@ -236,7 +284,7 @@ static struct rela *toc_rela(const struct rela *rela) static void kpatch_bundle_symbols(struct kpatch_elf *kelf) { struct symbol *sym; - unsigned int expected_offset; + Elf64_Addr expected_offset; list_for_each_entry(sym, &kelf->symbols, list) { if (is_bundleable(sym)) { @@ -244,11 +292,13 @@ static void kpatch_bundle_symbols(struct kpatch_elf *kelf) expected_offset = 16; else if (is_gcc6_localentry_bundled_sym(kelf, sym)) expected_offset = 8; + else if (is_function_nop_padded(kelf, sym)) + expected_offset = sym->sym.st_value; else expected_offset = 0; if (sym->sym.st_value != expected_offset) { - ERROR("symbol %s at offset %lu within section %s, expected %u", + ERROR("symbol %s at offset %lu within section %s, expected %lu", sym->name, sym->sym.st_value, sym->sec->name, expected_offset); } diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 374d424cc..4686c00f4 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -399,7 +399,7 @@ static void kpatch_create_section_list(struct kpatch_elf *kelf) /* * Some x86 kernels have NOP function padding [1] for which objtool [2] - * adds ELF function symbols with prefix "__pfx_" to indicate the start + * may add ELF function symbols with prefix "__pfx_" to indicate the start * of a function, inclusive of NOP-padding. Find the prefix symbols and * link them to their corresponding function symbols at an expected * offset. @@ -438,7 +438,7 @@ static void kpatch_create_section_list(struct kpatch_elf *kelf) * 0000000000000000 0 SECTION LOCAL DEFAULT 99 .text.unlikely.__mmdrop * 0000000000000000 48 FUNC LOCAL DEFAULT 99 __mmdrop.cold * - * (kpatch-build generated tmp.ko, multple functions in one section, no __pfx_ symbols) + * (kpatch-build generated tmp.ko, multiple functions in one section, no __pfx_ symbols) * 0000000000000000 0 SECTION LOCAL DEFAULT 10 .text.unlikely.callback_info.isra.0 * 0000000000000010 65 FUNC LOCAL DEFAULT 10 callback_info.isra.0 * 0000000000000061 54 FUNC LOCAL DEFAULT 10 callback_info.isra.0