mirror of
https://github.com/dynup/kpatch
synced 2025-01-18 02:50:49 +00:00
Merge pull request #1002 from jpoimboe/ppc-sibling
create-diff-object/ppc64le: Don't allow sibling calls
This commit is contained in:
commit
f96691f97a
@ -3253,6 +3253,60 @@ static void kpatch_build_strings_section_data(struct kpatch_elf *kelf)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't allow sibling calls from patched functions on ppc64le. Before doing a
|
||||
* sibling call, the patched function restores the stack to its caller's stack.
|
||||
* The kernel-generated stub then writes the patch module's r2 (toc) value to
|
||||
* the caller's stack, corrupting it, eventually causing a panic after it
|
||||
* returns to the caller and the caller tries to use the livepatch module's toc
|
||||
* value.
|
||||
*
|
||||
* In theory we could instead a) generate a custom stub, or b) modify the
|
||||
* kernel livepatch_handler code to save/restore the stack r2 value, but this
|
||||
* is easier for now.
|
||||
*/
|
||||
static void kpatch_no_sibling_calls_ppc64le(struct kpatch_elf *kelf)
|
||||
{
|
||||
#ifdef __powerpc64__
|
||||
struct symbol *sym;
|
||||
unsigned int insn;
|
||||
unsigned long offset;
|
||||
|
||||
list_for_each_entry(sym, &kelf->symbols, list) {
|
||||
if (sym->type != STT_FUNC || sym->status != CHANGED)
|
||||
continue;
|
||||
|
||||
for (offset = 0; offset < sym->sec->data->d_size; offset += 4) {
|
||||
|
||||
insn = *(unsigned int *)(sym->sec->data->d_buf + offset);
|
||||
|
||||
/*
|
||||
* The instruction 0x48000000 can be assumed to be a
|
||||
* sibling call:
|
||||
*
|
||||
* Bits 0-5 (opcode) == 0x9: unconditional branch
|
||||
* Bit 30 (absolute) == 0: relative address
|
||||
* Bit 31 (link) == 0: doesn't set LR (not a call)
|
||||
*
|
||||
* Bits 6-29 (branch address) == zero, which means
|
||||
* it's either a branch to self (infinite loop), or
|
||||
* there's a REL24 relocation for the address which
|
||||
* will be written by the linker or the kernel.
|
||||
*/
|
||||
if (insn != 0x48000000)
|
||||
continue;
|
||||
|
||||
/* Make sure it's not a branch-to-self: */
|
||||
if (!find_rela_by_offset(sym->sec->rela, offset))
|
||||
continue;
|
||||
|
||||
ERROR("Found an unsupported sibling call at %s()+0x%lx. Add __attribute__((optimize(\"-fno-optimize-sibling-calls\"))) to %s() definition.",
|
||||
sym->name, sym->sym.st_value + offset, sym->name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct arguments {
|
||||
char *args[7];
|
||||
int debug;
|
||||
@ -3414,6 +3468,8 @@ int main(int argc, char *argv[])
|
||||
free(base_locals);
|
||||
free(hint);
|
||||
|
||||
kpatch_no_sibling_calls_ppc64le(kelf_out);
|
||||
|
||||
/* create strings, patches, and dynrelas sections */
|
||||
kpatch_create_strings_elements(kelf_out);
|
||||
kpatch_create_patches_sections(kelf_out, lookup, parent_name);
|
||||
|
@ -14,7 +14,7 @@ diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c
|
||||
return (b - buf) ? b - buf : retval;
|
||||
}
|
||||
|
||||
+static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
|
||||
+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);
|
||||
|
@ -15,7 +15,7 @@ Index: kernel-rhel7/drivers/tty/n_tty.c
|
||||
return (b - buf) ? b - buf : retval;
|
||||
}
|
||||
|
||||
+static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
|
||||
+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);
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit aeb01db1590089298858f019be317d63cf04b45b
|
||||
Subproject commit 8ef8049ca314b5f9ffd19832992d4d4899b044a2
|
Loading…
Reference in New Issue
Block a user