Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions kmod/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -931,8 +935,9 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
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;
}
Expand All @@ -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)
Expand Down
45 changes: 30 additions & 15 deletions kmod/patch/kpatch-patch-hook.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
}

Expand Down
1 change: 1 addition & 0 deletions kmod/patch/kpatch.lds
Original file line number Diff line number Diff line change
Expand Up @@ -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 : {
Expand Down
3 changes: 3 additions & 0 deletions kpatch-build/kpatch-build
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,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

Expand Down
32 changes: 32 additions & 0 deletions kpatch/kpatch
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
}
Expand Down
3 changes: 3 additions & 0 deletions test/testmod/doit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down