|
|
576586 |
From b0058988c7e57627ae64ace40377de6da4fa14b9 Mon Sep 17 00:00:00 2001
|
|
|
576586 |
From: Jessica Yu <jyu@cowsay.org>
|
|
|
576586 |
Date: Thu, 4 Sep 2014 07:38:17 -0700
|
|
|
576586 |
Subject: [PATCH 3/3] re-enable patch modules with checksum matching
|
|
|
576586 |
To: rhkernel-list@redhat.com
|
|
|
576586 |
|
|
|
576586 |
In order to safely re-enable patch modules, add a special
|
|
|
576586 |
.kpatch.checksum section containing an md5sum of a patch module's
|
|
|
576586 |
contents. The contents of this section are exported to sysfs via
|
|
|
576586 |
patch_init and double checked when kpatch load finds that a module of
|
|
|
576586 |
the same name is already loaded.
|
|
|
576586 |
|
|
|
576586 |
Conflicts:
|
|
|
576586 |
kpatch-build/kpatch-build
|
|
|
576586 |
---
|
|
|
576586 |
kmod/core/core.c | 19 ++++++++++++------
|
|
|
576586 |
kmod/patch/kpatch-patch-hook.c | 45 ++++++++++++++++++++++++++++--------------
|
|
|
576586 |
kmod/patch/kpatch.lds | 1 +
|
|
|
576586 |
kpatch-build/kpatch-build | 5 ++++-
|
|
|
576586 |
kpatch/kpatch | 32 ++++++++++++++++++++++++++++++
|
|
|
576586 |
5 files changed, 80 insertions(+), 22 deletions(-)
|
|
|
576586 |
|
|
|
576586 |
diff --git a/kmod/core/core.c b/kmod/core/core.c
|
|
|
576586 |
index 8c1bb37..bb624a9 100644
|
|
|
576586 |
--- a/kmod/core/core.c
|
|
|
576586 |
+++ b/kmod/core/core.c
|
|
|
576586 |
@@ -811,12 +811,16 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
|
|
576586 |
|
|
|
576586 |
down(&kpatch_mutex);
|
|
|
576586 |
|
|
|
576586 |
- kpmod->enabled = false;
|
|
|
576586 |
+ if (kpmod->enabled) {
|
|
|
576586 |
+ ret = -EINVAL;
|
|
|
576586 |
+ goto err_up;
|
|
|
576586 |
+ }
|
|
|
576586 |
+
|
|
|
576586 |
list_add_tail(&kpmod->list, &kpmod_list);
|
|
|
576586 |
|
|
|
576586 |
if (!try_module_get(kpmod->mod)) {
|
|
|
576586 |
ret = -ENODEV;
|
|
|
576586 |
- goto err_up;
|
|
|
576586 |
+ goto err_list;
|
|
|
576586 |
}
|
|
|
576586 |
|
|
|
576586 |
list_for_each_entry(object, &kpmod->objects, list) {
|
|
|
576586 |
@@ -932,8 +936,9 @@ err_unlink:
|
|
|
576586 |
if (kpatch_object_linked(object))
|
|
|
576586 |
kpatch_unlink_object(object);
|
|
|
576586 |
module_put(kpmod->mod);
|
|
|
576586 |
-err_up:
|
|
|
576586 |
+err_list:
|
|
|
576586 |
list_del(&kpmod->list);
|
|
|
576586 |
+err_up:
|
|
|
576586 |
up(&kpatch_mutex);
|
|
|
576586 |
return ret;
|
|
|
576586 |
}
|
|
|
576586 |
@@ -945,11 +950,13 @@ int kpatch_unregister(struct kpatch_module *kpmod)
|
|
|
576586 |
struct kpatch_func *func;
|
|
|
576586 |
int ret, force = 0;
|
|
|
576586 |
|
|
|
576586 |
- if (!kpmod->enabled)
|
|
|
576586 |
- return -EINVAL;
|
|
|
576586 |
-
|
|
|
576586 |
down(&kpatch_mutex);
|
|
|
576586 |
|
|
|
576586 |
+ if (!kpmod->enabled) {
|
|
|
576586 |
+ ret = -EINVAL;
|
|
|
576586 |
+ goto out;
|
|
|
576586 |
+ }
|
|
|
576586 |
+
|
|
|
576586 |
do_for_each_linked_func(kpmod, func) {
|
|
|
576586 |
func->op = KPATCH_OP_UNPATCH;
|
|
|
576586 |
if (func->force)
|
|
|
576586 |
diff --git a/kmod/patch/kpatch-patch-hook.c b/kmod/patch/kpatch-patch-hook.c
|
|
|
576586 |
index ea68878..1370223 100644
|
|
|
576586 |
--- a/kmod/patch/kpatch-patch-hook.c
|
|
|
576586 |
+++ b/kmod/patch/kpatch-patch-hook.c
|
|
|
576586 |
@@ -35,6 +35,7 @@ extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[];
|
|
|
576586 |
extern struct kpatch_patch_hook __kpatch_hooks_load[], __kpatch_hooks_load_end[];
|
|
|
576586 |
extern struct kpatch_patch_hook __kpatch_hooks_unload[], __kpatch_hooks_unload_end[];
|
|
|
576586 |
extern unsigned long __kpatch_force_funcs[], __kpatch_force_funcs_end[];
|
|
|
576586 |
+extern char __kpatch_checksum[];
|
|
|
576586 |
|
|
|
576586 |
static struct kpatch_module kpmod;
|
|
|
576586 |
static struct kobject *patch_kobj;
|
|
|
576586 |
@@ -61,29 +62,43 @@ static ssize_t patch_enabled_store(struct kobject *kobj,
|
|
|
576586 |
int ret;
|
|
|
576586 |
unsigned long val;
|
|
|
576586 |
|
|
|
576586 |
- /* only disabling is supported */
|
|
|
576586 |
- if (!kpmod.enabled)
|
|
|
576586 |
- return -EINVAL;
|
|
|
576586 |
-
|
|
|
576586 |
ret = kstrtoul(buf, 10, &val;;
|
|
|
576586 |
if (ret)
|
|
|
576586 |
return ret;
|
|
|
576586 |
|
|
|
576586 |
val = !!val;
|
|
|
576586 |
|
|
|
576586 |
- /* only disabling is supported */
|
|
|
576586 |
if (val)
|
|
|
576586 |
- return -EINVAL;
|
|
|
576586 |
+ ret = kpatch_register(&kpmod, replace);
|
|
|
576586 |
+ else
|
|
|
576586 |
+ ret = kpatch_unregister(&kpmod);
|
|
|
576586 |
|
|
|
576586 |
- ret = kpatch_unregister(&kpmod);
|
|
|
576586 |
if (ret)
|
|
|
576586 |
return ret;
|
|
|
576586 |
|
|
|
576586 |
return count;
|
|
|
576586 |
}
|
|
|
576586 |
|
|
|
576586 |
+static ssize_t checksum_show(struct kobject *kobj,
|
|
|
576586 |
+ struct kobj_attribute *attr, char *buf)
|
|
|
576586 |
+{
|
|
|
576586 |
+ return snprintf(buf, PAGE_SIZE, "%s\n", __kpatch_checksum);
|
|
|
576586 |
+}
|
|
|
576586 |
+
|
|
|
576586 |
static struct kobj_attribute patch_enabled_attr =
|
|
|
576586 |
__ATTR(enabled, 0644, patch_enabled_show, patch_enabled_store);
|
|
|
576586 |
+static struct kobj_attribute checksum_attr =
|
|
|
576586 |
+ __ATTR(checksum, 0444, checksum_show, NULL);
|
|
|
576586 |
+
|
|
|
576586 |
+static struct attribute *kpatch_attrs[] = {
|
|
|
576586 |
+ &patch_enabled_attr.attr,
|
|
|
576586 |
+ &checksum_attr.attr,
|
|
|
576586 |
+ NULL,
|
|
|
576586 |
+};
|
|
|
576586 |
+
|
|
|
576586 |
+static struct attribute_group kpatch_attr_group = {
|
|
|
576586 |
+ .attrs = kpatch_attrs,
|
|
|
576586 |
+};
|
|
|
576586 |
|
|
|
576586 |
static ssize_t func_old_addr_show(struct kobject *kobj,
|
|
|
576586 |
struct kobj_attribute *attr, char *buf)
|
|
|
576586 |
@@ -354,14 +369,10 @@ static int __init patch_init(void)
|
|
|
576586 |
if (!patch_kobj)
|
|
|
576586 |
return -ENOMEM;
|
|
|
576586 |
|
|
|
576586 |
- ret = sysfs_create_file(patch_kobj, &patch_enabled_attr.attr);
|
|
|
576586 |
- if (ret)
|
|
|
576586 |
- goto err_patch;
|
|
|
576586 |
-
|
|
|
576586 |
functions_kobj = kobject_create_and_add("functions", patch_kobj);
|
|
|
576586 |
if (!functions_kobj) {
|
|
|
576586 |
ret = -ENOMEM;
|
|
|
576586 |
- goto err_sysfs;
|
|
|
576586 |
+ goto err_patch;
|
|
|
576586 |
}
|
|
|
576586 |
|
|
|
576586 |
kpmod.mod = THIS_MODULE;
|
|
|
576586 |
@@ -383,13 +394,17 @@ static int __init patch_init(void)
|
|
|
576586 |
if (ret)
|
|
|
576586 |
goto err_objects;
|
|
|
576586 |
|
|
|
576586 |
+ ret = sysfs_create_group(patch_kobj, &kpatch_attr_group);
|
|
|
576586 |
+ if (ret)
|
|
|
576586 |
+ goto err_sysfs;
|
|
|
576586 |
+
|
|
|
576586 |
return 0;
|
|
|
576586 |
|
|
|
576586 |
+err_sysfs:
|
|
|
576586 |
+ kpatch_unregister(&kpmod);
|
|
|
576586 |
err_objects:
|
|
|
576586 |
patch_free_objects();
|
|
|
576586 |
kobject_put(functions_kobj);
|
|
|
576586 |
-err_sysfs:
|
|
|
576586 |
- sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr);
|
|
|
576586 |
err_patch:
|
|
|
576586 |
kobject_put(patch_kobj);
|
|
|
576586 |
return ret;
|
|
|
576586 |
@@ -401,7 +416,7 @@ static void __exit patch_exit(void)
|
|
|
576586 |
|
|
|
576586 |
patch_free_objects();
|
|
|
576586 |
kobject_put(functions_kobj);
|
|
|
576586 |
- sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr);
|
|
|
576586 |
+ sysfs_remove_group(patch_kobj, &kpatch_attr_group);
|
|
|
576586 |
kobject_put(patch_kobj);
|
|
|
576586 |
}
|
|
|
576586 |
|
|
|
576586 |
diff --git a/kmod/patch/kpatch.lds b/kmod/patch/kpatch.lds
|
|
|
576586 |
index 105e5e6..49ae8e7 100644
|
|
|
576586 |
--- a/kmod/patch/kpatch.lds
|
|
|
576586 |
+++ b/kmod/patch/kpatch.lds
|
|
|
576586 |
@@ -2,6 +2,7 @@ __kpatch_funcs = ADDR(.kpatch.funcs);
|
|
|
576586 |
__kpatch_funcs_end = ADDR(.kpatch.funcs) + SIZEOF(.kpatch.funcs);
|
|
|
576586 |
__kpatch_dynrelas = ADDR(.kpatch.dynrelas);
|
|
|
576586 |
__kpatch_dynrelas_end = ADDR(.kpatch.dynrelas) + SIZEOF(.kpatch.dynrelas);
|
|
|
576586 |
+__kpatch_checksum = ADDR(.kpatch.checksum);
|
|
|
576586 |
SECTIONS
|
|
|
576586 |
{
|
|
|
576586 |
.kpatch.hooks.load : {
|
|
|
576586 |
diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build
|
|
|
576586 |
index 91bf5c2..38466f4 100755
|
|
|
576586 |
--- a/kpatch-build/kpatch-build
|
|
|
576586 |
+++ b/kpatch-build/kpatch-build
|
|
|
576586 |
@@ -343,7 +343,7 @@ else
|
|
|
576586 |
url="http://us.archive.ubuntu.com/ubuntu/pool/main/l/linux"
|
|
|
576586 |
extension="bz2"
|
|
|
576586 |
sublevel="SUBLEVEL = 0"
|
|
|
576586 |
- taroptions="xvjf"
|
|
|
576586 |
+ troptions="xvjf"
|
|
|
576586 |
|
|
|
576586 |
elif [[ $DISTRO = debian ]]; then
|
|
|
576586 |
|
|
|
576586 |
@@ -478,6 +478,9 @@ cd "$SRCDIR"
|
|
|
576586 |
make prepare >> "$LOGFILE" 2>&1 || die
|
|
|
576586 |
cd "$TEMPDIR/output"
|
|
|
576586 |
ld -r -o ../patch/output.o $FILES >> "$LOGFILE" 2>&1 || die
|
|
|
576586 |
+md5sum ../patch/output.o | awk '{printf "%s\0", $1}' > checksum.tmp || die
|
|
|
576586 |
+objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/output.o || die
|
|
|
576586 |
+rm -f checksum.tmp
|
|
|
576586 |
cd "$TEMPDIR/patch"
|
|
|
576586 |
KPATCH_BUILD="$SRCDIR" KPATCH_NAME="$PATCHNAME" KBUILD_EXTRA_SYMBOLS="$SYMVERSFILE" make "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die
|
|
|
576586 |
|
|
|
576586 |
diff --git a/kpatch/kpatch b/kpatch/kpatch
|
|
|
576586 |
index 0b932a6..0e36dda 100755
|
|
|
576586 |
--- a/kpatch/kpatch
|
|
|
576586 |
+++ b/kpatch/kpatch
|
|
|
576586 |
@@ -121,6 +121,21 @@ core_module_loaded () {
|
|
|
576586 |
grep -q "T kpatch_register" /proc/kallsyms
|
|
|
576586 |
}
|
|
|
576586 |
|
|
|
576586 |
+get_module_name () {
|
|
|
576586 |
+ echo $(readelf -p .gnu.linkonce.this_module $1 | grep '\[.*\]' | awk '{print $3}')
|
|
|
576586 |
+}
|
|
|
576586 |
+
|
|
|
576586 |
+verify_module_checksum () {
|
|
|
576586 |
+ modname=$(get_module_name $1)
|
|
|
576586 |
+ [[ -z $modname ]] && return 1
|
|
|
576586 |
+
|
|
|
576586 |
+ checksum=$(readelf -p .kpatch.checksum $1 | grep '\[.*\]' | awk '{print $3}')
|
|
|
576586 |
+ [[ -z $checksum ]] && return 1
|
|
|
576586 |
+
|
|
|
576586 |
+ sysfs_checksum=$(cat /sys/kernel/kpatch/patches/${modname}/checksum)
|
|
|
576586 |
+ [[ $checksum == $sysfs_checksum ]] || return 1
|
|
|
576586 |
+}
|
|
|
576586 |
+
|
|
|
576586 |
load_module () {
|
|
|
576586 |
if ! core_module_loaded; then
|
|
|
576586 |
if modprobe -q kpatch; then
|
|
|
576586 |
@@ -131,6 +146,23 @@ load_module () {
|
|
|
576586 |
insmod "$COREMOD" || die "failed to load core module"
|
|
|
576586 |
fi
|
|
|
576586 |
fi
|
|
|
576586 |
+
|
|
|
576586 |
+ modname=$(get_module_name $1)
|
|
|
576586 |
+ moddir=/sys/kernel/kpatch/patches/$modname
|
|
|
576586 |
+ if [[ -d $moddir ]] ; then
|
|
|
576586 |
+ if [[ $(cat "${moddir}/enabled") -eq 0 ]]; then
|
|
|
576586 |
+ if verify_module_checksum $1; then # same checksum
|
|
|
576586 |
+ echo "module already loaded, re-enabling"
|
|
|
576586 |
+ echo 1 > ${moddir}/enabled || die "failed to re-enable module $modname"
|
|
|
576586 |
+ return
|
|
|
576586 |
+ else
|
|
|
576586 |
+ die "error: cannot re-enable patch module $modname, cannot verify checksum match"
|
|
|
576586 |
+ fi
|
|
|
576586 |
+ else
|
|
|
576586 |
+ die "error: module named $modname already loaded and enabled"
|
|
|
576586 |
+ fi
|
|
|
576586 |
+ fi
|
|
|
576586 |
+
|
|
|
576586 |
echo "loading patch module: $1"
|
|
|
576586 |
insmod "$1" "$2"
|
|
|
576586 |
}
|
|
|
576586 |
--
|
|
|
576586 |
1.9.3
|
|
|
576586 |
|