Merge pull request #398 from flaming-toast/patch-reenable

re-enable forced patch modules
This commit is contained in:
Seth Jennings 2014-09-09 12:05:30 -05:00
commit c21cc1292f
6 changed files with 82 additions and 21 deletions

View File

@ -810,12 +810,16 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
down(&kpatch_mutex);
kpmod->enabled = false;
if (kpmod->enabled) {
ret = -EINVAL;
goto err_up;
}
list_add_tail(&kpmod->list, &kpmod_list);
if (!try_module_get(kpmod->mod)) {
ret = -ENODEV;
goto err_up;
goto err_list;
}
list_for_each_entry(object, &kpmod->objects, list) {
@ -931,8 +935,9 @@ err_unlink:
if (kpatch_object_linked(object))
kpatch_unlink_object(object);
module_put(kpmod->mod);
err_up:
err_list:
list_del(&kpmod->list);
err_up:
up(&kpatch_mutex);
return ret;
}
@ -944,11 +949,13 @@ int kpatch_unregister(struct kpatch_module *kpmod)
struct kpatch_func *func;
int ret, force = 0;
if (!kpmod->enabled)
return -EINVAL;
down(&kpatch_mutex);
if (!kpmod->enabled) {
ret = -EINVAL;
goto out;
}
do_for_each_linked_func(kpmod, func) {
func->op = KPATCH_OP_UNPATCH;
if (func->force)

View File

@ -35,6 +35,7 @@ extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[];
extern struct kpatch_patch_hook __kpatch_hooks_load[], __kpatch_hooks_load_end[];
extern struct kpatch_patch_hook __kpatch_hooks_unload[], __kpatch_hooks_unload_end[];
extern unsigned long __kpatch_force_funcs[], __kpatch_force_funcs_end[];
extern char __kpatch_checksum[];
static struct kpatch_module kpmod;
static struct kobject *patch_kobj;
@ -60,29 +61,43 @@ static ssize_t patch_enabled_store(struct kobject *kobj,
int ret;
unsigned long val;
/* only disabling is supported */
if (!kpmod.enabled)
return -EINVAL;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
val = !!val;
/* only disabling is supported */
if (val)
return -EINVAL;
ret = kpatch_register(&kpmod, replace);
else
ret = kpatch_unregister(&kpmod);
ret = kpatch_unregister(&kpmod);
if (ret)
return ret;
return count;
}
static ssize_t checksum_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", __kpatch_checksum);
}
static struct kobj_attribute patch_enabled_attr =
__ATTR(enabled, 0644, patch_enabled_show, patch_enabled_store);
static struct kobj_attribute checksum_attr =
__ATTR(checksum, 0444, checksum_show, NULL);
static struct attribute *kpatch_attrs[] = {
&patch_enabled_attr.attr,
&checksum_attr.attr,
NULL,
};
static struct attribute_group kpatch_attr_group = {
.attrs = kpatch_attrs,
};
static ssize_t func_old_addr_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
@ -356,14 +371,10 @@ static int __init patch_init(void)
if (!patch_kobj)
return -ENOMEM;
ret = sysfs_create_file(patch_kobj, &patch_enabled_attr.attr);
if (ret)
goto err_patch;
functions_kobj = kobject_create_and_add("functions", patch_kobj);
if (!functions_kobj) {
ret = -ENOMEM;
goto err_sysfs;
goto err_patch;
}
kpmod.mod = THIS_MODULE;
@ -385,13 +396,17 @@ static int __init patch_init(void)
if (ret)
goto err_objects;
ret = sysfs_create_group(patch_kobj, &kpatch_attr_group);
if (ret)
goto err_sysfs;
return 0;
err_sysfs:
kpatch_unregister(&kpmod);
err_objects:
patch_free_objects();
kobject_put(functions_kobj);
err_sysfs:
sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr);
err_patch:
kobject_put(patch_kobj);
return ret;
@ -403,7 +418,7 @@ static void __exit patch_exit(void)
patch_free_objects();
kobject_put(functions_kobj);
sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr);
sysfs_remove_group(patch_kobj, &kpatch_attr_group);
kobject_put(patch_kobj);
}

View File

@ -2,6 +2,7 @@ __kpatch_funcs = ADDR(.kpatch.funcs);
__kpatch_funcs_end = ADDR(.kpatch.funcs) + SIZEOF(.kpatch.funcs);
__kpatch_dynrelas = ADDR(.kpatch.dynrelas);
__kpatch_dynrelas_end = ADDR(.kpatch.dynrelas) + SIZEOF(.kpatch.dynrelas);
__kpatch_checksum = ADDR(.kpatch.checksum);
SECTIONS
{
.kpatch.hooks.load : {

View File

@ -476,6 +476,9 @@ cd "$SRCDIR"
make prepare >> "$LOGFILE" 2>&1 || die
cd "$TEMPDIR/output"
ld -r -o ../patch/output.o $(find . -name "*.o") >> "$LOGFILE" 2>&1 || die
md5sum ../patch/output.o | awk '{printf "%s\0", $1}' > checksum.tmp || die
objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/output.o || die
rm -f checksum.tmp
cd "$TEMPDIR/patch"
KPATCH_BUILD="$SRCDIR" KPATCH_NAME="$PATCHNAME" KBUILD_EXTRA_SYMBOLS="$SYMVERSFILE" make "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die

View File

@ -121,6 +121,21 @@ core_module_loaded () {
grep -q "T kpatch_register" /proc/kallsyms
}
get_module_name () {
echo $(readelf -p .gnu.linkonce.this_module $1 | grep '\[.*\]' | awk '{print $3}')
}
verify_module_checksum () {
modname=$(get_module_name $1)
[[ -z $modname ]] && return 1
checksum=$(readelf -p .kpatch.checksum $1 | grep '\[.*\]' | awk '{print $3}')
[[ -z $checksum ]] && return 1
sysfs_checksum=$(cat /sys/kernel/kpatch/patches/${modname}/checksum)
[[ $checksum == $sysfs_checksum ]] || return 1
}
load_module () {
if ! core_module_loaded; then
if modprobe -q kpatch; then
@ -131,6 +146,23 @@ load_module () {
insmod "$COREMOD" || die "failed to load core module"
fi
fi
modname=$(get_module_name $1)
moddir=/sys/kernel/kpatch/patches/$modname
if [[ -d $moddir ]] ; then
if [[ $(cat "${moddir}/enabled") -eq 0 ]]; then
if verify_module_checksum $1; then # same checksum
echo "module already loaded, re-enabling"
echo 1 > ${moddir}/enabled || die "failed to re-enable module $modname"
return
else
die "error: cannot re-enable patch module $modname, cannot verify checksum match"
fi
else
die "error: module named $modname already loaded and enabled"
fi
fi
echo "loading patch module: $1"
insmod "$1" "$2"
}

View File

@ -17,6 +17,9 @@ make || exit 1
cd ../../kmod/patch || exit 1
make clean || exit 1
cp ../../test/testmod/output.o . || exit 1
md5sum output.o | awk '{printf "%s\0", $1}' > checksum.tmp || exit 1
objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly output.o || exit 1
rm -f checksum.tmp
KBUILD_EXTRA_SYMBOLS="$(readlink -e ../../kmod/core/Module.symvers)" make || exit 1
cd ../../test/testmod