mirror of
https://github.com/dynup/kpatch
synced 2025-04-23 23:45:21 +00:00
Merge pull request #181 from spartacus06/interface-refactor
refactor core <-> patch interface
This commit is contained in:
commit
5ea376a47a
100
kmod/core/core.c
100
kmod/core/core.c
@ -105,6 +105,28 @@ enum {
|
||||
};
|
||||
static atomic_t kpatch_state;
|
||||
|
||||
enum kpatch_op {
|
||||
KPATCH_OP_NONE,
|
||||
KPATCH_OP_PATCH,
|
||||
KPATCH_OP_UNPATCH,
|
||||
};
|
||||
|
||||
struct kpatch_func {
|
||||
struct kpatch_patch *patch;
|
||||
struct hlist_node node;
|
||||
struct kpatch_module *kpmod;
|
||||
enum kpatch_op op;
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is allocated on a per registered module basis
|
||||
* and is stored in the struct kpatch_module "internal" field.
|
||||
* Any data associated with each registered module used internally
|
||||
* by this core module can be added here.
|
||||
*/
|
||||
struct kpatch_internal {
|
||||
struct kpatch_func *funcs;
|
||||
};
|
||||
|
||||
static inline void kpatch_state_idle(void)
|
||||
{
|
||||
@ -134,7 +156,7 @@ static struct kpatch_func *kpatch_get_func(unsigned long ip)
|
||||
|
||||
/* Here, we have to use rcu safe hlist because of NMI concurrency */
|
||||
hash_for_each_possible_rcu(kpatch_func_hash, f, node, ip)
|
||||
if (f->old_addr == ip)
|
||||
if (f->patch->old_addr == ip)
|
||||
return f;
|
||||
return NULL;
|
||||
}
|
||||
@ -143,7 +165,7 @@ static struct kpatch_func *kpatch_get_prev_func(struct kpatch_func *f,
|
||||
unsigned long ip)
|
||||
{
|
||||
hlist_for_each_entry_continue_rcu(f, node)
|
||||
if (f->old_addr == ip)
|
||||
if (f->patch->old_addr == ip)
|
||||
return f;
|
||||
return NULL;
|
||||
}
|
||||
@ -173,20 +195,20 @@ static void kpatch_backtrace_address_verify(void *data, unsigned long address,
|
||||
return;
|
||||
|
||||
/* check kpmod funcs */
|
||||
for (i = 0; i < kpmod->num_funcs; i++) {
|
||||
for (i = 0; i < kpmod->patches_nr; i++) {
|
||||
unsigned long func_addr, func_size;
|
||||
struct kpatch_func *active_func;
|
||||
|
||||
func = &kpmod->funcs[i];
|
||||
active_func = kpatch_get_func(func->old_addr);
|
||||
func = &kpmod->internal->funcs[i];
|
||||
active_func = kpatch_get_func(func->patch->old_addr);
|
||||
if (!active_func) {
|
||||
/* patching an unpatched func */
|
||||
func_addr = func->old_addr;
|
||||
func_size = func->old_size;
|
||||
func_addr = func->patch->old_addr;
|
||||
func_size = func->patch->old_size;
|
||||
} else {
|
||||
/* repatching or unpatching */
|
||||
func_addr = active_func->new_addr;
|
||||
func_size = active_func->new_size;
|
||||
func_addr = active_func->patch->new_addr;
|
||||
func_size = active_func->patch->new_size;
|
||||
}
|
||||
|
||||
args->ret = kpatch_compare_addresses(address, func_addr,
|
||||
@ -199,8 +221,8 @@ static void kpatch_backtrace_address_verify(void *data, unsigned long address,
|
||||
hash_for_each_rcu(kpatch_func_hash, i, func, node) {
|
||||
if (func->op == KPATCH_OP_UNPATCH) {
|
||||
args->ret = kpatch_compare_addresses(address,
|
||||
func->new_addr,
|
||||
func->new_size);
|
||||
func->patch->new_addr,
|
||||
func->patch->new_size);
|
||||
if (args->ret)
|
||||
return;
|
||||
}
|
||||
@ -251,8 +273,8 @@ out:
|
||||
static int kpatch_apply_patch(void *data)
|
||||
{
|
||||
struct kpatch_module *kpmod = data;
|
||||
struct kpatch_func *funcs = kpmod->funcs;
|
||||
int num_funcs = kpmod->num_funcs;
|
||||
struct kpatch_func *funcs = kpmod->internal->funcs;
|
||||
int num_funcs = kpmod->patches_nr;
|
||||
int i, ret;
|
||||
|
||||
ret = kpatch_verify_activeness_safety(kpmod);
|
||||
@ -264,7 +286,7 @@ static int kpatch_apply_patch(void *data)
|
||||
/* tentatively add the new funcs to the global func hash */
|
||||
for (i = 0; i < num_funcs; i++)
|
||||
hash_add_rcu(kpatch_func_hash, &funcs[i].node,
|
||||
funcs[i].old_addr);
|
||||
funcs[i].patch->old_addr);
|
||||
|
||||
/* memory barrier between func hash add and state change */
|
||||
smp_wmb();
|
||||
@ -291,8 +313,8 @@ static int kpatch_apply_patch(void *data)
|
||||
static int kpatch_remove_patch(void *data)
|
||||
{
|
||||
struct kpatch_module *kpmod = data;
|
||||
struct kpatch_func *funcs = kpmod->funcs;
|
||||
int num_funcs = kpmod->num_funcs;
|
||||
struct kpatch_func *funcs = kpmod->internal->funcs;
|
||||
int num_funcs = kpmod->patches_nr;
|
||||
int ret, i;
|
||||
|
||||
ret = kpatch_verify_activeness_safety(kpmod);
|
||||
@ -362,7 +384,7 @@ kpatch_ftrace_handler(unsigned long ip, unsigned long parent_ip,
|
||||
}
|
||||
done:
|
||||
if (func)
|
||||
regs->ip = func->new_addr;
|
||||
regs->ip = func->patch->new_addr;
|
||||
|
||||
preempt_enable_notrace();
|
||||
}
|
||||
@ -385,30 +407,40 @@ static void kpatch_remove_funcs_from_filter(struct kpatch_func *funcs,
|
||||
* If any other modules have also patched this function, don't
|
||||
* remove its ftrace handler.
|
||||
*/
|
||||
if (kpatch_get_func(func->old_addr))
|
||||
if (kpatch_get_func(func->patch->old_addr))
|
||||
continue;
|
||||
|
||||
/* Remove the ftrace handler for this function. */
|
||||
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops, func->old_addr,
|
||||
1, 0);
|
||||
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops,
|
||||
func->patch->old_addr, 1, 0);
|
||||
|
||||
WARN(ret, "can't remove ftrace filter at address 0x%lx (rc=%d)",
|
||||
func->old_addr, ret);
|
||||
func->patch->old_addr, ret);
|
||||
}
|
||||
}
|
||||
|
||||
int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
||||
{
|
||||
int ret, i;
|
||||
struct kpatch_func *funcs = kpmod->funcs;
|
||||
struct kpatch_func *func;
|
||||
int num_funcs = kpmod->num_funcs;
|
||||
struct kpatch_func *funcs, *func;
|
||||
int num_funcs = kpmod->patches_nr;
|
||||
|
||||
if (!kpmod->mod || !funcs || !num_funcs)
|
||||
if (!kpmod->mod || !kpmod->patches || !num_funcs)
|
||||
return -EINVAL;
|
||||
|
||||
kpmod->enabled = false;
|
||||
|
||||
kpmod->internal = kmalloc(sizeof(*kpmod->internal), GFP_KERNEL);
|
||||
if (!kpmod->internal)
|
||||
return -ENOMEM;
|
||||
|
||||
funcs = kmalloc(sizeof(*funcs) * num_funcs, GFP_KERNEL);
|
||||
if (!funcs) {
|
||||
kfree(kpmod->internal);
|
||||
return -ENOMEM;
|
||||
}
|
||||
kpmod->internal->funcs = funcs;
|
||||
|
||||
down(&kpatch_mutex);
|
||||
|
||||
if (!try_module_get(kpmod->mod)) {
|
||||
@ -421,20 +453,21 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
||||
|
||||
func->op = KPATCH_OP_PATCH;
|
||||
func->kpmod = kpmod;
|
||||
func->patch = &kpmod->patches[i];
|
||||
|
||||
/*
|
||||
* If any other modules have also patched this function, it
|
||||
* already has an ftrace handler.
|
||||
*/
|
||||
if (kpatch_get_func(func->old_addr))
|
||||
if (kpatch_get_func(func->patch->old_addr))
|
||||
continue;
|
||||
|
||||
/* Add an ftrace handler for this function. */
|
||||
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops, func->old_addr,
|
||||
0, 0);
|
||||
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops,
|
||||
func->patch->old_addr, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("can't set ftrace filter at address 0x%lx\n",
|
||||
func->old_addr);
|
||||
func->patch->old_addr);
|
||||
num_funcs = i;
|
||||
goto err_rollback;
|
||||
}
|
||||
@ -534,14 +567,16 @@ err_rollback:
|
||||
module_put(kpmod->mod);
|
||||
err_up:
|
||||
up(&kpatch_mutex);
|
||||
kfree(kpmod->internal->funcs);
|
||||
kfree(kpmod->internal);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(kpatch_register);
|
||||
|
||||
int kpatch_unregister(struct kpatch_module *kpmod)
|
||||
{
|
||||
struct kpatch_func *funcs = kpmod->funcs;
|
||||
int num_funcs = kpmod->num_funcs;
|
||||
struct kpatch_func *funcs = kpmod->internal->funcs;
|
||||
int num_funcs = kpmod->patches_nr;
|
||||
int i, ret;
|
||||
|
||||
if (!kpmod->enabled)
|
||||
@ -588,6 +623,9 @@ int kpatch_unregister(struct kpatch_module *kpmod)
|
||||
|
||||
kpatch_remove_funcs_from_filter(funcs, num_funcs);
|
||||
|
||||
kfree(kpmod->internal->funcs);
|
||||
kfree(kpmod->internal);
|
||||
|
||||
pr_notice("unloaded patch module \"%s\"\n", kpmod->mod->name);
|
||||
|
||||
kpmod->enabled = false;
|
||||
|
@ -23,34 +23,26 @@
|
||||
#ifndef _KPATCH_H_
|
||||
#define _KPATCH_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
enum kpatch_op {
|
||||
KPATCH_OP_NONE,
|
||||
KPATCH_OP_PATCH,
|
||||
KPATCH_OP_UNPATCH,
|
||||
};
|
||||
|
||||
struct kpatch_func {
|
||||
/* public */
|
||||
struct kpatch_patch {
|
||||
unsigned long new_addr;
|
||||
unsigned long new_size;
|
||||
unsigned long old_addr;
|
||||
unsigned long old_size;
|
||||
|
||||
/* private */
|
||||
struct hlist_node node;
|
||||
struct kpatch_module *kpmod;
|
||||
enum kpatch_op op;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
struct kpatch_internal;
|
||||
|
||||
struct kpatch_module {
|
||||
struct module *mod;
|
||||
struct kpatch_func *funcs;
|
||||
int num_funcs;
|
||||
|
||||
struct kpatch_patch *patches;
|
||||
int patches_nr;
|
||||
bool enabled;
|
||||
struct kpatch_internal *internal; /* used internally by core module */
|
||||
};
|
||||
|
||||
extern struct kobject *kpatch_patches_kobj;
|
||||
@ -58,4 +50,6 @@ extern struct kobject *kpatch_patches_kobj;
|
||||
extern int kpatch_register(struct kpatch_module *kpmod, bool replace);
|
||||
extern int kpatch_unregister(struct kpatch_module *kpmod);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _KPATCH_H_ */
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include "kpatch.h"
|
||||
#include "kpatch-patch.h"
|
||||
|
||||
static bool replace;
|
||||
module_param(replace, bool, S_IRUGO);
|
||||
@ -73,24 +72,12 @@ static struct kobj_attribute patch_enabled_attr =
|
||||
static int __init patch_init(void)
|
||||
{
|
||||
struct kpatch_patch *patches;
|
||||
int i, ret;
|
||||
|
||||
patches = (struct kpatch_patch *)&__kpatch_patches;
|
||||
int ret;
|
||||
|
||||
kpmod.mod = THIS_MODULE;
|
||||
kpmod.num_funcs = (&__kpatch_patches_end - &__kpatch_patches) /
|
||||
kpmod.patches = (struct kpatch_patch *)&__kpatch_patches;
|
||||
kpmod.patches_nr = (&__kpatch_patches_end - &__kpatch_patches) /
|
||||
sizeof(*patches);
|
||||
kpmod.funcs = kmalloc(kpmod.num_funcs * sizeof(struct kpatch_func),
|
||||
GFP_KERNEL);
|
||||
if (!kpmod.funcs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < kpmod.num_funcs; i++) {
|
||||
kpmod.funcs[i].old_addr = patches[i].old_addr;
|
||||
kpmod.funcs[i].old_size = patches[i].old_size;
|
||||
kpmod.funcs[i].new_addr = patches[i].new_addr;
|
||||
kpmod.funcs[i].new_size = patches[i].new_size;
|
||||
}
|
||||
|
||||
patch_kobj = kobject_create_and_add(THIS_MODULE->name,
|
||||
kpatch_patches_kobj);
|
||||
@ -114,7 +101,6 @@ err_sysfs:
|
||||
err_put:
|
||||
kobject_put(patch_kobj);
|
||||
err_free:
|
||||
kfree(kpmod.funcs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -123,7 +109,6 @@ static void __exit patch_exit(void)
|
||||
WARN_ON(kpmod.enabled);
|
||||
sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr);
|
||||
kobject_put(patch_kobj);
|
||||
kfree(kpmod.funcs);
|
||||
}
|
||||
|
||||
module_init(patch_init);
|
||||
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* kpatch-patch.h
|
||||
*
|
||||
* Copyright (C) 2014 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* Contains definitions needed for creating patch modules
|
||||
*/
|
||||
|
||||
#ifndef _KPATCH_PATCH_H_
|
||||
#define _KPATCH_PATCH_H_
|
||||
|
||||
struct kpatch_patch {
|
||||
unsigned long new_addr;
|
||||
unsigned long new_size;
|
||||
unsigned long old_addr;
|
||||
unsigned long old_size;
|
||||
};
|
||||
|
||||
#endif /* _KPATCH_PATCH_H_ */
|
@ -43,7 +43,7 @@
|
||||
#include <gelf.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "kpatch-patch.h"
|
||||
#include "kpatch.h"
|
||||
|
||||
#define ERROR(format, ...) \
|
||||
error(1, 0, "%s: %d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
|
Loading…
Reference in New Issue
Block a user