mirror of https://github.com/dynup/kpatch
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 <eshatokhin@virtuozzo.com>
This commit is contained in:
parent
34a45ba847
commit
f5f5479614
|
@ -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));
|
memcpy(&toc_data1, rela1->sym->sec->data->d_buf + rela1->addend, sizeof(toc_data1));
|
||||||
if (!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);
|
rela_toc2 = toc_rela(rela2);
|
||||||
if (!rela_toc2) {
|
if (!rela_toc2) {
|
||||||
memcpy(&toc_data2, rela2->sym->sec->data->d_buf + rela2->addend, sizeof(toc_data2));
|
memcpy(&toc_data2, rela2->sym->sec->data->d_buf + rela2->addend, sizeof(toc_data2));
|
||||||
if (!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)
|
if (!rela_toc1 && !rela_toc2)
|
||||||
|
@ -1369,7 +1369,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
||||||
rela->addend + add_off >= end)
|
rela->addend + add_off >= end)
|
||||||
continue;
|
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,
|
sec->name,
|
||||||
rela->sym->name, rela->addend,
|
rela->sym->name, rela->addend,
|
||||||
sym->name, rela->addend - start);
|
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))
|
if (is_dynamic_debug_symbol(key->sym))
|
||||||
continue;
|
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);
|
code->sym->name, code->addend, key->sym->name);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
@ -2237,8 +2237,8 @@ static void kpatch_check_relocations(struct kpatch_elf *kelf)
|
||||||
list_for_each_entry(rela, &sec->relas, list) {
|
list_for_each_entry(rela, &sec->relas, list) {
|
||||||
if (rela->sym->sec) {
|
if (rela->sym->sec) {
|
||||||
sdata = rela->sym->sec->data;
|
sdata = rela->sym->sec->data;
|
||||||
if (rela->addend > (int)sdata->d_size) {
|
if (rela->addend > (long)sdata->d_size) {
|
||||||
ERROR("out-of-range relocation %s+%x in %s", rela->sym->sec->name,
|
ERROR("out-of-range relocation %s+%lx in %s", rela->sym->sec->name,
|
||||||
rela->addend, sec->name);
|
rela->addend, sec->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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->sym->sec->sh.sh_flags & SHF_STRINGS)) {
|
||||||
rela->string = rela->sym->sec->data->d_buf + rela->addend;
|
rela->string = rela->sym->sec->data->d_buf + rela->addend;
|
||||||
if (!rela->string)
|
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);
|
rela->sym->name, rela->addend);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skip)
|
if (skip)
|
||||||
continue;
|
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->type, rela->sym->name,
|
||||||
(rela->addend < 0)?"-":"+", abs(rela->addend));
|
(rela->addend < 0)?"-":"+", labs(rela->addend));
|
||||||
if (rela->string)
|
if (rela->string)
|
||||||
log_debug(" (string = %s)", rela->string);
|
log_debug(" (string = %s)", rela->string);
|
||||||
log_debug("\n");
|
log_debug("\n");
|
||||||
|
@ -403,11 +403,11 @@ void kpatch_dump_kelf(struct kpatch_elf *kelf)
|
||||||
goto next;
|
goto next;
|
||||||
printf("rela section expansion\n");
|
printf("rela section expansion\n");
|
||||||
list_for_each_entry(rela, &sec->relas, list) {
|
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->sym->index, rela->offset,
|
||||||
rela->type, rela->sym->name,
|
rela->type, rela->sym->name,
|
||||||
(rela->addend < 0)?"-":"+",
|
(rela->addend < 0)?"-":"+",
|
||||||
abs(rela->addend));
|
labs(rela->addend));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (sec->sym)
|
if (sec->sym)
|
||||||
|
|
|
@ -90,8 +90,8 @@ struct rela {
|
||||||
GElf_Rela rela;
|
GElf_Rela rela;
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
int addend;
|
|
||||||
unsigned int offset;
|
unsigned int offset;
|
||||||
|
long addend;
|
||||||
char *string;
|
char *string;
|
||||||
bool need_dynrela;
|
bool need_dynrela;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue