kmod/core: Skip relocations of already altered instructions

When a patch module is loaded, the kernel facilities like alternatives
and paravirt may alter some of its instructions. This happens before
Kpatch core module is notified and tries to apply dynrelas to it. If an
instruction to apply a dynrela to has already been changed by these
facilities, an incorrect instruction might be written as a result.

The core module now detects such conditions and does not apply dynrela
to the changed instructions.

Suggested by Josh Poimboeuf in the discussion of
https://github.com/dynup/kpatch/issues/580.

Changes in v.2:
* Used pr_notice to give more emphasis to the messages.
* Added an explanation message.

Signed-off-by: Evgenii Shatokhin <eshatokhin@virtuozzo.com>
This commit is contained in:
Evgenii Shatokhin 2016-03-28 12:11:59 +03:00
parent 7eda8dab6d
commit 8dac9d0871
1 changed files with 14 additions and 0 deletions

View File

@ -43,6 +43,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/string.h>
#include <asm/stacktrace.h> #include <asm/stacktrace.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include "kpatch.h" #include "kpatch.h"
@ -650,6 +651,19 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod,
return -EINVAL; return -EINVAL;
} }
/*
* Skip it if the instruction to be relocated has been
* changed already (paravirt or alternatives may do this).
*/
if (memchr_inv((void *)loc, 0, size)) {
pr_notice("Skipped dynrela for %s (0x%lx <- 0x%lx): the instruction has been changed already.\n",
dynrela->name, dynrela->dest, dynrela->src);
pr_notice_once(
"This is not necessarily a bug but it may indicate in some cases "
"that the binary patch does not handle paravirt operations, alternatives or the like properly.\n");
continue;
}
#ifdef CONFIG_DEBUG_SET_MODULE_RONX #ifdef CONFIG_DEBUG_SET_MODULE_RONX
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
if (loc < core + kpmod->mod->core_layout.ro_size) if (loc < core + kpmod->mod->core_layout.ro_size)