From f5f547961414eafcc3e2661bd2764907be424ab2 Mon Sep 17 00:00:00 2001 From: Evgenii Shatokhin Date: Fri, 17 Jan 2020 18:32:11 +0300 Subject: [PATCH] create-diff-object: fix relocations used for ZERO_PAGE(0) On x86_64, GCC generates the following instruction to compute 'empty_zero_page - __START_KERNEL_map' (__phys_addr_nodebug(), used in the implementation of ZERO_PAGE()): 48 ba 00 00 00 00 00 00 00 00 movabs $0x0,%rdx R_X86_64_64 empty_zero_page+0x80000000 __START_KERNEL_map is 0xffffffff80000000. However, the relocation addend becomes wrong in the patch module: 48 ba 00 00 00 00 00 00 00 00 movabs $0x0,%rdx R_X86_64_64 empty_zero_page-0x80000000 Note the sign of the addend. As a result, ZERO_PAGE(0) returns a wrong value in any function touched by the patch, which may lead to memory corruption and difficult-to-debug kernel crashes. The cause is that 'struct rela' uses 'int' for the addend, which is not enough to store such values. r_addend from Elf64_Rela is int64_t (Elf64_Sxword) for that. Let us use 'long' instead of 'int' for the addend in 'struct rela'. v2: * Moved 'addend' field after 'offset' in struct rela to facilitate structure packing (suggested by Kamalesh Babulal). Fixes https://github.com/dynup/kpatch/issues/1064. Signed-off-by: Evgenii Shatokhin --- kpatch-build/create-diff-object.c | 12 ++++++------ kpatch-build/kpatch-elf.c | 10 +++++----- kpatch-build/kpatch-elf.h | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 0efe881..c9d2cc6 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -374,14 +374,14 @@ static int rela_equal(struct rela *rela1, struct rela *rela2) */ memcpy(&toc_data1, rela1->sym->sec->data->d_buf + rela1->addend, sizeof(toc_data1)); if (!toc_data1) - ERROR(".toc entry not found %s + %x", rela1->sym->name, rela1->addend); + ERROR(".toc entry not found %s + %lx", rela1->sym->name, rela1->addend); } rela_toc2 = toc_rela(rela2); if (!rela_toc2) { memcpy(&toc_data2, rela2->sym->sec->data->d_buf + rela2->addend, sizeof(toc_data2)); if (!toc_data2) - ERROR(".toc entry not found %s + %x", rela2->sym->name, rela2->addend); + ERROR(".toc entry not found %s + %lx", rela2->sym->name, rela2->addend); } if (!rela_toc1 && !rela_toc2) @@ -1369,7 +1369,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) rela->addend + add_off >= end) continue; - log_debug("%s: replacing %s+%d reference with %s+%d\n", + log_debug("%s: replacing %s+%ld reference with %s+%ld\n", sec->name, rela->sym->name, rela->addend, sym->name, rela->addend - start); @@ -2070,7 +2070,7 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, if (is_dynamic_debug_symbol(key->sym)) continue; - ERROR("Found a jump label at %s()+0x%x, using key %s. Jump labels aren't currently supported. Use static_key_enabled() instead.", + ERROR("Found a jump label at %s()+0x%lx, using key %s. Jump labels aren't currently supported. Use static_key_enabled() instead.", code->sym->name, code->addend, key->sym->name); continue; @@ -2237,8 +2237,8 @@ static void kpatch_check_relocations(struct kpatch_elf *kelf) list_for_each_entry(rela, &sec->relas, list) { if (rela->sym->sec) { sdata = rela->sym->sec->data; - if (rela->addend > (int)sdata->d_size) { - ERROR("out-of-range relocation %s+%x in %s", rela->sym->sec->name, + if (rela->addend > (long)sdata->d_size) { + ERROR("out-of-range relocation %s+%lx in %s", rela->sym->sec->name, rela->addend, sec->name); } } diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 9827c9b..1c0e099 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -194,15 +194,15 @@ void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec) (rela->sym->sec->sh.sh_flags & SHF_STRINGS)) { rela->string = rela->sym->sec->data->d_buf + rela->addend; if (!rela->string) - ERROR("could not lookup rela string for %s+%d", + ERROR("could not lookup rela string for %s+%ld", rela->sym->name, rela->addend); } if (skip) continue; - log_debug("offset %d, type %d, %s %s %d", rela->offset, + log_debug("offset %d, type %d, %s %s %ld", rela->offset, rela->type, rela->sym->name, - (rela->addend < 0)?"-":"+", abs(rela->addend)); + (rela->addend < 0)?"-":"+", labs(rela->addend)); if (rela->string) log_debug(" (string = %s)", rela->string); log_debug("\n"); @@ -403,11 +403,11 @@ void kpatch_dump_kelf(struct kpatch_elf *kelf) goto next; printf("rela section expansion\n"); list_for_each_entry(rela, &sec->relas, list) { - printf("sym %d, offset %d, type %d, %s %s %d\n", + printf("sym %d, offset %d, type %d, %s %s %ld\n", rela->sym->index, rela->offset, rela->type, rela->sym->name, (rela->addend < 0)?"-":"+", - abs(rela->addend)); + labs(rela->addend)); } } else { if (sec->sym) diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index 590aa6c..3a0dc4b 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -90,8 +90,8 @@ struct rela { GElf_Rela rela; struct symbol *sym; unsigned int type; - int addend; unsigned int offset; + long addend; char *string; bool need_dynrela; };